# 备课模块使用问题修复设计 > **日期**:2026-06-23 > **前置文档**:[2026-06-22-lesson-preparation-anchor-canvas-design.md](./2026-06-22-lesson-preparation-anchor-canvas-design.md) > **审计来源**:深度分析备课模块现状后发现的使用问题 ## 一、问题背景 备课模块 v3 锚点画布重构已完成,但实际使用中存在 15 个问题(3 个 P0 阻塞性 + 6 个 P1 严重 + 6 个 P2 一般),导致核心流程断裂、角色使用不完整。 ### 核心闭环断裂 ``` 教师备课 → 发布课案 → 学生查看 → 家长查看 ↑ ❌ P0-1 ❌ P0-2/3 ❌ P0-2/3 无"发布"action 无权限/路由 无权限/路由 ``` ## 二、功能定位 ### 备课模块核心闭环 1. **教师备课**:选定教材+章节 → 锚点画布编辑 → 保存版本 → **发布** 2. **学生查看**:按班级可见 published 课案,只读画布视图 3. **家长查看**:通过孩子关系自动可见 published 课案 4. **教研监督**:教研组长/年级主任查看本年级教师备课(只读) 5. **管理员管理**:全校课案列表 + 统计 ## 三、角色使用矩阵 | 角色 | 权限 | 路由 | 视图 | |------|------|------|------| | 教师 | CREATE/READ/UPDATE/DELETE/PUBLISH | `/teacher/lesson-plans` | 完整编辑画布 + 发布按钮 | | 学生 | READ(仅 published) | `/student/lesson-plans/[planId]/view` | 只读画布视图 | | 家长 | READ(仅孩子的 published) | `/parent/lesson-plans/[planId]/view` | 只读画布视图 | | 管理员 | 全部 | `/admin/lesson-plans` | 全校课案列表 + 统计 | | 教研组长 | READ(本年级教师) | `/grade-head/lesson-plans` | 本年级教师课案列表(只读) | | 年级主任 | READ(本年级教师) | `/teaching-head/lesson-plans` | 同上 | ### 学生查看视图设计 **只读画布视图**:复用 React Flow,配置: - `nodesDraggable={false}` - `nodesConnectable={false}` - `elementsSelectable={true}`(可点击查看节点详情) - `panOnDrag={true}`、`zoomOnScroll={true}`(可缩放平移) 保留正文+节点+锚点的完整视觉关系,学生可缩放平移但不可编辑。 ### 发布机制 - 发布到班级所有学生,家长通过孩子关系自动可见 - 状态流转:`draft` → `published`(发布)→ `draft`(撤回)或 `archived`(归档) - 发布后学生/家长立即可见,撤回后立即不可见 ## 四、修复范围(P0+P1 优先) ### P0 阻塞性问题 | 编号 | 问题 | 修复方案 | |------|------|----------| | P0-1 | 无"发布课案"action | 新增 `publishLessonPlanAction` / `unpublishLessonPlanAction` | | P0-2 | 学生/家长无权限 | 给 student/parent 添加 `LESSON_PLAN_READ` 权限 | | P0-3 | 无学生/家长/管理员路由 | 新增路由 + 导航入口 | ### P1 严重问题 | 编号 | 问题 | 修复方案 | |------|------|----------| | P1-1 | 锚点节点选择器占位 | 完善 `AnchorNodeSelector`,接收节点列表 + 实现"锚定到新节点" | | P1-2 | 锚点偏移计算不可靠 | 改用 `markdownToPlainText` 搜索定位,支持跨段落 | | P1-3 | 边回写丢失 anchorId | anchorId 存入 `edge.data`,`fromRfEdges` 从 data 读取 | | P1-4 | 锚点边颜色无区分 | 使用 `getNodeColor(anchor.nodeId)` | | P1-5 | 教研组长无权限 | 给 grade_head/teaching_head 添加 `LESSON_PLAN_READ` 权限 | | P1-6 | 个人模板不显示 | TemplatePicker 调用 `getLessonPlanTemplatesAction` 合并显示 | ### P2 一般问题(本次顺带修复) | 编号 | 问题 | 修复方案 | |------|------|----------| | P2-1 | 正文节点侧边面板提示误导 | 改为"请在画布上直接操作正文"或显示锚点列表 | | P2-5 | 默认骨架节点位置重叠 | 增大列间距,避免与正文节点重叠 | ## 五、实施阶段 ### 阶段 1:权限与路由基础(P0-2、P0-3、P1-5) **权限修改**(`shared/lib/permissions.ts`): - student 数组添加 `Permissions.LESSON_PLAN_READ` - parent 数组添加 `Permissions.LESSON_PLAN_READ` - grade_head 数组添加 `Permissions.LESSON_PLAN_READ` - teaching_head 数组添加 `Permissions.LESSON_PLAN_READ` **路由新增**: - `app/(dashboard)/student/lesson-plans/page.tsx` — 课案列表(按班级 published) - `app/(dashboard)/student/lesson-plans/[planId]/view/page.tsx` — 只读画布 - `app/(dashboard)/parent/lesson-plans/page.tsx` — 课案列表(按孩子 published) - `app/(dashboard)/parent/lesson-plans/[planId]/view/page.tsx` — 只读画布 - `app/(dashboard)/admin/lesson-plans/page.tsx` — 全校课案列表 + 统计 - `app/(dashboard)/admin/lesson-plans/[planId]/view/page.tsx` — 查看任意课案 **导航入口**(`shared/lib/navigation.ts` 或 `modules/layout/config/navigation.ts`): - student 导航添加"我的课案" - parent 导航添加"孩子课案" - admin 导航添加"课案管理" **只读画布组件**: - 新建 `components/lesson-plan-readonly-view.tsx` - 复用 React Flow,配置只读模式 - 复用 `toRfNodes` / `toRfEdges` 渲染 ### 阶段 2:发布机制(P0-1) **data-access 新增**(`data-access.ts`): ```typescript export async function publishLessonPlan(planId: string, userId: string): Promise export async function unpublishLessonPlan(planId: string, userId: string): Promise ``` **actions 新增**(`actions.ts`): ```typescript export async function publishLessonPlanAction(planId: string): Promise> export async function unpublishLessonPlanAction(planId: string): Promise> ``` **UI 修改**: - `LessonPlanEditor` 工具栏添加"发布"/"撤回发布"按钮 - `LessonPlanCard` 添加"发布"/"撤回"操作菜单项 - 发布时显示确认对话框 ### 阶段 3:锚点交互完善(P1-1、P1-2、P1-3、P1-4) **P1-1 完善 AnchorNodeSelector**: - 接收 `doc.nodes` 列表(过滤掉 textbook_content) - 渲染节点下拉选择器(显示节点标题 + 颜色标识) - 实现"锚定到新节点":弹出节点类型选择 → 创建节点 → 自动锚定 **P1-2 修复锚点偏移计算**: - `handleMouseUp` 改用 `Range.toString()` 获取选中文本 - 在 `markdownToPlainText(content)` 中搜索定位 start/end 偏移 - 支持跨段落、跨行内元素的选择 **P1-3 修复 fromRfEdges anchorId 丢失**: - `toRfEdges` 时将 `anchorId` 存入 `edge.data.anchorId` - `fromRfEdges` 从 `e.data?.anchorId` 读取 **P1-4 修复锚点边颜色**: - `rf-mappers.ts` 导入 `getNodeColor` - `toRfEdges` 中 `stroke: getNodeColor(anchor.nodeId)` ### 阶段 4:模板与体验(P1-6 + P2) **P1-6 TemplatePicker 显示个人模板**: - 初始化时调用 `getLessonPlanTemplatesAction()` - 合并系统模板和个人模板展示 - 个人模板标记"个人"徽章 **P2-1 修复正文节点侧边面板**: - `NodeEditPanel` 选中 textbook_content 时显示锚点列表 - 或显示提示"请在画布上直接操作正文,点击正文选中文本可创建锚点" **P2-5 优化默认骨架节点位置**: - 增大列间距,左列 x=80,右列 x=900 - 正文节点居中 (500, 250) ## 六、数据模型变更 无数据模型变更。发布机制仅修改 `lessonPlans.status` 字段(已存在)。 ## 七、i18n 键新增 ### zh-CN / en grades 命名空间(lesson-preparation.json) - `action.publish` / `action.unpublish` - `action.publishConfirm` / `action.unpublishConfirm` - `action.publishSuccess` / `action.unpublishSuccess` - `status.published` / `status.draft` / `status.archived` - `readonly.title` / `readonly.hint` - `admin.title` / `admin.stats` / `admin.totalPlans` / `admin.publishedPlans` - `gradeHead.title` - `anchor.selectNode` / `anchor.createNode` / `anchor.nodeTypePrompt` ## 八、架构文档同步 - `004_architecture_impact_map.md`:补充路由、权限、新组件 - `005_architecture_data.json`:补充 routes、permissions、exports ## 九、验证标准 - `npx tsc --noEmit` 零错误 - `npm run lint` 零错误 - 教师可发布课案,学生/家长可查看 published 课案 - 教研组长可查看本年级教师备课 - 锚点交互完整可用(选中文本 → 选择节点 → 创建锚点) - 锚点边颜色与关联节点一致