10 KiB
教师仪表盘实现与 Hydration 修复记录
日期: 2025-12-23 作者: 资深前端工程师 (Senior Frontend Engineer) 状态: 已实现
1. 概述
本文档详细说明了教师仪表盘 (Teacher Dashboard) 的实现细节,该实现严格遵循 Next_Edu 设计系统 v1.3.0。文档还记录了开发过程中遇到的 Hydration 错误及其解决方案。
2. 组件架构
仪表盘采用垂直切片架构 (Vertical Slice Architecture),代码位于 src/modules/dashboard。
2.1 文件结构
src/modules/dashboard/
└── components/
├── teacher-stats.tsx # 核心指标 (学生数, 课程数, 待批改作业)
├── teacher-schedule.tsx # 今日日程列表
├── recent-submissions.tsx # 最近的学生提交记录
└── teacher-quick-actions.tsx # 常用操作 (创建作业等)
2.2 设计系统集成
所有组件严格遵循 v1.3.0 规范:
- 排版 (Typography): 使用
Geist Sans,数据展示开启tabular-nums。 - 色彩 (Colors): 使用语义化 HSL 变量 (
muted,primary,destructive)。 - 图标 (Icons): 使用
lucide-react(如Users,BookOpen,Inbox)。 - 状态 (States):
- Loading: 使用自定义骨架屏 (Skeleton),拒绝全屏 Spinner。
- Empty: 使用
EmptyState组件处理无数据场景。
3. 组件详情
3.1 TeacherStats (教师统计)
- 用途: 展示教师当前状态的高层概览。
- 特性:
- 在响应式网格中展示 4 个关键指标。
- 支持
isLoading属性以渲染骨架屏。 - 使用
Card组件作为容器。
3.2 TeacherSchedule (教师日程)
- 用途: 展示今日课程安排。
- 特性:
- 列出课程时间及地点。
- 使用 Badge 区分 "Lecture" (讲座) 和 "Workshop" (研讨会)。
- 空状态: 当无日程时显示 "No Classes Today"。
3.3 RecentSubmissions (最近提交)
- 用途: 追踪最新的学生活动。
- 特性:
- 展示学生头像、姓名、作业名称及时间。
- "Late" (迟交) 状态指示器。
- 空状态: 当列表为空时显示 "No New Submissions"。
3.4 EmptyState Component (空状态组件)
- 位置:
src/shared/components/ui/empty-state.tsx - 规范:
- 虚线边框容器。
- 居中图标 (Muted 背景)。
- 清晰的标题和描述。
- 可选的操作按钮插槽。
4. Hydration 错误修复
4.1 问题描述
开发过程中观察到 "Hydration failed" 错误,原因是 HTML 嵌套无效。具体来说,是 p 标签内包含了块级元素(或 React 在 hydration 检查期间视为块级的元素)。
4.2 根本原因分析
React 的 hydration 过程对 HTML 有效性要求极高。将 div 放入 p 标签中违反了 HTML5 标准,但浏览器通常会自动修正 DOM 结构,导致实际 DOM 与 React 基于虚拟 DOM 预期的结构不一致。
4.3 实施的修复
将所有仪表盘组件中存在风险的 p 标签替换为 div 标签,以确保嵌套结构的健壮性。
示例 (RecentSubmissions):
修改前 (有风险):
<p className="text-sm font-medium leading-none">
{item.studentName}
</p>
修改后 (安全):
<div className="text-sm font-medium leading-none">
{item.studentName}
</div>
受影响的组件:
recent-submissions.tsxteacher-stats.tsxteacher-schedule.tsx
5. 更新记录(2026-01-04)
- 教师仪表盘从 Mock Data 切换为真实数据查询:
/teacher/dashboard组合getTeacherClasses、getClassSchedule、getHomeworkSubmissions({ creatorId })渲染 KPI / 今日课表 / 最近提交。 - Quick Actions 落地为真实路由跳转(创建作业、查看列表等)。
- Schedule / Submissions 增加 “View All” 跳转到对应列表页(并携带筛选参数)。
6. 教师端班级管理模块(真实数据接入记录)
日期: 2025-12-31
范围: 教师端「我的班级 / 学生 / 课表」页面与 MySQL(Drizzle) 真数据对接
6.1 页面入口与路由
班级管理相关页面位于:
src/app/(dashboard)/teacher/classes/my/page.tsxsrc/app/(dashboard)/teacher/classes/students/page.tsxsrc/app/(dashboard)/teacher/classes/schedule/page.tsx
为避免构建期/预渲染阶段访问数据库导致失败,以上页面显式启用动态渲染:
export const dynamic = "force-dynamic"
6.2 模块结构(Vertical Slice)
班级模块采用垂直切片架构,代码位于 src/modules/classes/:
src/modules/classes/
├── components/
│ ├── my-classes-grid.tsx
│ ├── students-filters.tsx
│ ├── students-table.tsx
│ ├── schedule-filters.tsx
│ └── schedule-view.tsx
├── data-access.ts
└── types.ts
其中 data-access.ts 负责班级、学生、课表三类查询的服务端数据读取,并作为页面层唯一的数据入口。
6.3 数据库表与迁移
新增班级领域表:
classesclass_enrollmentsclass_schedule
对应 Drizzle Schema:
src/shared/db/schema.tssrc/shared/db/relations.ts
对应迁移文件:
drizzle/0003_petite_newton_destine.sql
外键关系(核心):
classes.teacher_id->users.idclass_enrollments.class_id->classes.idclass_enrollments.student_id->users.idclass_schedule.class_id->classes.id
索引(核心):
classes_teacher_idx,classes_grade_idxclass_enrollments_class_idx,class_enrollments_student_idxclass_schedule_class_idx,class_schedule_class_day_idx
6.4 Seed 数据
Seed 脚本已覆盖班级相关数据,以便在开发环境快速验证页面渲染与关联关系:
scripts/seed.ts- 运行命令:
npm run db:seed
6.5 开发过程中的问题与处理
- 端口占用(EADDRINUSE):开发服务器端口被占用时,通过更换端口启动规避(例如
next dev -p <port>)。 - Next dev 锁文件:出现
.next/dev/lock无法获取锁时,需要确保只有一个 dev 实例在运行,并清理残留 lock。 - 头像资源 404:移除 Header 中硬编码的本地头像资源引用,避免
public/avatars/...不存在导致的 404 噪音(见src/modules/layout/components/site-header.tsx)。 - 班级人数统计查询失败:
class_enrollments表实际列名为class_enrollment_status,修复查询中引用的列名以恢复教师端班级列表渲染。 - Students 页面 key 冲突:学生列表跨班级汇总时,
<TableRow key={studentId}>会重复,改为使用classId:studentId作为 key。 - Build 预渲染失败(/login):
LoginForm使用useSearchParams()获取回跳地址,需在/login页面用Suspense包裹以避免 CSR bailout 报错。 - 构建警告(middleware):Next.js 16 将文件约定从
middleware.ts改为proxy.ts,已迁移以消除警告。
6.6 班级详情页(聚合视图 + Schedule Builder + Homework 统计)
日期: 2026-01-04
入口: src/app/(dashboard)/teacher/classes/my/[id]/page.tsx
聚合数据在单次 RSC 请求内并发获取:
- 学生:
getClassStudents({ classId }) - 课表:
getClassSchedule({ classId }) - 作业统计:
getClassHomeworkInsights({ classId, limit })(包含 latest、历史列表、overallScores、以及每次作业的 scoreStats:avg/median)
页面呈现:
- 顶部 KPI 卡片:学生数、课表条目数、作业数、整体 avg/median
- Latest homework:目标人数、提交数、批改数、avg/median,直达作业与提交列表
- Students / Schedule 预览:提供 View all 跳转到完整列表页
- Homework history 表格:支持通过 URL query
?hw=all|active|overdue过滤作业记录,并展示每条作业的 avg/median
课表编辑能力复用既有 Builder:
- 组件:
src/modules/classes/components/schedule-view.tsx(新增/编辑/删除课表项) - 数据变更:
src/modules/classes/actions.ts
6.7 班级邀请码(6 位码)加入与管理
日期: 2026-01-08
范围: 为班级新增 6 位邀请码,支持学生通过输入邀请码加入班级;教师可查看与刷新邀请码
7. 班级管理重构与角色分离 (2026-01-14)
日期: 2026-01-14
范围: 班级创建权限收归管理端,教师端仅保留查看与加入
7.1 职责分离 (Role Separation)
- 管理端 (Management):
- 新增
src/app/(dashboard)/management/grade/classes/page.tsx - 供年级组长 (Grade Head) 与管理员创建、编辑、删除班级
- 引入
GradeClassesView组件,支持按年级管理班级
- 新增
- 教师端 (Teacher):
- 移除创建班级入口
- 新增「通过邀请码加入班级」功能 (
JoinClassDialog) MyClassesGrid样式优化,移除硬编码渐变,使用标准bg-card
7.2 数据访问与权限
- 新增
getGradeManagedClasses: 仅返回用户作为 Grade Head 或 Teaching Head 管理的年级下的班级 - Server Actions (
createGradeClassAction等) 增加严格的 RBAC 校验,确保操作者对目标年级有管理权限
8. 课表模块视觉升级与架构优化 (2026-01-15)
日期: 2026-01-15
范围: 课表视图 (Schedule View) 视觉重构、Insights 模块移除
8.1 课表视图重构 (Schedule Optimization)
- 视觉对齐: 重构
ScheduleView(src/modules/classes/components/schedule-view.tsx) 以完全匹配ClassScheduleGrid组件的视觉风格。 - 无边框设计: 移除网格线与外边框,采用更现代的洁净布局。
- 时间轴定位: 废弃 Grid 布局,改用基于时间的绝对定位 (
top,height百分比计算),支持 8:00 - 18:00 时间段。 - 语义化配色: 新增
getSubjectColor工具函数,根据课程名称 (Math, Physics, etc.) 自动映射语义化背景色与边框色。 - 过滤器优化:
ScheduleFilters移除边框与阴影,居中显示当前选中的班级名称 ({Class Name} Schedule),移除冗余的 Reset 按钮。
8.2 架构精简 (Insights Removal)
- 移除 Insights: 经评估,
src/app/(dashboard)/teacher/classes/insights模块功能冗余,已全量移除。 - 保留核心数据: 保留
data-access.ts中的getClassHomeworkInsights函数,继续服务于班级详情页的统计卡片与图表。 - 导航更新: 从
NAV_CONFIG中移除 Insights 入口。