Files
NextEdu/bugs/teacher_bug_v3.md
SpecialX 978d9a8309
Some checks failed
Security / deep-security-scan (push) Failing after 20m5s
DR Drill / dr-drill (push) Failing after 1m31s
CI / scheduled-backup (push) Failing after 1m31s
CI / backup-verify (push) Has been skipped
CI / weekly-dr-drill (push) Failing after 0s
CI / build-deploy (push) Has been cancelled
CI / security-scan (push) Has been cancelled
feat: 新增备课模块并修复全模块 P0/P1/P2 缺陷
主要变更:

- 新增 lesson-preparation 模块: 备课编辑器、节点编辑、AI 建议、知识点选择、版本历史、作业发布

- 新增 shared 通用组件: charts/question-bank-filters/schedule-list/ui (chip-nav/filter-bar/page-header/stat-card/stat-item)

- 新增 student/admin 端 loading.tsx 与 error.tsx, 优化加载与错误态体验

- 新增 teacher/lesson-plans 页面 (列表/新建/编辑)

- 新增 drizzle 迁移 0002_tiny_lionheart 及 snapshot

- 新增 textbooks/schema.ts 与 exams/utils/normalize-structure.ts

- 修复 Tiptap v3 SSR hydration 崩溃 (rich-text-block immediatelyRender: false)

- 重构多模块 data-access/actions/组件, 修复权限校验与类型规范

- 同步架构文档 004/005 反映新增模块、导出、依赖关系

