新增: 多会话并发前端 UI 阶段3/4(F-09 收尾)

后端 per-conv 并发已就绪(d899c58 + CR-03~07),前端呈现层收尾:

阶段3 多会话生成态:
- stores/ai.ts generatingConvId 单值 → generatingConvs:Set + add/remove/isGenerating helper
- useAiEvents 5 处迁移(Completed/Error/路由标记改 per-conv Set 操作,
  AiError 精确清出错会话非全局清空,修原 = null 误清其他会话)
- ConversationSidebar 多会话并行生成脉冲指示(isGenerating 各会话独立)

阶段4 独立窗口多会话:
- useAiWindow label ai-detached → ai-detached-{convId}(per-conv 独立窗口)
- localStorage 单 key → per-conv key(防多会话串扰,读路径兼容旧 key 回退)

vue-tsc EXIT 0 + generatingConvId 零代码残留(仅注释)
This commit is contained in:
2026-06-19 20:15:37 +08:00
parent 19eb14ffc8
commit 7d5402951b
12 changed files with 181 additions and 53 deletions

View File

@@ -359,7 +359,7 @@ function handleLifecycleEvent(event: AiChatEvent): boolean {
flushCurrentText()
state.currentText = ''
state.streaming = false
state.generatingConvId = null
state.generatingConvs.delete(event.conversation_id || '')
state.agentRound = 0 // AE-2025-07: agentic 结束,复位轮次(隐藏进度条)
pendingMaxRounds.value = false // F-260616-03: 收尾(停止/续跑后新一轮达 max 才会再 set),清操作卡
pendingDirAuth.value = null // F-260619-03 Phase B: 收尾清路径授权弹窗(防残留)
@@ -389,7 +389,12 @@ function handleLifecycleEvent(event: AiChatEvent): boolean {
state.convTokenTotal = { prompt: event.prompt_tokens, completion: event.completion_tokens, total: event.total_tokens }
}
}
// 清理分离窗口生成态快照
// 清理分离窗口生成态快照(F-09:per-conv key,清本会话快照;兼容旧单 key)
const doneConv = event.conversation_id || ''
if (doneConv) {
localStorage.removeItem(`df-ai-gen-${doneConv}`)
localStorage.removeItem(`df-ai-text-${doneConv}`)
}
localStorage.removeItem('df-ai-gen')
localStorage.removeItem('df-ai-text')
void loadConversations()
@@ -407,7 +412,7 @@ function handleLifecycleEvent(event: AiChatEvent): boolean {
// 保留"回答到一半"的部分回复(否则清 currentText 后部分内容消失)。
flushCurrentText()
state.streaming = false
state.generatingConvId = null
state.generatingConvs.delete(event.conversation_id || '')
state.currentText = ''
state.agentRound = 0 // AE-2025-07: agentic 异常中断,复位轮次
state.queue = [] // B-32:错误收尾清队列,防生成中入队的消息被静默丢失(drainQueue 仅 AiCompleted 触发)
@@ -416,6 +421,12 @@ function handleLifecycleEvent(event: AiChatEvent): boolean {
state.pendingApprovals = []
pendingMaxRounds.value = false // F-260616-03: 异常中断,清操作卡
pendingDirAuth.value = null // F-260619-03 Phase B: 异常中断清路径授权弹窗
// F-09: 清理分离窗口生成态快照(per-conv key,清本会话快照;兼容旧单 key)
const errConv = event.conversation_id || ''
if (errConv) {
localStorage.removeItem(`df-ai-gen-${errConv}`)
localStorage.removeItem(`df-ai-text-${errConv}`)
}
localStorage.removeItem('df-ai-gen')
localStorage.removeItem('df-ai-text')
// UX-03: 错误消息携带 error_type(供错误气泡差异化显隐「去设置」按钮)。
@@ -450,14 +461,15 @@ export function handleEvent(event: AiChatEvent) {
const isCurrent = !convId || convId === state.activeConversationId
if (!isCurrent) {
if (event.type === 'AiCompleted' || event.type === 'AiError') {
state.generatingConvId = null
// F-09: 多会话并发 — 仅从 Set 移除该 conv(其他会话可能仍在生成),不再置 null 全局清空
state.generatingConvs.delete(convId || '')
void loadConversations()
}
return
}
// 标记正在生成的对话(完成/错误事件除外)
if (convId && event.type !== 'AiCompleted' && event.type !== 'AiError') {
state.generatingConvId = convId
state.generatingConvs.add(convId)
}
// 流式看门狗:活跃事件(delta/工具/新轮/审批结果)重置;审批等待/完成/错误在 case 内 clear
if (!NO_RESET_WATCHDOG.has(event.type)) {