From fc59144b1d6beb4719104128325a79387aa6cf58 Mon Sep 17 00:00:00 2001 From: wnzzer <1503123330@qq.com> Date: Sun, 23 Feb 2025 14:26:31 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E6=B8=85=E7=A9=BA=E8=AF=9D=E9=A2=98?= =?UTF-8?q?=E6=80=BB=E6=98=AF=E4=BF=AE=E5=A4=8D=E5=BD=93=E5=89=8D=E8=AF=9D?= =?UTF-8?q?=E9=A2=98=20(#2167)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../home/Tabs/AssistantItemComponent.tsx | 197 +++++++++++++ .../src/pages/home/Tabs/AssistantsTab.tsx | 261 +++++------------- 2 files changed, 267 insertions(+), 191 deletions(-) create mode 100644 src/renderer/src/pages/home/Tabs/AssistantItemComponent.tsx diff --git a/src/renderer/src/pages/home/Tabs/AssistantItemComponent.tsx b/src/renderer/src/pages/home/Tabs/AssistantItemComponent.tsx new file mode 100644 index 00000000..d955089c --- /dev/null +++ b/src/renderer/src/pages/home/Tabs/AssistantItemComponent.tsx @@ -0,0 +1,197 @@ +import CopyIcon from "@renderer/components/Icons/CopyIcon" +import { useAssistant } from "@renderer/hooks/useAssistant" +import { modelGenerating } from "@renderer/hooks/useRuntime" +import { useSettings } from "@renderer/hooks/useSettings" +import AssistantSettingsPopup from "@renderer/pages/settings/AssistantSettings" +import { getDefaultTopic } from "@renderer/services/AssistantService" + +import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' +import { Assistant } from "@renderer/types" +import { uuid } from "@renderer/utils" +import { Dropdown } from "antd" +import { ItemType } from "antd/es/menu/interface" +import { omit } from "lodash" +import { FC, useCallback } from "react" +import { useTranslation } from "react-i18next" +import { DeleteOutlined, EditOutlined, MinusCircleOutlined, SaveOutlined } from '@ant-design/icons' + +import styled from "styled-components" + +interface AssistantItemProps { + assistant: Assistant + isActive: boolean + onSwitch: (assistant: Assistant) => void + onDelete: (assistant: Assistant) => void + onCreateDefaultAssistant: () => void + addAgent: (agent: any) => void + addAssistant: (assistant: Assistant) => void + } + + const AssistantItemComponent: FC = ({ + assistant, + isActive, + onSwitch, + onDelete, + addAgent, + addAssistant + }) => { + const { t } = useTranslation() + const { removeAllTopics } = useAssistant(assistant.id) // 使用当前助手的ID + const { clickAssistantToShowTopic, topicPosition } = useSettings() + + const getMenuItems = useCallback( + (assistant: Assistant): ItemType[] => [ + { + label: t('assistants.edit.title'), + key: 'edit', + icon: , + onClick: () => AssistantSettingsPopup.show({ assistant }) + }, + { + label: t('assistants.copy.title'), + key: 'duplicate', + icon: , + onClick: async () => { + const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] } + addAssistant(_assistant) + onSwitch(_assistant) + } + }, + { + label: t('assistants.clear.title'), + key: 'clear', + icon: , + onClick: () => { + window.modal.confirm({ + title: t('assistants.clear.title'), + content: t('assistants.clear.content'), + centered: true, + okButtonProps: { danger: true }, + onOk: () => removeAllTopics() // 使用当前助手的removeAllTopics + }) + } + }, + { + label: t('assistants.save.title'), + key: 'save-to-agent', + icon: , + onClick: async () => { + const agent = omit(assistant, ['model', 'emoji']) + agent.id = uuid() + agent.type = 'agent' + addAgent(agent) + window.message.success({ + content: t('assistants.save.success'), + key: 'save-to-agent' + }) + } + }, + { type: 'divider' }, + { + label: t('common.delete'), + key: 'delete', + icon: , + danger: true, + onClick: () => { + window.modal.confirm({ + title: t('assistants.delete.title'), + content: t('assistants.delete.content'), + centered: true, + okButtonProps: { danger: true }, + onOk: () => onDelete(assistant) + }) + } + } + ], + [addAgent, addAssistant, onSwitch, removeAllTopics, t, onDelete] + ) + + const handleSwitch = useCallback(async () => { + await modelGenerating() + + if (topicPosition === 'left' && clickAssistantToShowTopic) { + EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR) + } + + onSwitch(assistant) + }, [clickAssistantToShowTopic, onSwitch, assistant, topicPosition]) + + return ( + + + {assistant.name || t('chat.default.name')} + {isActive && ( + EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}> + {assistant.topics.length} + + )} + + + ) + } + export default AssistantItemComponent // 使用默认导出 + + + + +const AssistantItem = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + padding: 7px 12px; + position: relative; + margin: 0 10px; + padding-right: 35px; + font-family: Ubuntu; + border-radius: var(--list-item-border-radius); + border: 0.5px solid transparent; + cursor: pointer; + .iconfont { + opacity: 0; + color: var(--color-text-3); + } + &:hover { + background-color: var(--color-background-soft); + } + &.active { + background-color: var(--color-background-soft); + border: 0.5px solid var(--color-border); + .name { + } + } +` + +const AssistantName = styled.div` + color: var(--color-text); + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + font-size: 13px; +` + +const MenuButton = styled.div` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + min-width: 22px; + height: 22px; + min-width: 22px; + min-height: 22px; + border-radius: 11px; + position: absolute; + background-color: var(--color-background); + right: 9px; + top: 6px; +` + +const TopicCount = styled.div` + color: var(--color-text); + font-size: 10px; + border-radius: 10px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +` diff --git a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx index efb8c13b..e655c1f0 100644 --- a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx @@ -1,170 +1,23 @@ -import { DeleteOutlined, EditOutlined, MinusCircleOutlined, PlusOutlined, SaveOutlined } from '@ant-design/icons' -import DragableList from '@renderer/components/DragableList' -import CopyIcon from '@renderer/components/Icons/CopyIcon' -import Scrollbar from '@renderer/components/Scrollbar' -import { useAgents } from '@renderer/hooks/useAgents' -import { useAssistant, useAssistants } from '@renderer/hooks/useAssistant' -import { modelGenerating } from '@renderer/hooks/useRuntime' -import { useSettings } from '@renderer/hooks/useSettings' -import AssistantSettingsPopup from '@renderer/pages/settings/AssistantSettings' -import { getDefaultTopic } from '@renderer/services/AssistantService' -import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' -import { Assistant } from '@renderer/types' -import { uuid } from '@renderer/utils' -import { Dropdown } from 'antd' -import { ItemType } from 'antd/es/menu/interface' -import { last, omit } from 'lodash' -import { FC, useCallback, useState } from 'react' -import { useTranslation } from 'react-i18next' -import styled from 'styled-components' +import { useCallback, useState, FC } from "react" +import { useTranslation } from "react-i18next" +import { PlusOutlined } from '@ant-design/icons' +import DragableList from "@renderer/components/DragableList" +import Scrollbar from "@renderer/components/Scrollbar" +import { useAgents } from "@renderer/hooks/useAgents" +import { useAssistants } from "@renderer/hooks/useAssistant" +import { Assistant } from "@renderer/types" +import styled from "styled-components" +import AssistantItemComponent from "@renderer/pages/home/Tabs/AssistantItemComponent" -interface Props { +// 类型定义 +interface AssistantsProps { activeAssistant: Assistant setActiveAssistant: (assistant: Assistant) => void - onCreateDefaultAssistant: () => void onCreateAssistant: () => void + onCreateDefaultAssistant: () => void } -const Assistants: FC = ({ - activeAssistant, - setActiveAssistant, - onCreateAssistant, - onCreateDefaultAssistant -}) => { - const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() - const [dragging, setDragging] = useState(false) - const { removeAllTopics } = useAssistant(activeAssistant.id) - const { clickAssistantToShowTopic, topicPosition } = useSettings() - const { t } = useTranslation() - const { addAgent } = useAgents() - - const onDelete = useCallback( - (assistant: Assistant) => { - const _assistant: Assistant | undefined = last(assistants.filter((a) => a.id !== assistant.id)) - _assistant ? setActiveAssistant(_assistant) : onCreateDefaultAssistant() - removeAssistant(assistant.id) - }, - [assistants, onCreateDefaultAssistant, removeAssistant, setActiveAssistant] - ) - - const getMenuItems = useCallback( - (assistant: Assistant) => - [ - { - label: t('assistants.edit.title'), - key: 'edit', - icon: , - onClick: () => AssistantSettingsPopup.show({ assistant }) - }, - { - label: t('assistants.copy.title'), - key: 'duplicate', - icon: , - onClick: async () => { - const _assistant: Assistant = { ...assistant, id: uuid(), topics: [getDefaultTopic(assistant.id)] } - addAssistant(_assistant) - setActiveAssistant(_assistant) - } - }, - { - label: t('assistants.clear.title'), - key: 'clear', - icon: , - onClick: () => { - window.modal.confirm({ - title: t('assistants.clear.title'), - content: t('assistants.clear.content'), - centered: true, - okButtonProps: { danger: true }, - onOk: removeAllTopics - }) - } - }, - { - label: t('assistants.save.title'), - key: 'save-to-agent', - icon: , - onClick: async () => { - const agent = omit(assistant, ['model', 'emoji']) - agent.id = uuid() - agent.type = 'agent' - addAgent(agent) - window.message.success({ - content: t('assistants.save.success'), - key: 'save-to-agent' - }) - } - }, - { type: 'divider' }, - { - label: t('common.delete'), - key: 'delete', - icon: , - danger: true, - onClick: () => { - window.modal.confirm({ - title: t('assistants.delete.title'), - content: t('assistants.delete.content'), - centered: true, - okButtonProps: { danger: true }, - onOk: () => onDelete(assistant) - }) - } - } - ] as ItemType[], - [addAgent, addAssistant, onDelete, removeAllTopics, setActiveAssistant, t] - ) - - const onSwitchAssistant = useCallback( - async (assistant: Assistant) => { - await modelGenerating() - - if (topicPosition === 'left' && clickAssistantToShowTopic) { - EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR) - } - - setActiveAssistant(assistant) - }, - [clickAssistantToShowTopic, setActiveAssistant, topicPosition] - ) - - return ( - - setDragging(true)} - onDragEnd={() => setDragging(false)}> - {(assistant) => { - const isCurrent = assistant.id === activeAssistant?.id - return ( - - onSwitchAssistant(assistant)} className={isCurrent ? 'active' : ''}> - {assistant.name || t('chat.default.name')} - {isCurrent && ( - EventEmitter.emit(EVENT_NAMES.SWITCH_TOPIC_SIDEBAR)}> - {assistant.topics.length} - - )} - - - ) - }} - - {!dragging && ( - - - - {t('chat.add.assistant.title')} - - - )} -
-
- ) -} - +// 样式组件(只定义一次) const Container = styled(Scrollbar)` display: flex; flex-direction: column; @@ -184,18 +37,14 @@ const AssistantItem = styled.div` border-radius: var(--list-item-border-radius); border: 0.5px solid transparent; cursor: pointer; - .iconfont { - opacity: 0; - color: var(--color-text-3); - } + &:hover { background-color: var(--color-background-soft); } + &.active { background-color: var(--color-background-soft); border: 0.5px solid var(--color-border); - .name { - } } ` @@ -208,30 +57,60 @@ const AssistantName = styled.div` font-size: 13px; ` -const MenuButton = styled.div` - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - min-width: 22px; - height: 22px; - min-width: 22px; - min-height: 22px; - border-radius: 11px; - position: absolute; - background-color: var(--color-background); - right: 9px; - top: 6px; -` +const Assistants: FC = ({ + activeAssistant, + setActiveAssistant, + onCreateAssistant, + onCreateDefaultAssistant +}) => { + const { assistants, removeAssistant, addAssistant, updateAssistants } = useAssistants() + const [dragging, setDragging] = useState(false) + const { addAgent } = useAgents() + const { t } = useTranslation() -const TopicCount = styled.div` - color: var(--color-text); - font-size: 10px; - border-radius: 10px; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; -` + const onDelete = useCallback( + (assistant: Assistant) => { + const remaining = assistants.filter(a => a.id !== assistant.id) + const newActive = remaining[remaining.length - 1] + newActive ? setActiveAssistant(newActive) : onCreateDefaultAssistant() + removeAssistant(assistant.id) + }, + [assistants, removeAssistant, setActiveAssistant, onCreateDefaultAssistant] + ) + + return ( + + setDragging(true)} + onDragEnd={() => setDragging(false)} + > + {(assistant) => ( + + )} + + {!dragging && ( + + + + {t('chat.add.assistant.title')} + + + )} +
+
+ ) +} export default Assistants