perf: 优化智能体页面性能和体验 (#756)
* feat: improved model validation and error handling * refactor: 优化智能体页面下拉流畅度和分类切换效果,让其更加顺畅自然 --------- Co-authored-by: kangfenmao <kangfenmao@qq.com> Co-authored-by: magicdmer <magicdmer@163.com>
This commit is contained in:
parent
2300cca070
commit
999bd802c4
@ -120,12 +120,31 @@ const AgentsPage: FC = () => {
|
|||||||
[i18n.language]
|
[i18n.language]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const renderAgentList = useCallback(
|
||||||
|
(agents: Agent[]) => {
|
||||||
|
return (
|
||||||
|
<Row gutter={[20, 20]}>
|
||||||
|
{agents.map((agent, index) => (
|
||||||
|
<Col span={6} key={agent.id || index}>
|
||||||
|
<AgentCard
|
||||||
|
onClick={() => onAddAgentConfirm(getAgentFromSystemAgent(agent as any))}
|
||||||
|
agent={agent as any}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
))}
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
[onAddAgentConfirm]
|
||||||
|
);
|
||||||
|
|
||||||
const tabItems = useMemo(() => {
|
const tabItems = useMemo(() => {
|
||||||
const groups = Object.keys(filteredAgentGroups)
|
const groups = Object.keys(filteredAgentGroups);
|
||||||
|
|
||||||
return groups.map((group, i) => {
|
return groups.map((group, i) => {
|
||||||
const id = String(i + 1)
|
const id = String(i + 1);
|
||||||
const localizedGroupName = getLocalizedGroupName(group)
|
const localizedGroupName = getLocalizedGroupName(group);
|
||||||
|
const agents = filteredAgentGroups[group] || [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: localizedGroupName,
|
label: localizedGroupName,
|
||||||
@ -135,25 +154,16 @@ const AgentsPage: FC = () => {
|
|||||||
<Title level={5} key={group} style={{ marginBottom: 10 }}>
|
<Title level={5} key={group} style={{ marginBottom: 10 }}>
|
||||||
{localizedGroupName}
|
{localizedGroupName}
|
||||||
</Title>
|
</Title>
|
||||||
<Row gutter={[20, 20]}>
|
{group === '我的' ? (
|
||||||
{group === '我的' ? (
|
<MyAgents onClick={onAddAgentConfirm} search={search} />
|
||||||
<MyAgents onClick={onAddAgentConfirm} search={search} />
|
) : (
|
||||||
) : (
|
renderAgentList(agents)
|
||||||
filteredAgentGroups[group]?.map((agent, index) => (
|
)}
|
||||||
<Col span={6} key={group + index}>
|
|
||||||
<AgentCard
|
|
||||||
onClick={() => onAddAgentConfirm(getAgentFromSystemAgent(agent as any))}
|
|
||||||
agent={agent as any}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
</TabContent>
|
</TabContent>
|
||||||
)
|
)
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
}, [filteredAgentGroups, getLocalizedGroupName, onAddAgentConfirm, search])
|
}, [filteredAgentGroups, getLocalizedGroupName, onAddAgentConfirm, search, renderAgentList]);
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
if (searchInput.trim() === '') {
|
if (searchInput.trim() === '') {
|
||||||
@ -185,26 +195,20 @@ const AgentsPage: FC = () => {
|
|||||||
<div style={{ width: 80 }} />
|
<div style={{ width: 80 }} />
|
||||||
</NavbarCenter>
|
</NavbarCenter>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<ContentContainer id="content-container">
|
<ContentContainer>
|
||||||
<AssistantsContainer>
|
<AssistantsContainer>
|
||||||
{Object.values(filteredAgentGroups).flat().length > 0 ? (
|
{Object.values(filteredAgentGroups).flat().length > 0 ? (
|
||||||
search.trim() ? (
|
search.trim() ? (
|
||||||
<TabContent>
|
<TabContent>
|
||||||
<Row gutter={[20, 20]}>
|
{renderAgentList(Object.values(filteredAgentGroups).flat())}
|
||||||
{Object.values(filteredAgentGroups)
|
|
||||||
.flat()
|
|
||||||
.map((agent, index, array) => (
|
|
||||||
<Col span={array.length === 1 ? 12 : 6} key={index}>
|
|
||||||
<AgentCard
|
|
||||||
onClick={() => onAddAgentConfirm(getAgentFromSystemAgent(agent as any))}
|
|
||||||
agent={agent as any}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
</TabContent>
|
</TabContent>
|
||||||
) : (
|
) : (
|
||||||
<Tabs tabPosition="right" animated items={tabItems} $language={i18n.language} />
|
<Tabs
|
||||||
|
tabPosition="right"
|
||||||
|
animated={false}
|
||||||
|
items={tabItems}
|
||||||
|
$language={i18n.language}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<EmptyView>
|
<EmptyView>
|
||||||
@ -232,6 +236,7 @@ const ContentContainer = styled.div`
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
border-top: 0.5px solid var(--color-border);
|
||||||
`
|
`
|
||||||
|
|
||||||
const AssistantsContainer = styled.div`
|
const AssistantsContainer = styled.div`
|
||||||
@ -239,6 +244,7 @@ const AssistantsContainer = styled.div`
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
height: calc(100vh - var(--navbar-height));
|
height: calc(100vh - var(--navbar-height));
|
||||||
|
border-left: 0.5px solid var(--color-border);
|
||||||
`
|
`
|
||||||
|
|
||||||
const TabContent = styled(Scrollbar)`
|
const TabContent = styled(Scrollbar)`
|
||||||
@ -247,6 +253,9 @@ const TabContent = styled(Scrollbar)`
|
|||||||
margin-right: -4px;
|
margin-right: -4px;
|
||||||
padding-bottom: 20px !important;
|
padding-bottom: 20px !important;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
transform: translateZ(0);
|
||||||
|
will-change: transform;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
`
|
`
|
||||||
|
|
||||||
const AgentPrompt = styled.div`
|
const AgentPrompt = styled.div`
|
||||||
@ -268,12 +277,16 @@ const Tabs = styled(TabsAntd)<{ $language: string }>`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
|
border-right: 0.5px solid var(--color-border);
|
||||||
|
|
||||||
.ant-tabs-tabpane {
|
.ant-tabs-tabpane {
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
.ant-tabs-nav {
|
.ant-tabs-nav {
|
||||||
min-width: ${({ $language }) => ($language.startsWith('zh') ? '120px' : '140px')};
|
min-width: ${({ $language }) => ($language.startsWith('zh') ? '120px' : '140px')};
|
||||||
max-width: ${({ $language }) => ($language.startsWith('zh') ? '120px' : '140px')};
|
max-width: ${({ $language }) => ($language.startsWith('zh') ? '120px' : '140px')};
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
.ant-tabs-nav-list {
|
.ant-tabs-nav-list {
|
||||||
padding: 10px 8px;
|
padding: 10px 8px;
|
||||||
@ -291,11 +304,14 @@ const Tabs = styled(TabsAntd)<{ $language: string }>`
|
|||||||
border: 0.5px solid transparent;
|
border: 0.5px solid transparent;
|
||||||
justify-content: ${({ $language }) => ($language.startsWith('zh') ? 'center' : 'flex-start')};
|
justify-content: ${({ $language }) => ($language.startsWith('zh') ? 'center' : 'flex-start')};
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
|
|
||||||
.ant-tabs-tab-btn {
|
.ant-tabs-tab-btn {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--color-text) !important;
|
color: var(--color-text) !important;
|
||||||
@ -304,12 +320,11 @@ const Tabs = styled(TabsAntd)<{ $language: string }>`
|
|||||||
}
|
}
|
||||||
.ant-tabs-tab-active {
|
.ant-tabs-tab-active {
|
||||||
background-color: var(--color-background-soft);
|
background-color: var(--color-background-soft);
|
||||||
border-right: none;
|
|
||||||
border: 0.5px solid var(--color-border);
|
border: 0.5px solid var(--color-border);
|
||||||
|
transform: scale(1.02);
|
||||||
}
|
}
|
||||||
.ant-tabs-content-holder {
|
.ant-tabs-content-holder {
|
||||||
border-left: 0.5px solid var(--color-border);
|
border-left: 0.5px solid var(--color-border);
|
||||||
border-right: none;
|
|
||||||
}
|
}
|
||||||
.ant-tabs-ink-bar {
|
.ant-tabs-ink-bar {
|
||||||
display: none;
|
display: none;
|
||||||
@ -322,6 +337,9 @@ const Tabs = styled(TabsAntd)<{ $language: string }>`
|
|||||||
color: var(--color-text) !important;
|
color: var(--color-text) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ant-tabs-content {
|
||||||
|
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export default AgentsPage
|
export default AgentsPage
|
||||||
|
|||||||
@ -3,10 +3,11 @@ import { Agent } from '@renderer/types'
|
|||||||
import { getLeadingEmoji } from '@renderer/utils'
|
import { getLeadingEmoji } from '@renderer/utils'
|
||||||
import { Dropdown } from 'antd'
|
import { Dropdown } from 'antd'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
import { FC, memo } from 'react'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
agent: Agent
|
agent: Agent
|
||||||
onClick?: () => void
|
onClick: () => void
|
||||||
contextMenu?: { label: string; onClick: () => void }[]
|
contextMenu?: { label: string; onClick: () => void }[]
|
||||||
menuItems?: {
|
menuItems?: {
|
||||||
key: string
|
key: string
|
||||||
@ -17,7 +18,7 @@ interface Props {
|
|||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const AgentCard: React.FC<Props> = ({ agent, onClick, contextMenu, menuItems }) => {
|
const AgentCard: FC<Props> = ({ agent, onClick, contextMenu, menuItems }) => {
|
||||||
const emoji = agent.emoji || getLeadingEmoji(agent.name)
|
const emoji = agent.emoji || getLeadingEmoji(agent.name)
|
||||||
const prompt = (agent.description || agent.prompt).substring(0, 100).replace(/\\n/g, '')
|
const prompt = (agent.description || agent.prompt).substring(0, 100).replace(/\\n/g, '')
|
||||||
const content = (
|
const content = (
|
||||||
@ -205,4 +206,4 @@ const MenuContainer = styled.div`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export default AgentCard
|
export default memo(AgentCard)
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useAgents } from '@renderer/hooks/useAgents'
|
|||||||
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
|
import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings'
|
||||||
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
|
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
|
||||||
import { Agent } from '@renderer/types'
|
import { Agent } from '@renderer/types'
|
||||||
import { Col } from 'antd'
|
import { Col, Row } from 'antd'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ const MyAgents: React.FC<Props> = ({ onClick, search }) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Row gutter={[20, 20]}>
|
||||||
{filteredAgents.map((agent) => {
|
{filteredAgents.map((agent) => {
|
||||||
const dropdownMenuItems = [
|
const dropdownMenuItems = [
|
||||||
{
|
{
|
||||||
@ -102,7 +102,7 @@ const MyAgents: React.FC<Props> = ({ onClick, search }) => {
|
|||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
<AddAgentCard onClick={() => AddAgentPopup.show()} />
|
<AddAgentCard onClick={() => AddAgentPopup.show()} />
|
||||||
</Col>
|
</Col>
|
||||||
</>
|
</Row>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user