feat: 完善端到端对话闭环,增强消息渲染与错误处理
- ChatPanel:流式光标动画、token/延迟元数据、自动滚动、三种空状态 - Toast 组件:错误码映射为中文友好文案,3 秒自动消失 - useVisionSession:stt_result 流式更新、防重复发送、打断保存未完成内容、结束清理全部状态 - App:断线提示、视频 loading 遮罩、按钮连接中状态 - ChatMessage 增加 latencyMs、model 字段 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
51
frontend/src/components/Toast/index.tsx
Normal file
51
frontend/src/components/Toast/index.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
// ============================================================
|
||||
// Toast — 轻量通知组件
|
||||
// 职责:3 秒自动消失的通知提示
|
||||
// ============================================================
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { registerToastSetter, type ToastItem } from "../../lib/toast";
|
||||
|
||||
export type { ToastItem };
|
||||
|
||||
/** Toast 容器组件,放在 App 根部 */
|
||||
export function ToastContainer() {
|
||||
const [toasts, setToasts] = useState<ToastItem[]>([]);
|
||||
|
||||
// 注册全局 setter
|
||||
useEffect(() => {
|
||||
registerToastSetter(setToasts);
|
||||
return () => registerToastSetter(null);
|
||||
}, []);
|
||||
|
||||
const dismiss = useCallback((id: number) => {
|
||||
setToasts((prev) => prev.filter((t) => t.id !== id));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="toast-container">
|
||||
{toasts.map((t) => (
|
||||
<ToastItemView key={t.id} item={t} onDismiss={dismiss} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ToastItemView({
|
||||
item,
|
||||
onDismiss,
|
||||
}: {
|
||||
item: ToastItem;
|
||||
onDismiss: (id: number) => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => onDismiss(item.id), 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [item.id, onDismiss]);
|
||||
|
||||
return (
|
||||
<div className={`toast toast--${item.type}`} onClick={() => onDismiss(item.id)}>
|
||||
{item.message}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user