PPT 项目是基于 Vue 3 + TypeScript + Pinia 的课件编辑器,以 iframe 嵌入 cocorobo 父应用。编辑器使用 CollapsibleToolbar/index2.vue 作为左侧栏,已有"英语"tab 入口(activeSubmenu === 'english')。
enspeak demo(React/Next.js)SpeakingPanelV2.tsx 实现了三层页面结构:
本次只迁移话题讨论(Topic Discussion)功能,只做左侧配置面板。
Goals:
parentWindow.addTool(77) 集成到课件Non-Goals:
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/ 下 — 文件多了会混乱,层级不清晰。
三层页面切换(pageMode: 'layer1' | 'layer2' | 'config')由 SpeakingPanel.vue 内的 ref 管理,不需要放 Pinia store。
Why: 页面导航状态是纯 UI 状态,只在 SpeakingPanel 内部使用。demo 中也是组件内 useState 管理。Store 只放需要跨组件共享的配置数据。
src/store/speaking.ts管理话题讨论配置状态(topic、learningGoals、practice、evaluation 等),对应 demo 中 useTopicDiscussionConfig hook 的逻辑。
Why: 配置数据需要在 Layer 2(推荐卡片预填充)和 Layer 3(配置表单编辑)之间共享,也需要在"应用配置"时传给父窗口。
src/types/englishSpeaking.ts从 enspeak useTopicDiscussionConfig.ts 移植话题讨论相关类型:TopicDiscussionConfig、PracticeSettings、EvaluationSettings、LearningGoals、Role 等。只移植话题讨论需要的,不搬整个 types/index.ts。
当前 CollapsibleToolbar/index2.vue 英语 tab 的 submenu 里只有 video 占位。替换为渲染 SpeakingPanel 组件。
Why: 利用现有 submenu 面板机制,不需要改动 CollapsibleToolbar 的框架逻辑。
addTool(77)配置完成 → 调用 parentWindow.addTool(77) → 父应用回调 window.addContent(data) → PPT 创建 { type: 'frame', toolType: 77 } element 嵌入 slide。
toolType 77 需要在 BaseFrameElement.vue 的 getTypeLabel 中注册。
topicDiscussionTasks.json 目前只有 Unit 2 的 4 条推荐。→ 选择其他 Unit 时推荐列表为空,UI 上显示空状态提示,后续补充数据。