diff --git a/msh_single_uniapp/.fixes/PROGRESS.md b/msh_single_uniapp/.fixes/PROGRESS.md new file mode 100644 index 0000000..d4e691b --- /dev/null +++ b/msh_single_uniapp/.fixes/PROGRESS.md @@ -0,0 +1,42 @@ +# MSH 前端修复进度追踪 + +**最后更新**: 2026-02-28 +**总任务数**: 9 +**待处理**: 9 +**进行中**: 0 +**已完成**: 0 + +--- + +## 任务清单 + +### 高优先级 +- [ ] FIX-001: 移除初始假数据 +- [ ] FIX-002: 添加清空对话按钮 +- [ ] FIX-003: 拆分单文件组件 + +### 中优先级 +- [ ] FIX-004: 多图合并为一轮对话 +- [ ] FIX-005: 消息列表使用稳定 key +- [ ] FIX-008: 提取硬编码配置 + +### 低优先级 +- [ ] FIX-006: 优化滚动到底部实现 +- [ ] FIX-007: 删除废弃方法 +- [ ] FIX-009: H5 语音输入降级优化 + +--- + +## 完成记录 + +| 日期 | 任务ID | 备注 | +|------|--------|------| +| - | - | 暂无 | + +--- + +## 下一步行动 + +1. 启动 FIX-001: 移除初始假数据(最简单,低风险) +2. 完成后启动 FIX-002: 添加清空对话按钮 +3. 最后处理 FIX-003: 组件拆分(工作量大) diff --git a/msh_single_uniapp/.fixes/backlog/FIX-001-remove-fake-data.md b/msh_single_uniapp/.fixes/backlog/FIX-001-remove-fake-data.md new file mode 100644 index 0000000..b7cc2e8 --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-001-remove-fake-data.md @@ -0,0 +1,51 @@ +# FIX-001: 移除初始假数据 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 高 +**负责人**: msh-agent + +--- + +## 问题描述 + +当前 `ai-nutritionist.vue` 页面 `data()` 中 `messageList` 数组包含两条写死的示例消息(用户问香蕉、AI回答),用户首次进入会误以为有历史对话记录。 + +### 当前行为 +页面加载后自动显示两条假对话消息。 + +### 预期行为 +首次进入时 `messageList` 为空,仅显示欢迎语(欢迎语固定在模板中)。 + +--- + +## 修复方案 + +**方案A(推荐)**:将 `messageList` 初始值设为空数组 `[]`,保留欢迎语在模板中的展示。 + +--- + +## 实施步骤 + +- [ ] 1. 备份原文件 +- [ ] 2. 修改 `data()` 中 `messageList: []` +- [ ] 3. 本地 H5 测试 +- [ ] 4. 微信小程序测试 +- [ ] 5. 提交代码 + +--- + +## 文件变更 + +修改 `pages/tool/ai-nutritionist.vue` 第 115-128 行: + +```javascript +// 修改前 +messageList: [ + { role: 'user', content: '我今天想吃香蕉,可以吗?', type: 'text' }, + { role: 'ai', content: '香蕉含钾量较高...', type: 'text' } +] + +// 修改后 +messageList: [] +``` diff --git a/msh_single_uniapp/.fixes/backlog/FIX-002-clear-chat-button.md b/msh_single_uniapp/.fixes/backlog/FIX-002-clear-chat-button.md new file mode 100644 index 0000000..1846345 --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-002-clear-chat-button.md @@ -0,0 +1,32 @@ +# FIX-002: 添加清空对话按钮 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 中 +**负责人**: msh-agent + +--- + +## 问题描述 + +`clearChat()` 方法已实现(清空消息列表和会话ID),但在模板中没有对应的触发按钮,用户无法清空对话。 + +### 预期行为 +在页面顶部导航栏或输入区域提供"清空对话"按钮。 + +--- + +## 修复方案 + +在 `promo-banner` 右侧添加清空按钮图标,绑定 `@click="clearChat"`。 + +--- + +## 实施步骤 + +- [ ] 1. 备份原文件 +- [ ] 2. 在 `promo-banner` 区域添加清空按钮 +- [ ] 3. 绑定 `@click="clearChat"` +- [ ] 4. 添加样式 +- [ ] 5. 测试确认对话框 +- [ ] 6. 多端测试 diff --git a/msh_single_uniapp/.fixes/backlog/FIX-003-split-components.md b/msh_single_uniapp/.fixes/backlog/FIX-003-split-components.md new file mode 100644 index 0000000..5dd803c --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-003-split-components.md @@ -0,0 +1,32 @@ +# FIX-003: 拆分单文件组件 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 高 +**负责人**: msh-agent + +--- + +## 问题描述 + +`ai-nutritionist.vue` 单文件超过 550 行,包含聊天、语音、图片、状态管理等多个功能模块,可维护性差。 + +--- + +## 修复方案 + +拆分为以下组件: +- `components/ai-chat/ChatMessage.vue` - 消息气泡 +- `components/ai-chat/ChatInput.vue` - 输入框 +- `components/ai-chat/VoiceRecorder.vue` - 语音录制 +- `components/ai-chat/ImageUploader.vue` - 图片上传 + +--- + +## 实施步骤 + +- [ ] 1. 备份原文件 +- [ ] 2. 创建 `components/ai-chat/` 目录 +- [ ] 3. 逐个提取组件 +- [ ] 4. 在主文件引入组件 +- [ ] 5. 测试所有功能正常 diff --git a/msh_single_uniapp/.fixes/backlog/FIX-004-merge-multi-images.md b/msh_single_uniapp/.fixes/backlog/FIX-004-merge-multi-images.md new file mode 100644 index 0000000..8a65b6a --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-004-merge-multi-images.md @@ -0,0 +1,18 @@ +# FIX-004: 多图合并为一轮对话 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 中 +**负责人**: msh-agent + +--- + +## 问题描述 + +当前多图时每张单独一轮 Coze 对话,AI 回复会一条条出现,且无法把"多图+一句说明"作为同一上下文。 + +--- + +## 修复方案 + +若 Coze 支持,一次 `additionalMessages` 里带多条(文本+多图),减少轮次、统一上下文。 diff --git a/msh_single_uniapp/.fixes/backlog/FIX-005-stable-key.md b/msh_single_uniapp/.fixes/backlog/FIX-005-stable-key.md new file mode 100644 index 0000000..cea7069 --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-005-stable-key.md @@ -0,0 +1,18 @@ +# FIX-005: 消息列表使用稳定 key + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 中 +**负责人**: msh-agent + +--- + +## 问题描述 + +`:key="index"` 在列表中间增删时可能影响复用与动画,建议改为稳定 id。 + +--- + +## 修复方案 + +为每条消息生成唯一 id(如 uuid 或服务端 id),用 `:key="msg.id"`。 diff --git a/msh_single_uniapp/.fixes/backlog/FIX-006-scroll-optimize.md b/msh_single_uniapp/.fixes/backlog/FIX-006-scroll-optimize.md new file mode 100644 index 0000000..0bd6da7 --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-006-scroll-optimize.md @@ -0,0 +1,18 @@ +# FIX-006: 优化滚动到底部实现 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 低 +**负责人**: msh-agent + +--- + +## 问题描述 + +`scrollToBottom` 使用 99998/99999 magic number,在部分机型上可能不生效。 + +--- + +## 修复方案 + +改为:先设一个很大的数触底,再在 `@scroll` 里记录实际 scrollTop,下次用 `lastScrollTop + 1` 等小步进触发一次滚动。 diff --git a/msh_single_uniapp/.fixes/backlog/FIX-007-remove-dead-code.md b/msh_single_uniapp/.fixes/backlog/FIX-007-remove-dead-code.md new file mode 100644 index 0000000..d5ae95d --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-007-remove-dead-code.md @@ -0,0 +1,12 @@ +# FIX-007: 删除废弃方法 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 低 +**负责人**: msh-agent + +--- + +## 问题描述 + +`sendImageMessage` 方法注释"已废弃,逻辑合并到 sendMessage",建议删除该方法,避免误导。 diff --git a/msh_single_uniapp/.fixes/backlog/FIX-008-extract-config.md b/msh_single_uniapp/.fixes/backlog/FIX-008-extract-config.md new file mode 100644 index 0000000..4225470 --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-008-extract-config.md @@ -0,0 +1,26 @@ +# FIX-008: 提取硬编码配置 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 中 +**负责人**: msh-agent + +--- + +## 问题描述 + +`botId`、轮询次数(60/30)、轮询间隔(1000/2000)、最大录音 60s 等硬编码,建议提到 `data` 或单独 config。 + +--- + +## 修复方案 + +创建配置对象集中管理: +```javascript +const CHAT_CONFIG = { + botId: '7591133240535449654', + maxPollAttempts: 60, + pollInterval: 1000, + maxRecordDuration: 60 +} +``` diff --git a/msh_single_uniapp/.fixes/backlog/FIX-009-h5-voice-fallback.md b/msh_single_uniapp/.fixes/backlog/FIX-009-h5-voice-fallback.md new file mode 100644 index 0000000..ba94d4d --- /dev/null +++ b/msh_single_uniapp/.fixes/backlog/FIX-009-h5-voice-fallback.md @@ -0,0 +1,18 @@ +# FIX-009: H5 语音输入降级优化 + +**状态**: 待开始 +**创建时间**: 2026-02-28 +**优先级**: 低 +**负责人**: msh-agent + +--- + +## 问题描述 + +H5 环境仅提示"暂不支持语音输入",可考虑集成 Web Speech API 作为降级方案。 + +--- + +## 修复方案 + +使用 Web Speech API (webkitSpeechRecognition) 作为 H5 环境的语音输入方案。 diff --git a/msh_single_uniapp/.fixes/scripts/complete-fix.sh b/msh_single_uniapp/.fixes/scripts/complete-fix.sh new file mode 100755 index 0000000..eb7b2e1 --- /dev/null +++ b/msh_single_uniapp/.fixes/scripts/complete-fix.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# 完成修复任务脚本 + +FIX_ID=$1 +if [ -z "$FIX_ID" ]; then + echo "Usage: complete-fix.sh FIX-XXX" + exit 1 +fi + +# 检查任务是否在进行中 +if [ ! -d ".fixes/in-progress/$FIX_ID" ]; then + echo "❌ 任务 $FIX_ID 不在 in-progress 中" + exit 1 +fi + +# 更新状态 +sed -i '' 's/状态.*$/状态: 已完成/' ".fixes/in-progress/$FIX_ID/README.md" +sed -i '' "s/完成时间.*$/完成时间: $(date +%Y-%m-%d)/" ".fixes/in-progress/$FIX_ID/README.md" + +# 移动到 completed +mv ".fixes/in-progress/$FIX_ID" ".fixes/completed/" + +# 更新进度日志 +echo "$(date +%Y-%m-%d) - $FIX_ID completed" >> .fixes/PROGRESS.md + +# 删除备份文件(确认已完成) +if [ -f "pages/tool/ai-nutritionist.vue.backup" ]; then + rm "pages/tool/ai-nutritionist.vue.backup" + echo "🗑️ 已清理备份文件" +fi + +echo "✅ 任务 $FIX_ID 已完成" +echo "📁 已移动到 .fixes/completed/$FIX_ID/" diff --git a/msh_single_uniapp/.fixes/scripts/start-fix.sh b/msh_single_uniapp/.fixes/scripts/start-fix.sh new file mode 100755 index 0000000..ca3ae39 --- /dev/null +++ b/msh_single_uniapp/.fixes/scripts/start-fix.sh @@ -0,0 +1,37 @@ +#!/bin/bash +# 开始修复任务脚本 + +FIX_ID=$1 +if [ -z "$FIX_ID" ]; then + echo "Usage: start-fix.sh FIX-XXX" + exit 1 +fi + +# 检查任务是否存在 +if [ ! -f ".fixes/backlog/$FIX_ID.md" ]; then + echo "❌ 任务 $FIX_ID 不存在于 backlog" + exit 1 +fi + +# 创建任务工作目录 +mkdir -p ".fixes/in-progress/$FIX_ID" + +# 复制任务文件 +cp ".fixes/backlog/$FIX_ID.md" ".fixes/in-progress/$FIX_ID/README.md" + +# 更新状态 +sed -i '' 's/状态.*$/状态: 进行中/' ".fixes/in-progress/$FIX_ID/README.md" + +# 备份原文件 +if [ -f "pages/tool/ai-nutritionist.vue" ]; then + cp pages/tool/ai-nutritionist.vue "pages/tool/ai-nutritionist.vue.backup" + echo "✅ 已备份原文件" +fi + +echo "✅ 任务 $FIX_ID 已启动" +echo "📁 工作目录: .fixes/in-progress/$FIX_ID/" +echo "" +echo "下一步:" +echo "1. 阅读 .fixes/in-progress/$FIX_ID/README.md" +echo "2. 创建测试用例" +echo "3. 实施修改" diff --git a/msh_single_uniapp/.fixes/templates/FIX_TEMPLATE.md b/msh_single_uniapp/.fixes/templates/FIX_TEMPLATE.md new file mode 100644 index 0000000..847903f --- /dev/null +++ b/msh_single_uniapp/.fixes/templates/FIX_TEMPLATE.md @@ -0,0 +1,85 @@ +# FIX-XXX: 任务标题 + +**状态**: 进行中 +**创建时间**: YYYY-MM-DD +**优先级**: 高/中/低 +**负责人**: msh-agent + +--- + +## 问题描述 + +详细描述要修复的问题... + +### 当前行为 +... + +### 预期行为 +... + +### 影响范围 +... + +--- + +## 修复方案 + +### 方案A(推荐) +... + +### 方案B +... + +--- + +## 实施步骤 + +- [ ] 1. 备份原文件 +- [ ] 2. 创建测试用例 +- [ ] 3. 实施修改 +- [ ] 4. 本地测试 +- [ ] 5. 多端测试(微信小程序/H5/App) +- [ ] 6. 代码审查 +- [ ] 7. 合并到主文件 +- [ ] 8. 更新文档 + +--- + +## 文件变更 + +### 新增文件 +- `components/xxx.vue` +- `utils/xxx.js` + +### 修改文件 +- `pages/tool/ai-nutritionist.vue` (行号 X-X) + +--- + +## 测试记录 + +### 测试环境 +- 平台: 微信小程序 / H5 / App +- 设备: iPhone / Android / 浏览器 + +### 测试结果 +| 测试项 | 结果 | 备注 | +|--------|------|------| +| 功能测试 | ⬜ | | +| 边界测试 | ⬜ | | +| 性能测试 | ⬜ | | + +--- + +## 回滚计划 + +如需回滚: +```bash +cp pages/tool/ai-nutritionist.vue.backup pages/tool/ai-nutritionist.vue +``` + +--- + +## 备注 + +... diff --git a/msh_single_uniapp/pages/tool/ai-nutritionist-REVIEW.md b/msh_single_uniapp/pages/tool/ai-nutritionist-REVIEW.md new file mode 100644 index 0000000..d0311d7 --- /dev/null +++ b/msh_single_uniapp/pages/tool/ai-nutritionist-REVIEW.md @@ -0,0 +1,206 @@ +# AI 营养师页面代码审查报告 + +**文件**: `pages/tool/ai-nutritionist.vue` +**审查范围**: 页面架构、Coze 集成、腾讯云 ASR、消息与状态、图片上传、问题与优化建议 + +--- + +## 1. 页面整体架构和组件设计 + +### 1.1 布局结构 + +页面采用**单文件 Vue 组件**,自上而下分为: + +| 区域 | 说明 | +|------|------| +| **header-container** | 顶部宣传横幅(慢生活守护健康、AI 营养师入驻) | +| **chat-container** | 可滚动聊天区(scroll-view),包含欢迎语、消息列表、加载态 | +| **quick-questions** | 快捷问题(2 个固定问题按钮) | +| **input-container** | 输入区:图片预览、相机/麦克风/输入框、发送按钮 | + +整体为 `flex` 纵向布局,`height: 100vh`,聊天区 `flex: 1` 占满中间,保证输入框始终在底部。 + +### 1.2 组件与复用 + +- **无子组件拆分**:所有 UI 均在当前页面内,未抽取为独立组件(如消息气泡、打字指示器、语音按钮等)。 +- **逻辑集中**:对话、语音、图片、滚动等逻辑均在当前页 `methods` 中,约 550+ 行,可维护性一般。 + +### 1.3 数据流 + +- **状态来源**:`data()` 本地状态 + Vuex `mapGetters(['userInfo','uid'])`。 +- **无专门状态库**:未使用 Pinia/Vuex 管理对话或会话,适合单页场景,多页共享会话需后续扩展。 + +--- + +## 2. Coze AI 对话集成的实现方式 + +### 2.1 调用链路 + +``` +sendMessage() + → sendToAI(content, type) + → api.cozeChat(requestData) + → pollChatStatus(conversationId, chatId) + → getChatMessages(conversationId, chatId) +``` + +- **入口**: 文本在 `sendMessage` 中拼好 `userMessage` 后调用 `sendToAI(text, 'text')`;图片在循环中对每张调用 `sendToAI(img.fileInfo || img.path, 'image')`。 +- **会话保持**: `conversationId` 存在时通过 `requestData.conversationId` 传给 Coze,实现多轮对话。 + +### 2.2 请求参数构造 + +- **文本消息**(type === 'text') + - `additionalMessages`: `[{ role: 'user', type: 'question', content, content_type: 'text' }]` +- **图片消息**(type === 'image') + - 使用 `fileInfo.id` 或 `fileInfo.file_id` 构造 `content_type: 'object_string'`,内容为 `JSON.stringify([{ type: 'image', file_id: fileId }])`。 + - 无 `fileId` 时降级为纯文本“我发送了一张图片,请帮我分析”。 + +### 2.3 非流式与轮询 + +- 使用 **stream: false**,不采用 SSE/流式。 +- 发起对话后根据返回的 `chat.conversation_id`、`chat.id` 做 **轮询**:`pollChatStatus` 每 1 秒请求 `api.cozeRetrieveChat`,最多 60 次。 +- 当 `status === 'completed'` 时调用 `getChatMessages` 拉取消息列表,再从中筛 `role === 'assistant'` 且 `type === 'answer'` 的消息,逐条 push 到 `messageList`。 + +### 2.4 错误与降级 + +- `cozeChat` 或后续轮询失败时,在 catch 里 push 一条 AI 文案(文本用 `getAIResponse(content)`,图片用固定“处理出错”提示)。 +- `getAIResponse` 为**本地兜底逻辑**:按关键词(如“香蕉”“苹果”“蛋白质”)返回预设回复,与真实 Coze 无关,仅用于接口失败时的展示。 + +--- + +## 3. 腾讯云 ASR 语音识别功能的实现 + +### 3.1 流程概览 + +1. **录音**: 使用 `uni.getRecorderManager()`(仅 `#ifdef MP-WEIXIN || APP-PLUS`),参数:60s、16k、单声道、mp3。 +2. **上传**: 录音结束后用 `api.uploadFile(res.tempFilePath, { model: 'audio', pid: '8' })` 上传到业务后端,得到 `fullUrl`。 +3. **创建任务**: `api.createAsrTask({ url: audioUrl, engineModelType: '16k_zh', channelNum: 1, resTextFormat: 0, sourceType: 0 })`,拿到 `taskId`。 +4. **轮询结果**: `pollAsrResult(taskId)`,每 2 秒查一次,最多 30 次(约 60 秒),直到 `status === 2` 取结果,`status === 3` 视为失败。 +5. **解析**: `parseAsrResult(data)` 去掉时间轴格式、换行,整理空格,得到纯文本。 +6. **回填**: 将识别结果写入 `inputText`,并自动切回文本模式并聚焦输入框。 + +### 3.2 平台与权限 + +- **H5**: `startRecord` 内直接弹窗“H5 暂不支持语音”,不调用录音。 +- **微信/App**: 先 `uni.authorize({ scope: 'scope.record' })`,拒绝则引导去设置页。 + +### 3.3 细节行为 + +- 录音时长 < 1 秒会 toast“录音时间太短”,不发起识别。 +- 最长录音 60 秒,`startRecordTimer` 到 60 秒会调用 `stopRecord()`;注意 `stopRecord` 内未传参,实际停止依赖 `recorderManager.onStop` 回调里的 `handleRecordResult(res)`。 + +--- + +## 4. 消息列表渲染和状态管理 + +### 4.1 数据结构 + +- **欢迎语**: 写死在模板里,内容来自 `welcomeMessage`,不进入 `messageList`。 +- **messageList 单项**: + - 用户: `{ role: 'user', content?, type: 'text' | 'image', imageUrl? }` + - AI: `{ role: 'ai', content }`(从 Coze 拉取时未统一写 `type: 'text'`,模板按 `msg.type !== 'image'` 判断,故正常按文本显示)。 + +### 4.2 渲染逻辑 + +- `v-for="(msg, index) in messageList"`,**:key="index"**:用 index 作 key,在列表中间增删时可能影响复用与动画,建议改为稳定 id。 +- 根据 `msg.role` 切换 `user-message` / `ai-message`;AI 显示头像与“AI营养师”名称,用户不显示。 +- 文本用 `message-text`,图片用 `message-image` + `@click="previewImage"`。 +- **加载态**: `isLoading === true` 时在列表底部渲染“打字指示器”(三个动点),与消息项同一列表内。 + +### 4.3 滚动 + +- `scroll-top` 绑定 `scrollTop`,`scrollToBottom()` 通过交替设置 `lastScrollTop` 为 99998/99999 再赋给 `scrollTop` 触发到底部。依赖 `scroll-with-animation` 有动画。 +- 在 `sendMessage`、`onInputFocus`、以及收到 AI 回复后都会调用 `scrollToBottom()`。 + +### 4.4 状态与竞态 + +- **isLoading**: 在 `sendToAI` 开头置 true,在 `getChatMessages` 成功或失败、以及 `pollChatStatus` 超时/失败时置 false。 +- **多图 + 文本**:先顺序 `await sendToAI` 每张图,再发一条文本。每轮都会把 loading 置 true,最后一条请求的结束会置 false,逻辑正确,但多图会连续多轮 loading,体验可优化(见下文建议)。 + +--- + +## 5. 图片上传和处理逻辑 + +### 5.1 选择与上传 + +- **chooseImage**: `uni.chooseImage`,count 为 `3 - pendingImages.length`,最多 3 张;`sizeType: ['compressed']`,`sourceType: ['album', 'camera']`。 +- 每选一张即调用 **api.cozeUploadFile(filePath)**(Coze 专用上传),成功则 push 到 `pendingImages`:`{ path: filePath, fileInfo: fileInfo }`。上传失败 toast,并 `uni.hideLoading()`。 + +### 5.2 发送时处理 + +- 在 `sendMessage` 中先 `imagesToSend = [...pendingImages]`,再清空 `pendingImages`。 +- 对每张图: + - 立即 push 一条用户消息:`role: 'user', type: 'image', imageUrl: img.path, content: '[图片]'`。 + - 注意这里 **imageUrl 用的是本地 path**,在部分环境下可能一段时间有效,若需长期展示建议用上传后的线上 URL(若后端有返回)。 + - 然后 `await sendToAI(img.fileInfo || img.path, 'image')`。多图会顺序产生多轮对话,每轮一次 AI 回复。 + +### 5.3 预览与删除 + +- 待发送区:`pendingImages` 列表展示缩略图,点击删除调用 `removeImage(index)` splice 掉。 +- 已发送图片:点击气泡内图片 `previewImage(msg.imageUrl)`,使用 `uni.previewImage`。 + +### 5.4 Coze 上传接口 + +- `api.cozeUploadFile` 使用 `uni.uploadFile`,name 为 `'file'`,成功时 `JSON.parse(res.data)` 后 resolve。 +- 页面侧兼容 `uploadRes.code === 0 || uploadRes.code === 200` 以及 `fileInfo.id` / `fileInfo.file_id`,兼容不同后端约定。 + +--- + +## 6. 发现的问题和优化建议 + +### 6.1 功能与逻辑问题 + +| 问题 | 严重程度 | 说明与建议 | +|------|----------|------------| +| **初始 messageList 为假数据** | 中 | 当前写死两条“用户+AI”示例消息,易让用户误以为是历史。建议首次进入时 `messageList` 为空,仅保留欢迎语;或从服务端拉取真实历史再渲染。 | +| **clearChat 未在界面暴露** | 低 | `clearChat()` 已实现(清空列表 + 清空 conversationId),但模板中无按钮调用,用户无法清空对话。建议在头部或输入区增加“清空对话”入口。 | +| **showCommonQuestions 仅 toast** | 低 | 仅提示“常见问题功能开发中”,若暂无规划可移除或改为跳转帮助页。 | +| **sendImageMessage 空实现** | 低 | 注释“已废弃,逻辑合并到 sendMessage”,建议删除该方法,避免误导。 | +| **多图多轮对话** | 中 | 多图时每张单独一轮 Coze,AI 回复会一条条出现,且无法把“多图+一句说明”作为同一上下文。若 Coze 支持多附件一轮,建议改为:一次 `additionalMessages` 里带多条(文本+多图),减少轮次、统一上下文。 | + +### 6.2 健壮性与错误处理 + +| 问题 | 建议 | +|------|------| +| **cozeChat 返回结构变化** | 当前直接取 `response.data.chat`,若后端结构调整易报错。建议加存在性判断(如 `response?.data?.chat`),并对缺少 `conversation_id` 的情况做提示或重试。 | +| **cozeUploadFile 未统一解析 code** | `cozeUploadFile` 的 success 里只 `JSON.parse(res.data)` 未校验 code;页面侧用 code 0/200 判断。建议在 api 层统一解析并 reject 非成功 code,页面只处理成功结果。 | +| **ASR 轮询超时 60s** | 60 秒对语音识别偏长,可适当缩短(如 20 次 × 2 秒)并提示“识别超时,请重试”。 | +| **录音 60 秒自动 stop** | `stopRecord()` 无参,`recorderManager.stop()` 会触发 `onStop`,但若用户 60 秒松手,与定时器几乎同时可能产生两次结束,可加防抖或状态位避免重复处理。 | + +### 6.3 性能与体验 + +| 问题 | 建议 | +|------|------| +| **消息列表 key 用 index** | 为每条消息生成唯一 id(如 uuid 或服务端 id),用 `:key="msg.id"`,避免中间插入导致错位或动画异常。 | +| **scrollToBottom 依赖 magic number** | 99998/99999 在部分机型上可能不生效。可改为:先设一个很大的数触底,再在 `@scroll` 里记录实际 scrollTop,下次用 `lastScrollTop + 1` 等小步进触发一次滚动。 | +| **多图连续 loading** | 多图时会出现多次“加载中 → 回复 → 加载中 → 回复”。若后端支持一次请求多图,可合并为一次 loading;否则可考虑“仅最后一条显示 loading”或“所有图片请求发完再统一轮询一次”(需后端支持)。 | + +### 6.4 代码与维护 + +| 问题 | 建议 | +|------|------| +| **单文件过长** | 可拆分为:消息列表(含欢迎语、气泡、打字指示器)、输入栏(文本/语音/图片预览/发送)、快捷问题等子组件,便于复用和单测。 | +| **魔法数字与配置** | `botId`、轮询次数(60/30)、轮询间隔(1000/2000)、最大录音 60s 等建议提到 `data` 或单独 config,便于环境区分与调参。 | +| **条件编译分散** | `#ifdef MP-WEIXIN || APP-PLUS` 与 `#ifdef H5` 分散在 initRecorder、startRecord、stopRecord 等处,可集中到 mixin 或 composable(如 useRecorder),H5 直接返回“不支持”的接口。 | +| **getAIResponse 用途不清** | 仅用于 Coze 失败时的兜底,但名字易让人以为是主流程。建议重命名为 `getFallbackResponse` 或移到工具/常量文件,并注释“仅作接口失败兜底”。 | + +### 6.5 API 与数据约定 + +| 问题 | 建议 | +|------|------| +| **Coze 请求字段命名** | 当前传 `botId`、`userId`、`additionalMessages` 等 camelCase,若后端期望 snake_case(如 `bot_id`),需在 api 层或页面层做一层转换,避免 400 或静默失败。 | +| **用户图片消息 imageUrl** | 当前用本地 path,跨会话或重新打开可能失效。若后端/Coze 返回可访问的图片 URL,建议存该 URL 到 `imageUrl`,便于持久化与分享。 | + +--- + +## 7. 总结 + +| 维度 | 评价 | +|------|------| +| **架构** | 单页单组件,结构清晰但体积偏大,建议按区域拆组件。 | +| **Coze 集成** | 非流式 + 轮询实现完整,多轮会话、文本/图片消息和错误降级均有考虑;可加强返回结构校验与多图合并发送。 | +| **ASR** | 录音 → 上传 → 腾讯云 ASR 任务 → 轮询 → 解析流程完整,平台与权限处理到位;可缩短超时、避免重复 onStop。 | +| **消息与状态** | 渲染与滚动逻辑正确;建议稳定 key、初始列表去假数据、暴露清空对话。 | +| **图片** | 选择、Coze 上传、待发送预览与发送流程完整;建议用户消息使用持久化 URL、评估多图合并一轮。 | + +按上述问题逐项整改后,可提升可维护性、健壮性和用户体验。