## Context PPT 项目是基于 Vue 3 + TypeScript + Pinia 的课件编辑器,以 iframe 嵌入 cocorobo 父应用。编辑器使用 `CollapsibleToolbar/index2.vue` 作为左侧栏,已有"英语"tab 入口(`activeSubmenu === 'english'`)。 enspeak demo(React/Next.js)`SpeakingPanelV2.tsx` 实现了三层页面结构: - Layer 1:英语学科主页(教材/年级/单元联动 + 练习类型选择) - Layer 2:口语子页面(创建方式切换 + 任务筛选 + 推荐卡片) - Layer 3:具体配置页(如 TopicDiscussionConfig) 本次只迁移话题讨论(Topic Discussion)功能,只做左侧配置面板。 ## Goals / Non-Goals **Goals:** - 完整迁移 demo 三层页面结构到 Vue 3(Layer 1 + Layer 2 + Layer 3) - Layer 1:教材/年级/单元三级联动下拉框 + 单元信息卡 + 练习类型入口 - Layer 2:创建方式切换(参照 demo)+ 推荐卡片列表(目前只有话题讨论类型) - Layer 3:TopicDiscussionConfig 话题讨论配置(讨论话题、学习目标、练习方式、高级配置) - AI 生成模式只迁移 UI 壳子,保留切换入口 - 配置完成后通过 `parentWindow.addTool(77)` 集成到课件 - 所有文案走 i18n(cn/en/hk) **Non-Goals:** - 不实现听力、阅读、写作的配置页(只做口语 - 话题讨论) - 不实现右侧主预览区域(由独立项目完成) - 不实现学生端练习界面 - 不实现 AI 生成的后端功能(只保留 UI) - 不实现后端 API 对接(静态 JSON 数据,后续改接口) - 不实现 TTS/ASR - 不做 teacher-dashboard ## Decisions ### 1. 组件目录结构 ``` src/views/Editor/EnglishSpeaking/ SpeakingPanel.vue # 三层页面容器(管理 layer1/layer2/config 切换) layers/ Layer1Home.vue # 英语学科主页:联动下拉框 + 单元信息 + 练习类型入口 Layer2Speaking.vue # 口语子页面:创建方式切换 + 推荐卡片 configs/ TopicDiscussionConfig.vue # 话题讨论配置(Layer 3) components/ CurriculumSelector.vue # 教材/年级/单元三级联动下拉框 UnitInfoCard.vue # 单元信息展示卡片 ExerciseTypeGrid.vue # 练习类型网格(口语/听力/阅读/写作) RecommendCard.vue # 推荐任务卡片 CreationModeSwitch.vue # 创建方式切换(智能推荐/AI生成/手动创建) AIGenerationForm.vue # AI 生成表单(仅 UI 壳子) data/ curriculum.json # 教材联动数据(来源 units-en.txt) topicDiscussionTasks.json # 话题讨论推荐任务(来源 task.txt) ``` **Why**: 按页面层级 + 职责拆分组件,而非按功能类型一股脑堆在一个文件里。layers/ 对应三层导航,configs/ 对应具体配置页,components/ 是复用的 UI 片段。符合用户要求的"合理分组件"。 **Alternatives considered**: 全部平铺在 EnglishSpeaking/ 下 — 文件多了会混乱,层级不清晰。 ### 2. 页面状态管理:组件内 ref 三层页面切换(`pageMode: 'layer1' | 'layer2' | 'config'`)由 `SpeakingPanel.vue` 内的 ref 管理,不需要放 Pinia store。 **Why**: 页面导航状态是纯 UI 状态,只在 SpeakingPanel 内部使用。demo 中也是组件内 useState 管理。Store 只放需要跨组件共享的配置数据。 ### 3. 配置数据管理:Pinia store `src/store/speaking.ts` 管理话题讨论配置状态(topic、learningGoals、practice、evaluation 等),对应 demo 中 `useTopicDiscussionConfig` hook 的逻辑。 **Why**: 配置数据需要在 Layer 2(推荐卡片预填充)和 Layer 3(配置表单编辑)之间共享,也需要在"应用配置"时传给父窗口。 ### 4. 类型定义:`src/types/englishSpeaking.ts` 从 enspeak `useTopicDiscussionConfig.ts` 移植话题讨论相关类型:TopicDiscussionConfig、PracticeSettings、EvaluationSettings、LearningGoals、Role 等。只移植话题讨论需要的,不搬整个 types/index.ts。 ### 5. 集成方式:替换英语 tab submenu 内容 当前 `CollapsibleToolbar/index2.vue` 英语 tab 的 submenu 里只有 video 占位。替换为渲染 `SpeakingPanel` 组件。 **Why**: 利用现有 submenu 面板机制,不需要改动 CollapsibleToolbar 的框架逻辑。 ### 6. 与父应用通信:`addTool(77)` 配置完成 → 调用 `parentWindow.addTool(77)` → 父应用回调 `window.addContent(data)` → PPT 创建 `{ type: 'frame', toolType: 77 }` element 嵌入 slide。 toolType 77 需要在 `BaseFrameElement.vue` 的 `getTypeLabel` 中注册。 ## Risks / Trade-offs - **[Tailwind → SCSS]** → Demo 用 Tailwind CSS,PPT 用 SCSS。需要逐个转换样式。→ 优先保证功能和布局一致,视觉细节可迭代。 - **[只有 Unit 2 有推荐数据]** → `topicDiscussionTasks.json` 目前只有 Unit 2 的 4 条推荐。→ 选择其他 Unit 时推荐列表为空,UI 上显示空状态提示,后续补充数据。 - **[AI 生成无后端]** → UI 壳子就绪但无实际功能。→ 点击生成后可显示"功能开发中"提示,不影响主流程。 - **[父应用 addTool(77) 未对接]** → 父应用侧还没有注册 77 类型工具。→ PPT 侧先做完配置面板,addTool 调用就绪,等父应用配合对接。