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:
2026-06-13 11:43:16 +08:00
parent 9ccd4d6238
commit 734c93a866
8 changed files with 357 additions and 31 deletions

View File

@@ -119,10 +119,10 @@ body {
.chat-section {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
overflow: hidden;
}
/* ---- Video Preview ---- */
@@ -157,6 +157,8 @@ body {
display: flex;
flex-direction: column;
gap: 12px;
overflow-y: auto;
flex: 1;
}
.chat-panel--empty {
@@ -248,3 +250,111 @@ body {
.btn--warning:hover {
opacity: 0.9;
}
/* ---- Streaming Cursor ---- */
.cursor {
display: inline;
animation: blink 0.8s step-end infinite;
color: var(--color-success);
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
/* ---- Message Meta ---- */
.chat-message__meta {
margin-top: 6px;
font-size: 0.75rem;
color: var(--color-text-muted);
}
/* ---- System Message ---- */
.system-message {
text-align: center;
padding: 8px 16px;
border-radius: var(--radius);
font-size: 0.85rem;
}
.system-message--warning {
background: rgba(245, 158, 11, 0.15);
color: var(--color-warning);
border: 1px solid rgba(245, 158, 11, 0.3);
}
/* ---- Toast ---- */
.toast-container {
position: fixed;
top: 16px;
right: 16px;
z-index: 1000;
display: flex;
flex-direction: column;
gap: 8px;
}
.toast {
padding: 10px 16px;
border-radius: var(--radius);
font-size: 0.85rem;
cursor: pointer;
animation: slideIn 0.3s ease-out;
max-width: 320px;
}
.toast--error {
background: rgba(239, 68, 68, 0.9);
color: white;
}
.toast--warning {
background: rgba(245, 158, 11, 0.9);
color: #000;
}
.toast--info {
background: rgba(37, 99, 235, 0.9);
color: white;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
/* ---- Video Overlay ---- */
.video-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius);
}
.video-overlay__spinner {
width: 32px;
height: 32px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}