design.md 4.7 KB

Context

PPT 编辑器采用三栏布局:左侧 CollapsibleToolbar(配置面板)、中间 Canvas(画布)、右侧 Toolbar(目前隐藏)。english-speaking 功能已完成教师配置流程(Layer 1-3),教师点击"应用配置"后触发 parentWindow.addTool(77) 向画布添加一个 PPTFrameElement

画布中的 BaseFrameElement.vue 负责渲染 PPTFrameElement,按 toolType 分支:

  • toolType 74 → <video> 标签
  • toolType 75 → <iframe> (bilibili)
  • 其他 → <iframe :src="url"><iframe :srcdoc="url">

当前 toolType 77 走的是通用 iframe 分支。现在要改为直接渲染 Vue 组件,不再使用 iframe。

enspeak demo 项目中的 TopicDiscussionPreview 组件提供了完整的学生视角预览,包含三个阶段:准备页(Ready)→ 对话过程(Chatting)→ 完成报告(Completed),需要迁移到 PPT 项目中。

demo 组件依赖树:

TopicDiscussionPreview.tsx
├── StudentPreview.tsx        — 16:9 PPT 页面容器
├── DialogueChatView.tsx      — 对话交互(语音条、即时反馈、提示、徽章)
├── OverallReport.tsx         — 整体报告(评分、四维能力、AI 点评、统计)
└── DetailedReport.tsx        — 详细报告(按轮次分组、单句评价卡片)

Goals / Non-Goals

Goals:

  • 将 TopicDiscussionPreview 从 React + Tailwind 迁移为 Vue 3 + SCSS
  • 保持 demo 的完整 UI 和交互逻辑(三阶段切换、模拟对话脚本、模拟评估数据)
  • 组件放在 src/views/Editor/EnglishSpeaking/preview/ 目录下
  • BaseFrameElement.vue 中 toolType 77 时直接渲染 Vue 组件(替代 iframe)
  • 在现有 src/types/englishSpeaking.ts 中追加预览相关类型

Non-Goals:

  • 不实现真实的语音识别/AI 对话(使用模拟脚本)
  • 不实现真实的音频播放(静态波形展示)
  • 不修改编辑器主布局(index3.vue)
  • 不处理与后端 API 的对接

Decisions

1. 集成方式:Vue 组件直接渲染 vs iframe

选择: 在 BaseFrameElement.vue 的 v-if/v-else-if 链中新增 toolType 77 分支,直接渲染 <TopicDiscussionPreview> Vue 组件。 理由: 与 iframe 方式相比,Vue 组件可以直接访问 Pinia store 和父组件状态,便于后续配置联动;同时避免了 iframe 通信的复杂性。类似 toolType 74 渲染 <video> 的模式。 实现:

<!-- BaseFrameElement.vue 第 26 行后插入 -->
<TopicDiscussionPreview
  v-else-if="elementInfo.toolType === 77 && !isThumbnail && isVisible"
  :style="{ width: width + 'px', height: height + 'px' }"
/>

2. 组件架构:平铺式组件 vs 嵌套式组件

选择: 平铺式组件(每个子组件独立文件),保持与 demo 一致的层级结构。 理由: 每个组件职责明确、代码量大(DialogueChatView 700+ 行),拆分有利于维护。

3. 状态管理:组件内 ref vs Pinia store

选择: 组件内 ref 管理预览状态(dialogueState、messages 等)。 理由: 预览状态是纯 UI 状态,不需要跨组件共享或持久化,使用 store 过度设计。

4. 样式方案:Tailwind 转换策略

选择: 手动转换为 scoped SCSS,与 PPT 项目其他组件保持一致。 理由: PPT 项目不使用 Tailwind,保持技术栈统一。

5. 图标方案:SVG 内联

选择: 所有图标(MicIcon、VolumeIcon、LightbulbIcon、RefreshIcon、XIcon、ChevronDownIcon、PlayIcon、CheckIcon)使用 SVG 内联方式直接写在模板中。 理由: PPT 项目没有统一的图标组件库,内联 SVG 最简单直接,不引入额外依赖。从 enspeak demo 的 @/components/icons 中提取 SVG 路径数据。

6. 类型文件:扩展现有文件

选择: 在现有 src/types/englishSpeaking.ts 中追加预览相关类型(OverallEvaluationSentenceEvaluationAIRole 等)。 理由: 该文件已存在且包含配置侧类型(TopicDiscussionConfigRole 等),预览类型与其紧密关联,保持集中管理。

Risks / Trade-offs

  • [代码量大] DialogueChatView 有 700+ 行 React 代码,迁移工作量较大 → 分步迁移,先骨架后细节
  • [模拟数据硬编码] 对话脚本和评分数据硬编码在组件中 → 后续可提取为配置,当前阶段可接受
  • [无真实音频] 语音条是纯静态的 → 与 demo 行为一致,后续集成真实语音时再改
  • [画布缩放] BaseFrameElement 内的 Vue 组件需要适配画布的缩放比例 → 组件使用 100% 宽高填充父容器,缩放由 BaseFrameElement 外层处理
  • [SVG 内联冗余] 多个组件可能重复相同的 SVG 代码 → 当前可接受,后续如果图标增多可抽取为公共组件