design.md 5.2 KB

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.vuegetTypeLabel 中注册。

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 调用就绪,等父应用配合对接。