From c7b340087ab887f16c3f9e21dd2321f852504e82 Mon Sep 17 00:00:00 2001 From: allen2fuc Date: Wed, 29 Apr 2026 15:27:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E4=BC=9A=E8=AF=9D=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=E5=92=8C=E9=BB=98=E8=AE=A4=E6=9F=A5=E7=9C=8B=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/lib/aiApi.ts | 8 ++- client/src/locales/data-en.json | 6 +- client/src/locales/data-zh-HK.json | 6 +- client/src/locales/data-zh.json | 6 +- client/src/pages/AIPage.tsx | 94 +++++++++++++++++++++++++++--- 5 files changed, 106 insertions(+), 14 deletions(-) diff --git a/client/src/lib/aiApi.ts b/client/src/lib/aiApi.ts index 1b3e29e..67b282d 100644 --- a/client/src/lib/aiApi.ts +++ b/client/src/lib/aiApi.ts @@ -131,9 +131,11 @@ export async function listMessages(chatId: string): Promise { } export async function clearChat(chatId: string): Promise { - await fetch(`${getAIBaseUrl()}/sse/clear?chat_id=${encodeURIComponent(chatId)}`, { - method: "POST", - }); + const res = await fetch( + `${getAIBaseUrl()}/sse/clear?chat_id=${encodeURIComponent(chatId)}`, + { method: "POST" }, + ); + if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`); } export async function updateMessageApplied(messageId: string, applied: boolean): Promise { diff --git a/client/src/locales/data-en.json b/client/src/locales/data-en.json index 68882fd..400e2d2 100644 --- a/client/src/locales/data-en.json +++ b/client/src/locales/data-en.json @@ -518,7 +518,11 @@ "title": "Chat history", "newChat": "Start a new chat", "empty": "No chat history yet", - "current": "Current" + "current": "Current", + "delete": "Delete", + "deleteConfirm": "Delete this chat? This cannot be undone.", + "deleted": "Chat deleted", + "deleteFailed": "Delete failed: {message}" }, "optimize": { "badge": "Optimization", diff --git a/client/src/locales/data-zh-HK.json b/client/src/locales/data-zh-HK.json index b6384e3..ecef9d2 100644 --- a/client/src/locales/data-zh-HK.json +++ b/client/src/locales/data-zh-HK.json @@ -332,7 +332,11 @@ "title": "歷史會話", "newChat": "開始新會話", "empty": "暫無歷史會話", - "current": "目前" + "current": "目前", + "delete": "刪除", + "deleteConfirm": "確定刪除這條對話嗎?刪除後無法復原。", + "deleted": "對話已刪除", + "deleteFailed": "刪除失敗:{message}" }, "optimize": { "badge": "優化記錄", diff --git a/client/src/locales/data-zh.json b/client/src/locales/data-zh.json index 3b0794c..3774cb6 100644 --- a/client/src/locales/data-zh.json +++ b/client/src/locales/data-zh.json @@ -333,7 +333,11 @@ "title": "历史会话", "newChat": "开始新会话", "empty": "暂无历史会话", - "current": "当前" + "current": "当前", + "delete": "删除", + "deleteConfirm": "确定删除这条会话吗?删除后无法恢复。", + "deleted": "会话已删除", + "deleteFailed": "删除失败:{message}" }, "optimize": { "badge": "优化记录", diff --git a/client/src/pages/AIPage.tsx b/client/src/pages/AIPage.tsx index 3d60fdc..647f08e 100644 --- a/client/src/pages/AIPage.tsx +++ b/client/src/pages/AIPage.tsx @@ -42,6 +42,7 @@ import { Check, RotateCcw, Loader2, + Trash2, } from "lucide-react"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Streamdown } from "streamdown"; @@ -147,10 +148,13 @@ export default function AIPage() { const [sending, setSending] = useState(false); const [historyOpen, setHistoryOpen] = useState(false); const [chats, setChats] = useState([]); + // 初次加载历史会话期间,避免先闪一下欢迎页再切到消息 + const [initializing, setInitializing] = useState(true); const scrollRef = useRef(null); const abortRef = useRef(null); const textareaRef = useRef(null); + const initialLoadRef = useRef(false); const isEmpty = messages.length === 0; @@ -227,6 +231,37 @@ export default function AIPage() { [aiText, mapRowsToUI], ); + // ── 进入页面时若有历史会话,自动打开最近一次 ── + useEffect(() => { + if (initialLoadRef.current) return; + if (!mac) return; + initialLoadRef.current = true; + (async () => { + try { + const list = await listChats(mac); + setChats(list); + if (list.length === 0) return; + const newest = list.reduce((a, b) => + new Date(a.updated_at) > new Date(b.updated_at) ? a : b, + ); + await loadChat(newest.id, { closePanel: false }); + } catch { + // 静默失败:保留欢迎页 + } finally { + setInitializing(false); + } + })(); + }, [mac, loadChat]); + + // 没有 mac 时(设备未识别完毕)不应该停在 initializing;ConnectScreen 会拦截 + useEffect(() => { + if (!mac && initializing) { + // 给 mac 一段缓冲期,否则即便最终没有 mac 也要释放欢迎页 + const t = setTimeout(() => setInitializing(false), 800); + return () => clearTimeout(t); + } + }, [mac, initializing]); + // ── 静默刷新当前会话(流式结束后用于拉取新增的 type=2 优化记录) ── const refreshCurrentChat = useCallback( async (id: string) => { @@ -257,6 +292,7 @@ export default function AIPage() { } try { await clearChat(chatId); + setChats((prev) => prev.filter((c) => c.id !== chatId)); newChat(); toast.success(aiText.cleared); } catch (e: any) { @@ -264,6 +300,28 @@ export default function AIPage() { } }, [aiText, chatId, newChat]); + // ── 历史面板里删除某条会话 ── + const deleteChatById = useCallback( + async (id: string) => { + const ok = window.confirm(aiText.history.deleteConfirm); + if (!ok) return; + try { + await clearChat(id); + setChats((prev) => prev.filter((c) => c.id !== id)); + if (chatId === id) { + setChatId(null); + setMessages([]); + } + toast.success(aiText.history.deleted); + } catch (e: any) { + toast.error( + formatText(aiText.history.deleteFailed, { message: e?.message ?? e }), + ); + } + }, + [aiText, chatId], + ); + // ── 发送提问 ── const send = useCallback( (question: string) => { @@ -590,7 +648,11 @@ export default function AIPage() { {/* ── Messages / Welcome ── */}
- {isEmpty ? ( + {initializing ? ( +
+ +
+ ) : isEmpty ? ( setHistoryOpen(false)} onSelect={(id) => loadChat(id)} onNew={newChat} + onDelete={deleteChatById} /> )} @@ -895,6 +958,7 @@ function HistoryPanel({ onClose, onSelect, onNew, + onDelete, }: { aiText: AILocale; chats: ChatRead[]; @@ -902,9 +966,10 @@ function HistoryPanel({ onClose: () => void; onSelect: (id: string) => void; onNew: () => void; + onDelete: (id: string) => void; }) { return ( -
+
{chats.map((c) => ( -
+ {currentId === c.id && ( {aiText.history.current} )} - + +
))}
)}