From 2cedc1c2341a99223734977cefea940cf39d0fa5 Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Sun, 19 Apr 2026 17:00:09 +0800 Subject: [PATCH] feat: add ConversationDeleteDialog and ConversationRenameDialog components with integration in HomePage --- .../components/ConversationDeleteDialog.tsx | 112 ++++++++++++++++++ .../components/ConversationRenameDialog.tsx | 107 +++++++++++++++++ src/pages/Home/components/index.ts | 2 + src/pages/Home/index.tsx | 106 ++++++++++++++--- 4 files changed, 308 insertions(+), 19 deletions(-) create mode 100644 src/pages/Home/components/ConversationDeleteDialog.tsx create mode 100644 src/pages/Home/components/ConversationRenameDialog.tsx diff --git a/src/pages/Home/components/ConversationDeleteDialog.tsx b/src/pages/Home/components/ConversationDeleteDialog.tsx new file mode 100644 index 0000000..f354e66 --- /dev/null +++ b/src/pages/Home/components/ConversationDeleteDialog.tsx @@ -0,0 +1,112 @@ +import * as Dialog from '@radix-ui/react-dialog'; +import { Loader2, Trash2, X } from 'lucide-react'; + +type ConversationDeleteDialogProps = { + open: boolean; + busy: boolean; + conversationTitle: string; + onClose: () => void; + onConfirm: () => void; +}; + +export default function ConversationDeleteDialog({ + open, + busy, + conversationTitle, + onClose, + onConfirm, +}: ConversationDeleteDialogProps) { + function handleOpenChange(nextOpen: boolean) { + if (!nextOpen && !busy) { + onClose(); + } + } + + return ( + + + + { + if (busy) { + event.preventDefault(); + } + }} + onPointerDownOutside={(event) => { + if (busy) { + event.preventDefault(); + } + }} + > +
+
+
+ +
+ +
+ + 删除会话 + + + 删除后将无法恢复,请确认是否继续。 + +
+
+ + +
+ +
+
+
+ 会话标题 +
+
+ {conversationTitle} +
+
+ +
+ + +
+
+
+
+
+ ); +} diff --git a/src/pages/Home/components/ConversationRenameDialog.tsx b/src/pages/Home/components/ConversationRenameDialog.tsx new file mode 100644 index 0000000..0e217c0 --- /dev/null +++ b/src/pages/Home/components/ConversationRenameDialog.tsx @@ -0,0 +1,107 @@ +import * as Dialog from '@radix-ui/react-dialog'; +import { PencilLine, X } from 'lucide-react'; + +type ConversationRenameDialogProps = { + open: boolean; + value: string; + onValueChange: (value: string) => void; + onClose: () => void; + onConfirm: () => void; +}; + +export default function ConversationRenameDialog({ + open, + value, + onValueChange, + onClose, + onConfirm, +}: ConversationRenameDialogProps) { + const trimmedValue = value.trim(); + + return ( + { + if (!nextOpen) { + onClose(); + } + }} + > + + + +
+
+
+ +
+ +
+ + 重命名对话 + + + 为当前会话设置一个更容易辨认的名称。 + +
+
+ + +
+ +
{ + event.preventDefault(); + if (!trimmedValue) return; + onConfirm(); + }} + > +
+ + onValueChange(event.target.value)} + placeholder="输入新的对话名称" + className="mt-3 h-12 w-full rounded-[16px] border border-black/10 bg-white/80 px-4 text-[14px] text-[#171717] outline-none transition-colors placeholder:text-[#99A0AE] focus:border-[#8ea8ff] dark:border-white/10 dark:bg-[#202024] dark:text-[#f3f4f6] dark:placeholder:text-gray-500" + /> +
+ 你也可以稍后再次修改。 +
+
+ +
+ + +
+
+
+
+
+ ); +} diff --git a/src/pages/Home/components/index.ts b/src/pages/Home/components/index.ts index c751199..eb3dfe1 100644 --- a/src/pages/Home/components/index.ts +++ b/src/pages/Home/components/index.ts @@ -1,2 +1,4 @@ export { default as AddChannelDialog } from './AddChannelDialog'; +export { default as ConversationDeleteDialog } from './ConversationDeleteDialog'; +export { default as ConversationRenameDialog } from './ConversationRenameDialog'; export { default as TaskOperationDialog } from './TaskOperationDialog'; diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 5afe697..5eac7e4 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -28,7 +28,12 @@ import { type StagedAttachment, } from '../../stores'; import { agentsStore, useAgentsStore } from '../../stores/agents'; -import { AddChannelDialog, TaskOperationDialog } from './components'; +import { + AddChannelDialog, + ConversationDeleteDialog, + ConversationRenameDialog, + TaskOperationDialog, +} from './components'; type SessionBucketKey = 'today' | 'yesterday' | 'withinWeek' | 'withinTwoWeeks' | 'withinMonth' | 'older'; @@ -164,24 +169,6 @@ function handleSelectConversation(conversationId: string): void { chatStore.switchSession(conversationId); } -function handleRenameConversation(conversationId: string): void { - const chat = chatStore.getState(); - const currentLabel = chat.sessionLabels[conversationId] - || chat.sessions.find((session) => session.key === conversationId)?.displayName - || ''; - const nextLabel = window.prompt('重命名对话', currentLabel); - if (nextLabel) { - chatStore.renameSession(conversationId, nextLabel); - } -} - -function handleDeleteConversation(conversationId: string): void { - const confirmed = window.confirm('确定删除该会话吗?删除后将无法恢复。'); - if (confirmed) { - void chatStore.deleteSession(conversationId); - } -} - function handleRefreshConversationData(): void { void agentsStore.load(); void chatStore.loadSessions(); @@ -251,6 +238,16 @@ export default function HomePage() { const [taskCenterNotice, setTaskCenterNotice] = useState(null); const [taskDialogOpen, setTaskDialogOpen] = useState(false); const [addChannelDialogOpen, setAddChannelDialogOpen] = useState(false); + const [renameConversationTarget, setRenameConversationTarget] = useState<{ + conversationId: string; + title: string; + } | null>(null); + const [renameConversationValue, setRenameConversationValue] = useState(''); + const [deleteConversationTarget, setDeleteConversationTarget] = useState<{ + conversationId: string; + title: string; + } | null>(null); + const [deletingConversation, setDeletingConversation] = useState(false); useEffect(() => { void agentsStore.init(); @@ -379,6 +376,52 @@ export default function HomePage() { setTaskCenterNotice(result.error || '重试失败,请稍后再试。'); } + function handleRenameConversation(conversationId: string): void { + const nextTitle = chatSessionLabels[conversationId] + || chatSessions.find((session) => session.key === conversationId)?.displayName + || ''; + + setRenameConversationTarget({ + conversationId, + title: nextTitle, + }); + setRenameConversationValue(nextTitle); + } + + function handleConfirmRenameConversation(): void { + if (!renameConversationTarget) return; + + const trimmedValue = renameConversationValue.trim(); + if (!trimmedValue) return; + + chatStore.renameSession(renameConversationTarget.conversationId, trimmedValue); + setRenameConversationTarget(null); + setRenameConversationValue(''); + } + + function handleDeleteConversation(conversationId: string): void { + const nextTitle = chatSessionLabels[conversationId] + || chatSessions.find((session) => session.key === conversationId)?.displayName + || '未命名会话'; + + setDeleteConversationTarget({ + conversationId, + title: nextTitle, + }); + } + + async function handleConfirmDeleteConversation(): Promise { + if (!deleteConversationTarget) return; + + setDeletingConversation(true); + try { + await chatStore.deleteSession(deleteConversationTarget.conversationId); + setDeleteConversationTarget(null); + } finally { + setDeletingConversation(false); + } + } + return (
@@ -535,6 +578,31 @@ export default function HomePage() { onClose={() => setAddChannelDialogOpen(false)} onConfirm={handleSaveChannels} /> + + { + setRenameConversationTarget(null); + setRenameConversationValue(''); + }} + onConfirm={handleConfirmRenameConversation} + /> + + { + if (!deletingConversation) { + setDeleteConversationTarget(null); + } + }} + onConfirm={() => { + void handleConfirmDeleteConversation(); + }} + />
); }