From 95ce23f172694c86b31a0b8d7e1e024cf54a0098 Mon Sep 17 00:00:00 2001 From: Peanut Date: Mon, 20 Jul 2026 00:16:05 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=BF=83=E6=84=BF=E5=AE=9E=E7=8E=B0=20?= =?UTF-8?q?chat=20=E8=A7=86=E5=9B=BE=20loading=20=E6=A0=87=E8=AF=86?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-07-19-chat-view-loading-fix-design.md | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-19-chat-view-loading-fix-design.md diff --git a/docs/superpowers/specs/2026-07-19-chat-view-loading-fix-design.md b/docs/superpowers/specs/2026-07-19-chat-view-loading-fix-design.md new file mode 100644 index 0000000..e6d0981 --- /dev/null +++ b/docs/superpowers/specs/2026-07-19-chat-view-loading-fix-design.md @@ -0,0 +1,109 @@ +--- +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`