修改了历史会话显示的问题和默认查看历史记录信息

This commit is contained in:
allen2fuc
2026-04-29 15:27:45 +08:00
parent 7936fea313
commit c7b340087a
5 changed files with 106 additions and 14 deletions
+5 -3
View File
@@ -131,9 +131,11 @@ export async function listMessages(chatId: string): Promise<MessageRead[]> {
}
export async function clearChat(chatId: string): Promise<void> {
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<void> {
+5 -1
View File
@@ -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",
+5 -1
View File
@@ -332,7 +332,11 @@
"title": "歷史會話",
"newChat": "開始新會話",
"empty": "暫無歷史會話",
"current": "目前"
"current": "目前",
"delete": "刪除",
"deleteConfirm": "確定刪除這條對話嗎?刪除後無法復原。",
"deleted": "對話已刪除",
"deleteFailed": "刪除失敗:{message}"
},
"optimize": {
"badge": "優化記錄",
+5 -1
View File
@@ -333,7 +333,11 @@
"title": "历史会话",
"newChat": "开始新会话",
"empty": "暂无历史会话",
"current": "当前"
"current": "当前",
"delete": "删除",
"deleteConfirm": "确定删除这条会话吗?删除后无法恢复。",
"deleted": "会话已删除",
"deleteFailed": "删除失败:{message}"
},
"optimize": {
"badge": "优化记录",
+86 -8
View File
@@ -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<ChatRead[]>([]);
// 初次加载历史会话期间,避免先闪一下欢迎页再切到消息
const [initializing, setInitializing] = useState(true);
const scrollRef = useRef<HTMLDivElement | null>(null);
const abortRef = useRef<AbortController | null>(null);
const textareaRef = useRef<HTMLTextAreaElement | null>(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 时(设备未识别完毕)不应该停在 initializingConnectScreen 会拦截
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 ── */}
<div ref={scrollRef} className="flex-1 min-h-0 overflow-y-auto px-4">
{isEmpty ? (
{initializing ? (
<div className="h-full flex items-center justify-center">
<Loader2 size={20} className="animate-spin text-white/30" />
</div>
) : isEmpty ? (
<WelcomeView
aiText={aiText}
deviceName={deviceName}
@@ -712,6 +774,7 @@ export default function AIPage() {
onClose={() => 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 (
<div className="fixed inset-0 z-50 flex flex-col bg-black/70" onClick={onClose}>
<div className="fixed inset-0 z-[60] flex flex-col bg-black/70" onClick={onClose}>
<div
className="mt-auto rounded-t-[22px] max-h-[70vh] flex flex-col"
style={{
@@ -947,26 +1012,39 @@ function HistoryPanel({
) : (
<div className="ios-list-group">
{chats.map((c) => (
<button
<div
key={c.id}
onClick={() => onSelect(c.id)}
className={cn(
"ios-list-row w-full text-left active:bg-white/5",
"ios-list-row w-full",
currentId === c.id && "bg-white/[0.04]",
)}
>
<div className="flex-1 min-w-0">
<button
onClick={() => onSelect(c.id)}
className="flex-1 min-w-0 text-left active:opacity-60"
>
<div className="text-[15px] text-white truncate">{c.title}</div>
<div className="text-[11px] text-white/35 mt-0.5">
{new Date(c.updated_at).toLocaleString()}
</div>
</div>
</button>
{currentId === c.id && (
<span className="text-[11px] text-[#00FFF6] ml-2">
{aiText.history.current}
</span>
)}
</button>
<button
onClick={(e) => {
e.stopPropagation();
onDelete(c.id);
}}
className="ml-2 w-8 h-8 rounded-full flex items-center justify-center text-white/40 hover:text-red-400 active:text-red-500 transition-colors"
style={{ background: "rgba(44,44,46,0.6)" }}
aria-label={aiText.history.delete}
>
<Trash2 size={14} />
</button>
</div>
))}
</div>
)}