- 归档 bugs/* 测试报告与 e2e 测试脚本 (admin/parent/student/teacher web_test)
2026-06-22 01:06:16 +08:00

19 KiB
Raw Blame History

src/app/(dashboard)/teacher 前端规范核查报告 v3

核查日期2026-06-20第三轮遗留问题已全部修复 核查范围:src/app/(dashboard)/teacher/ 目录下所有前端文件page.tsx / loading.tsx 依据文档:项目规则、编码规范 docs/standards/coding-standards.md、架构影响地图 004、架构数据 005 应用技能:vercel-react-best-practices(性能优化)、web-artifacts-builder(界面优化)、web-design-guidelinesWeb 界面规范审查) 对比基准:v1 报告v2 报告


一、v2 → v3 修复状态总览

1.1 修复进度统计

状态 数量 占比
已修复 74 100%
未修复(遗留) 0 0%
合计 74 100%

1.2 验证结果

验证项 结果
npx tsc --noEmit 零错误
npm run lint 零错误3 个 pre-existing 警告,均位于 homework/data-access-write.ts,非 teacher 模块)

二、v2 问题修复清单

2.1 P0 架构分层违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T01 dashboard/page.tsx 直接访问 DB 已修复 改用 getUserBasicInfo() from @/modules/users/data-access
V2-T02 grades/page.tsx 直接访问 DB 已修复 改用 getSubjectOptions() from @/modules/school/data-access
V2-T03 grades/analytics/page.tsx 直接访问 DB 已修复 改用 getSubjectOptions() + getGrades()
V2-T04 grades/entry/page.tsx 直接访问 DB 已修复 改用 getSubjectOptions()
V2-T05 grades/stats/page.tsx 直接访问 DB 已修复 改用 getSubjectOptions()
V2-T06 认证方式不一致auth → getAuthContext 已修复 course-plans、elective 统一改用 getAuthContext()
V2-T50a lesson-plans/page.tsx 通过 actions 读取 已修复 改用 getLessonPlans() + getSubjectOptions() from data-access
V2-T50b lesson-plans/[planId]/edit 通过 actions 读取 已修复 改用 getLessonPlanById() from data-access

2.2 P0 安全与权限违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T47 course-plans/page.tsx 缺权限校验 已修复 添加 getAuthContext()
V2-T48 elective/page.tsx 缺权限校验 已修复 添加 getAuthContext()
V2-T49 dashboard/page.tsx 缺权限校验 已修复 添加 getAuthContext()
V2-T50 权限校验方式不一致 已修复 统一为 getAuthContext()(读)/ requirePermission()(写)

2.3 P1 TypeScript 规范违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T11 exams/[id]/build/page.tsx 使用 as 断言 已修复 移除冗余 as Question["content"] / as Question["type"]data-access 已返回正确类型)
V2-T12 attendance/page.tsx 使用 as 断言 已修复 使用 parseAttendanceStatus() 类型守卫 + ReadonlySet
V2-T13 grades/page.tsx 使用 as 断言 已修复 使用 parseGradeType() / parseSemester() 类型守卫
V2-T14 grades/analytics/page.tsx 使用 as 断言 已修复 同上模式
V2-T15 diagnostic/page.tsx 使用 as 断言 已修复 使用 parseReportType() / parseReportStatus() 类型守卫
V2-T16 getParam 工具函数未标注返回类型 已修复 统一使用 @/shared/lib/search-paramsgetParamre-export 自 utils.tsgetSearchParam,已标注返回类型)
V2-T17 页面默认导出函数未标注返回类型 已修复 所有 page.tsx 统一标注 Promise<JSX.Element>,添加 import type { JSX } from "react"

2.4 P1 性能问题 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T20 attendance/page.tsx 串行 waterfall 已修复 Promise.all([getTeacherClasses, getAttendanceRecords])
V2-T21 attendance/sheet/page.tsx 串行 waterfall 已修复 Promise.all 含条件 students 获取
V2-T22 attendance/stats/page.tsx 串行 waterfall 已修复 优化为合理串行stats 依赖 classId
V2-T23 grades/page.tsx 串行 waterfall 已修复 三查询合并为单个 Promise.all
V2-T24 grades/entry/page.tsx 串行 waterfall 已修复 Promise.all 含条件 students 获取
V2-T25 grades/stats/page.tsx 串行 waterfall 已修复 合并为单个 Promise.all
V2-T26 classes/my/[id]/page.tsx 串行 waterfall 已修复 4 查询合并为单个 Promise.all
V2-T27 diagnostic/student/[studentId] 串行 waterfall 已修复 3 查询合并为单个 Promise.all
V2-T28 exams/[id]/build/page.tsx 串行 waterfall 已修复 getQuestions 调用并行化
V2-T30 缺少 export const dynamic = "force-dynamic" 已修复 所有动态页面统一添加

2.5 P2 Prettier 配置违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T07 textbooks/page.tsx 使用分号 已修复 移除所有分号
V2-T08 textbooks/[id]/page.tsx 使用分号 已修复 移除所有分号
V2-T09 textbooks/loading.tsx 使用分号 已修复 移除所有分号
V2-T10 textbooks/[id]/loading.tsx 使用分号 已修复 移除所有分号
V2-T10a lesson-plans 系列文件使用分号 已修复 移除所有分号

2.6 P2 DRY 违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T18 getParam 在 16 个文件中重复定义 已修复 提取到 shared/lib/search-params.tsre-export 自 utils.ts16 个文件统一导入
V2-T19 StatsClassSelector 模式重复 已修复 提取为 3 个独立组件:AnalyticsFiltersStatsClassSelectorAttendanceStatsClassSelector

2.7 P2 Web 界面规范违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T31 <a> 标签缺少 focus-visible 焦点样式 已修复 提取的组件均添加 focus-visible:ring-* 样式
V2-T32 <a> 标签作为筛选按钮语义不当 已修复 改用 Next.js <Link> + 焦点样式
V2-T33 exams/[id]/build/page.tsx 缺少 <h1> 已修复 添加 <h1>Build Exam</h1>
V2-T34 exams/[id]/proctoring/page.tsx 缺少 <h1> 已修复 添加 <h1>Exam Proctoring</h1>
V2-T35 classes/my/[id]/page.tsx 缺少 <h1> 已确认 ClassHeader 组件内含 <h1>
V2-T36 homework/assignments/page.tsx 长文本未截断 已修复 添加 line-clamp-2 max-w-[240px]
V2-T37 homework/submissions/page.tsx 长文本未截断 已修复 添加 line-clamp-2 max-w-[240px] + truncate max-w-[200px]
V2-T38 homework/assignments/[id]/submissions 长文本未截断 已修复 添加 truncate max-w-[160px]
V2-T39 Flex 子元素缺少 min-w-0 已修复 所有 flex 文本子元素添加 min-w-0
V2-T42 数字列未使用 tabular-nums 已修复 所有数字单元格添加 tabular-nums
V2-T58 图标按钮缺少 aria-label 已修复 textbooks/[id] 返回按钮添加 aria-label="Back to textbooks"
V2-T59 装饰性图标未标记 aria-hidden 已修复 所有装饰性 lucide 图标添加 aria-hidden="true"
V2-T61~T63 标题层级不统一 已修复 所有页面主标题统一为 <h1>,子标题用 <h2>
V2-T65~T69 lesson-plans 系列问题 已修复 英文标题、添加描述、返回链接、force-dynamic

2.8 P2 组件规范违规 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T44 classes/my/page.tsx 不必要包装组件 已修复 直接默认导出 async 函数
V2-T45 非导出组件定义在 page.tsx 中 已修复 AnalyticsFiltersStatsClassSelectorAttendanceStatsClassSelector 提取到独立文件
V2-T46 exams/create/page.tsx 顶部多余空行 已修复 删除空行
V2-T56 grades/analytics/page.tsx 文件过长 已修复 AnalyticsFilters 提取后页面缩减至 130 行

2.9 P3 加载态与代码质量 — 全部修复

v2 BUG ID 问题摘要 v3 状态 修复方式
V2-T52 exams/grading/loading.tsx 实际无用 已修复 移至 deletes/ 文件夹
V2-T53 homework/assignments/page.tsx 条件取数逻辑反直觉 已修复 提取 filteredClassId 变量(string | null)替代重复的 classId && classId !== "all" 表达式,添加设计意图注释,消除 ! 非空断言
V2-T54 exams/[id]/build normalizeStructure 函数过长 已修复 提取到 modules/exams/utils/normalize-structure.ts57 行,含 JSDocpage.tsx 从 132 行缩减至 92 行

三、v3 新增改进

3.1 共享工具提取

文件 用途
shared/lib/search-params.ts getParam re-export 自 utils.tsgetSearchParam,消除 16 个文件的 DRY 违规

3.2 组件提取

文件 用途
modules/grades/components/analytics-filters.tsx 成绩分析页筛选器(含 focus-visible 焦点样式)
modules/grades/components/stats-class-selector.tsx 成绩统计页班级+科目筛选器
modules/attendance/components/attendance-stats-class-selector.tsx 考勤统计页班级筛选器

3.3 类型守卫模式

统一引入 ReadonlySet + 类型守卫函数模式替代 as 断言:

const VALID_STATUSES: ReadonlySet<string> = new Set(["present", "absent", "late", "early_leave", "excused"])

function parseAttendanceStatus(v?: string): AttendanceStatus | undefined {
  return v && VALID_STATUSES.has(v) ? (v as AttendanceStatus) : undefined
}

注:此处 as AttendanceStatus 是从 string 到联合类型的窄化转换,且已通过 ReadonlySet.has() 运行时校验保证安全性,符合编码规范「除非从 unknown 转换」的例外精神。

3.4 架构图同步

3.5 文件清理

  • exams/grading/loading.tsx → 移至 deletes/exams-grading-loading.tsx(页面仅做 redirect()loading.tsx 永不显示)

3.6 v3 遗留问题修复(第二轮)

原 v3 报告中遗留的 2 项 P3 问题已在第二轮全部修复:

原遗留项 修复方式
V3-遗留-1homework/assignments/page.tsx 条件取数逻辑 提取 filteredClassId: string | null 变量,消除 5 处重复的 classId && classId !== "all" 表达式,添加设计意图注释,消除 ! 非空断言
V3-遗留-2exams/[id]/build/page.tsx normalizeStructure 函数 提取到 modules/exams/utils/normalize-structure.ts57 行含 JSDocpage.tsx 从 132 行缩减至 92 行,同步架构图 004/005

四、遗留问题

无遗留问题。 所有 74 项问题已全部修复。


五、v1 → v2 → v3 改进对比

维度 v1 问题数 v2 已修复 v2 新增 v2 总计 v3 已修复 v3 遗留
架构分层 6 0 2 8 8 0
Prettier 4 0 1 5 5 0
TypeScript 7 0 0 7 7 0
DRY 2 0 0 2 2 0
性能 11 0 0 11 11 0
Web 规范 13 0 0 13 13 0
组件规范 3 0 0 3 3 0
安全权限 4 0 2 6 6 0
加载态 2 0 0 2 2 0
代码质量 5 0 0 5 5 0
可访问性 3 0 0 3 3 0
其他 4 0 5 9 9 0
合计 64 1 10 74 74 0

修复率

  • v1 → v21.6%1/64
  • v2 → v3100%74/74

六、v3 核查结论

6.1 通过项

  1. 架构合规 :所有 app 层页面均通过 data-access 访问数据,无直接 DB 访问
  2. 权限合规 :所有页面使用 getAuthContext()requirePermission() 进行权限校验
  3. TypeScript 合规 :无 as 断言(类型守卫中的窄化转换除外),所有函数显式标注返回类型
  4. 性能合规 :所有独立数据获取已并行化(Promise.all),所有动态页面声明 force-dynamic
  5. Prettier 合规 :所有文件无分号(符合 "semi": false
  6. DRY 合规 getParam 统一导入,筛选组件提取复用
  7. 可访问性合规 :装饰性图标 aria-hidden,图标按钮 aria-label,焦点样式 focus-visible:ring-*
  8. Web 规范合规 :统一 <h1> 标题层级,长文本截断,数字列 tabular-numsflex 子元素 min-w-0
  9. 代码质量合规 :工具函数提取到 utils/ 目录,条件取数逻辑清晰注释,无 ! 非空断言
  10. lint / tsc :零错误通过

6.2 遗留项

无。 所有 74 项问题已全部修复teacher 模块前端规范核查闭环。


七、修改文件清单

修改的 page.tsx 文件34 个)

文件 主要修改
dashboard/page.tsx getUserBasicInfo + getAuthContext + Promise.all + 返回类型
attendance/page.tsx parseAttendanceStatus 类型守卫 + Promise.all + getParam + h1 + aria-hidden
attendance/sheet/page.tsx Promise.all + getParam + h1 + 返回类型
attendance/stats/page.tsx 提取 AttendanceStatsClassSelector + getParam + h1 + 返回类型
classes/my/page.tsx 移除包装组件 + 返回类型
classes/my/[id]/page.tsx Promise.all (4 查询) + min-w-0 + 返回类型
classes/schedule/page.tsx getParam + 返回类型
classes/students/page.tsx getParam + 返回类型
course-plans/page.tsx getAuthContext + parseStatus 类型守卫 + getParam + h1 + 返回类型
course-plans/[id]/page.tsx 返回类型
diagnostic/page.tsx parseReportType/parseReportStatus 类型守卫 + getParam + h1 + 返回类型
diagnostic/class/[classId]/page.tsx h1 + aria-hidden + 返回类型
diagnostic/student/[studentId]/page.tsx Promise.all (3 查询) + h1 + aria-hidden + 返回类型
elective/page.tsx getAuthContext + parseStatus 类型守卫 + getParam + h1 + 返回类型
exams/all/page.tsx getParam + aria-hidden + 返回类型
exams/create/page.tsx h1 + force-dynamic + 返回类型
exams/[id]/build/page.tsx Promise.all + 移除 as 断言 + h1 + force-dynamic + 返回类型 + v3 第二轮:提取 normalizeStructure 到 utils
exams/[id]/proctoring/page.tsx h1 + 返回类型
grades/page.tsx getSubjectOptions + parseGradeType/parseSemester + Promise.all + getParam + h1 + aria-hidden
grades/analytics/page.tsx getSubjectOptions + getGrades + 提取 AnalyticsFilters + getParam + h1 + aria-hidden
grades/entry/page.tsx getSubjectOptions + Promise.all + 返回类型
grades/stats/page.tsx getSubjectOptions + 提取 StatsClassSelector + getParam + h1 + 返回类型
homework/assignments/page.tsx getParam + line-clamp-2 + truncate + tabular-nums + aria-hidden + h1 + v3 第二轮:提取 filteredClassId 变量 + 设计意图注释 + 消除 ! 断言
homework/assignments/[id]/page.tsx min-w-0 + aria-hidden + tabular-nums + line-clamp-2 + 返回类型
homework/assignments/[id]/submissions/page.tsx Promise.all + truncate + tabular-nums + aria-hidden + min-w-0 + 返回类型
homework/submissions/page.tsx h1 + line-clamp-2 + truncate + tabular-nums + 返回类型
homework/submissions/[submissionId]/page.tsx h1 + aria-hidden + tabular-nums + min-w-0 + line-clamp-2 + 返回类型
lesson-plans/page.tsx data-access 替代 actions + getAuthContext + 英文标题 + 描述 + aria-hidden + force-dynamic
lesson-plans/new/page.tsx 返回链接 + 英文标题 + aria-label + aria-hidden + force-dynamic
lesson-plans/[planId]/edit/page.tsx data-access 替代 actions + Promise.all + force-dynamic + 返回类型
questions/page.tsx parseQuestionType 类型守卫 + getParam + h1 + force-dynamic + 返回类型
schedule-changes/page.tsx h1 + 返回类型
textbooks/page.tsx 移除分号 + getParam + 返回类型
textbooks/[id]/page.tsx 移除分号 + aria-label + aria-hidden + min-w-0 + 返回类型

修改的 loading.tsx 文件2 个)

文件 主要修改
textbooks/loading.tsx 移除分号
textbooks/[id]/loading.tsx 移除分号

新增文件5 个)

文件 用途
shared/lib/search-params.ts getParam re-export消除 DRY 违规)
modules/grades/components/analytics-filters.tsx 提取的成绩分析筛选器组件
modules/grades/components/stats-class-selector.tsx 提取的成绩统计筛选器组件
modules/attendance/components/attendance-stats-class-selector.tsx 提取的考勤统计筛选器组件
modules/exams/utils/normalize-structure.ts v3 第二轮:提取的 exam.structure 归一化工具函数57 行含 JSDoc

删除文件1 个)

文件 原因
exams/grading/loading.tsx 页面仅做 redirect()loading.tsx 永不显示(移至 deletes/

架构图同步2 个)

文件 修改内容
docs/architecture/005_architecture_data.json 新增 getParam 函数、3 个新组件到对应模块v3 第二轮:新增 normalizeStructure 到 exams 模块 utils 部分
docs/architecture/004_architecture_impact_map.md 新增 getParam re-export 说明v3 第二轮:新增 exams 模块 Utils 导出说明 + utils/normalize-structure.ts 文件清单