From 00f4447bc9e9186c8508ada813415c24db248992 Mon Sep 17 00:00:00 2001 From: cc <94575594@qq.com> Date: Mon, 23 Mar 2026 20:29:04 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20docs:=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E6=96=87=E6=A1=A3=E5=92=8C=E5=8F=A3=E8=AF=AD?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEPLOY.md | 64 ++++++++++++++++++++++++++++++++--------- src/views/Speaking.vue | 65 ++++++++++++++++++++++++++++-------------- 2 files changed, 94 insertions(+), 35 deletions(-) diff --git a/DEPLOY.md b/DEPLOY.md index bed2eb9..23647da 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -39,12 +39,13 @@ npm install | 配置项 | 说明 | |--------|------| -| `GRS_API_KEY` | GRS AI 接口密钥(作文批改 / 试题分析) | +| `GRS_API_KEY` | GRS AI 接口密钥(作文批改) | +| `DOUBAO_KEY` | 豆包 API 密钥 | | `DOUBAO_APP_ID` | 豆包语音 App ID | | `DOUBAO_ACCESS_TOKEN` | 豆包语音访问令牌 | | `DOUBAO_RESOURCE_ID` | 豆包 TTS 资源 ID | | `DOUBAO_ASR_RESOURCE_ID` | 豆包 ASR 资源 ID | -| `ARK_API_KEY` | 火山引擎 Ark 大模型密钥 | +| `ARK_API_KEY` | 火山引擎 Ark 大模型密钥(口语对话) | | `ARK_MODEL` | Ark 模型名称(如 doubao-pro-4k) | ### 2.2 跨域响应头要求(重要) @@ -290,7 +291,7 @@ DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js ### 方案 C:EdgeOne Pages / Vercel / Netlify(纯静态托管) -> **注意**:此类平台**不支持服务端代理**,`/tts-api`、`/ark-api`、`/dashscope-api`、`/asr-ws` 等接口将因跨域或缺少鉴权 Header 而失败。 +> **注意**:此类平台**不支持服务端代理**,`/tts-api`、`/ark-api`、`/dashscope-api`、`/asr-ws` 等接口将因跨域或缺少鉴权 Header 而失败。 > 建议仅用于演示或配合独立后端服务使用。 #### 部署步骤(以 Vercel 为例) @@ -321,14 +322,14 @@ DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js | 验证项 | 检查方法 | |--------|----------| -| 页面正常加载 | 访问首页,确认 7 个功能卡片显示正常 | +| 页面正常加载 | 访问首页,确认 6 个功能卡片显示正常 | | 路由刷新不 404 | 直接访问 `/pronunciation`、`/speaking` 等子路由 | | COOP/COEP 响应头 | 浏览器 DevTools → Network → 查看 index.html 响应头 | | TTS 语音合成 | 进入发音练习页,测试语音播放 | | ASR 语音识别 | 进入口语对话页,测试麦克风录音识别 | | 作文批改 | 进入作文批改页,提交一段英文作文 | -| 视频讲解(FFmpeg) | 进入视频讲解页,上传视频文件测试 | -| HTTPS 证书 | 确认浏览器地址栏显示锁形图标 | +| 试题分析 | 进入试题分析页,上传图片或输入题目 | +| 单词拼写练习 | 进入单词拼写页,测试拼写检测功能 | --- @@ -336,27 +337,27 @@ DOUBAO_APP_ID=xxx DOUBAO_ACCESS_TOKEN=xxx node server.js ### Q1:视频讲解页面报错 `SharedArrayBuffer is not defined` -**原因**:服务器未返回 `Cross-Origin-Opener-Policy: same-origin` 和 `Cross-Origin-Embedder-Policy: require-corp` 响应头。 +**原因**:服务器未返回 `Cross-Origin-Opener-Policy: same-origin` 和 `Cross-Origin-Embedder-Policy: require-corp` 响应头。 **解决**:参考第二节 2.2,在 Nginx 或 Node.js 服务器中添加对应响应头。 ### Q2:刷新页面出现 404 -**原因**:Vue Router 使用 History 模式,服务器未配置路由回退。 +**原因**:Vue Router 使用 History 模式,服务器未配置路由回退。 **解决**:Nginx 添加 `try_files $uri $uri/ /index.html;`,Node.js 添加通配路由返回 `index.html`。 ### Q3:API 请求跨域报错(CORS) -**原因**:生产环境未配置反向代理,浏览器直接请求第三方 API 被跨域拦截。 +**原因**:生产环境未配置反向代理,浏览器直接请求第三方 API 被跨域拦截。 **解决**:参考第二节 2.3,在 Nginx 或 Node.js 中配置对应代理规则。 ### Q4:ASR 语音识别 WebSocket 连接失败 -**原因**:代理未注入豆包 ASR 鉴权 Header,或 WebSocket 升级配置缺失。 +**原因**:代理未注入豆包 ASR 鉴权 Header,或 WebSocket 升级配置缺失。 **解决**:确认 Nginx 配置中包含 `proxy_set_header Upgrade $http_upgrade;` 及三个 `X-Api-*` Header。 ### Q5:构建时内存不足 -**原因**:项目包含 FFmpeg WASM,构建产物较大。 +**原因**:项目包含 FFmpeg WASM,构建产物较大。 **解决**: ```bash @@ -366,7 +367,35 @@ NODE_OPTIONS=--max-old-space-size=4096 npm run build --- -## 七、目录结构参考 +## 七、页面路由清单 + +| 路由路径 | 页面名称 | 功能说明 | +|----------|----------|----------| +| `/` | 首页 | 功能导航卡片 | +| `/pronunciation` | 发音练习 | TTS 语音合成,音标学习 | +| `/speaking` | 口语对话 | ASR 语音识别,AI 对话 | +| `/essay-correction` | 作文批改 | 图片/文本作文批改 | +| `/exam-analysis` | 试题分析 | 图片题目分析解答 | +| `/spell-practice` | 单词拼写 | 单词拼写练习检测 | + +--- + +## 八、依赖版本参考 + +| 依赖包 | 版本 | +|--------|------| +| vue | ^3.5.30 | +| vue-router | ^5.0.3 | +| vite | ^8.0.0 | +| @ffmpeg/ffmpeg | ^0.12.15 | +| @ffmpeg/util | ^0.12.2 | +| axios | ^1.13.6 | +| marked | ^17.0.5 | +| pako | ^2.1.0 | + +--- + +## 九、目录结构参考 ``` AI_Demo/ @@ -374,7 +403,16 @@ AI_Demo/ ├── src/ │ ├── config/index.js # API 密钥配置(部署前确认) │ ├── router/index.js # 路由定义 -│ └── views/ # 页面组件 +│ ├── views/ # 页面组件 +│ │ ├── HomePage.vue # 首页 +│ │ ├── Pronunciation.vue # 发音练习 +│ │ ├── Speaking.vue # 口语对话 +│ │ ├── EssayCorrection.vue # 作文批改 +│ │ ├── ExamAnalysis.vue # 试题分析 +│ │ └── SpellPractice.vue # 单词拼写 +│ ├── components/ # 公共组件 +│ ├── assets/ # 静态资源 +│ └── MainLayout.vue # 布局组件 ├── vite.config.js # 开发代理配置(生产环境需在服务器复现) ├── package.json └── DEPLOY.md # 本文档 diff --git a/src/views/Speaking.vue b/src/views/Speaking.vue index 2016758..8e27791 100644 --- a/src/views/Speaking.vue +++ b/src/views/Speaking.vue @@ -189,7 +189,8 @@ const scrollToBottom = async () => { }; // TTS 合成并播放 -const synthesizeAndPlay = async (text, msgId) => { +// autoPlay: 是否自动播放,默认为 true +const synthesizeAndPlay = async (text, msgId, autoPlay = true) => { const payload = { user: { uid: "speaking_" + Date.now() }, req_params: { @@ -264,7 +265,9 @@ const synthesizeAndPlay = async (text, msgId) => { const msg = messages.value.find((m) => m.id === msgId); if (msg) msg.audioUrl = url; - playAudio(url, msgId); + if (autoPlay) { + playAudio(url, msgId); + } }; // 播放音频 @@ -345,8 +348,8 @@ const sendMessage = async () => { messages.value.push(userMsg); await scrollToBottom(); - // 用户消息 TTS(不阻塞主流程) - synthesizeAndPlay(text, userMsgId).catch((e) => console.error("User TTS error:", e)); + // 用户消息 TTS(不阻塞主流程,不自动播放) + synthesizeAndPlay(text, userMsgId, false).catch((e) => console.error("User TTS error:", e)); // 插入 AI loading 占位 const aiMsgId = ++msgIdCounter; @@ -643,8 +646,17 @@ const startRecording = async () => { }; onUnmounted(() => { - if (currentAudioInstance) { currentAudioInstance.pause(); currentAudioInstance = null; } + // 停止音频播放 + if (currentAudioInstance) { + currentAudioInstance.pause(); + currentAudioInstance = null; + } + // 重置所有消息的播放状态 + messages.value.forEach((m) => (m.isPlaying = false)); + // 释放 blob URL blobUrls.forEach((url) => URL.revokeObjectURL(url)); + blobUrls.length = 0; + // 停止录音 stopRecording(); }); @@ -703,21 +715,20 @@ onUnmounted(() => { {{ msg.content }} - - - -