--- author: AI Assistant created_at: 2026-07-19 purpose: 修复心愿实现 chat 视图在等待接口响应期间没有 loading 标识、呈现空白等待状态的问题 --- # 心愿实现 chat 视图 Loading 标识修复设计 ## 概述 心愿实现页面(`ScriptView.vue` 的 chat 视图)在用户发送消息后、等待后端 SSE 接口响应的阶段,页面处于空白状态,没有任何 loading 标识,无法判断是否卡住。需要在等待期间展示动态 loading,提升等待体验。 ## 问题根因 `ScriptView.vue` 中 chat-loading 组件的显示条件(line 177): ```html ``` `runGeneration` 函数(line 1618-1672)中: ```js generating.value = true // Line 1630 // ... try { currentStreamTask.value = startNovelStream({...}) // Line 1653 - 异步 fire-and-forget } finally { generating.value = false // Line 1671 - 立即执行! } ``` **Bug**:`startNovelStream` 是异步流式调用(fire-and-forget,没有 await),`finally` 在流启动后**立即**把 `generating` 设回 `false`。结果: 1. 用户点发送 → `generating = true` → 切换到 chat 视图 2. 几乎同时 → `finally` 执行 → `generating = false` → chat-loading 立即隐藏 3. 在第一个 SSE 事件(`status`)到达前,页面只有用户消息气泡,下方空白 4. 用户看到"发送 → 空白等待 → 突然跳出内容",无法判断系统是否在工作 ## 修复方案 **用新的 computed `showChatLoading` 替换原有显示条件**,语义精确匹配"等待首个 assistant 响应": ```js const showChatLoading = computed(() => { if (generationPhase.value !== 'generating') return false if (generationStatus.value === 'failed') return false const lastMsg = resultMessages.value[resultMessages.value.length - 1] // 最后一条消息是 user(刚发送的请求),说明还没收到 assistant 响应 return lastMsg && lastMsg.role === 'user' }) ``` ```html {{ generationCopy }} ``` ### 场景覆盖表 | 场景 | generationPhase | 最后一条消息 | loading 显示 | |---|---|---|---| | 用户刚发送 | 'generating' | user | ✅ 显示 | | SSE status 事件(processing 阶段)| 'generating' | user | ✅ 显示 | | `clarification_card` 到达 | 'generating' | assistant (card) | ❌ 隐藏 | | 用户回答澄清问题 | 'generating' | user | ✅ 显示 | | `outline_created` 到达 | 'generating' | assistant (outline) | ❌ 隐藏 | | 用户提交大纲修改意见 | 'generating' | user | ✅ 显示 | | `novel_start` 到达 | 'generating' | assistant (novel) | ❌ 隐藏 | | `novel_done` 到达 | 'done' | assistant | ❌ 隐藏 | ### 为什么这样设计 1. **用 `generationPhase` 而非 `generating`**:`generationPhase` 在 `runGeneration` 开始时设为 'generating',在 `novel_done` 时设为 'done',完整覆盖生成流程生命周期。`generating` 因为 try-finally 立即重置,不可靠。 2. **用最后一条消息的 role 判断**:assistant 消息(card/outline/novel)到达后,用户看到的是交互内容而非空白,loading 应自动隐藏。通过判断最后一条消息的 role,精确实现"等待首个响应"的语义。 3. **支持多轮交互**:用户回答澄清问题、提交大纲修改后,最后一条又变回 user,loading 自动重新显示。无需额外的 flag 管理。 ## 改动文件 - `mini-program/src/pages/main/ScriptView.vue` - 新增 computed `showChatLoading`(在 `generationCopy` 附近,约 line 930-950) - 替换 line 177 的 `v-if` 条件 ## 不影响范围 - `chat-loading` 组件的样式、动画内容保持不变 - `generating` 变量的其他用途(按钮禁用、重试逻辑等)不变 - `generationStatus`、`generationPhase`、`generationCopy` 等 computed 不变 - `home` 视图、`generation` 视图、`chat-input-bar` 不受影响 - `sendChat`、`sendRewrite`、`sendChatAsContinue` 等其他流程不受影响(它们本身有 await,generating 状态正确) ## 验收标准 - [ ] H5(`http://localhost:5180`):点击发送后,chat 视图立即显示 loading 动画(loading-orbit + generationCopy 文本) - [ ] Loading 持续到 `clarification_card` / `outline_created` / `novel_start` 任一事件到达前 - [ ] 澄清卡片 / 大纲 / 小说消息出现后,loading 自动隐藏 - [ ] 用户回答澄清、提交大纲修改后,loading 重新显示直到下一个 assistant 响应到达 - [ ] 失败状态(`generationStatus === 'failed'`)不显示 loading,显示 resume 按钮 - [ ] 浏览器 Console 无新增错误 - [ ] mp-weixin 编译通过(`npm run build:mp-weixin` 无 error) - [ ] 编译产物中 `.chat-loading` 的 v-if 使用 `showChatLoading`