> ⚠️ **已归档文档**
> 本文档记录的是 2025-12-23 教师仪表盘的实现细节（含 Hydration 修复）。
> 当前实现已演进，最新架构与组件清单详见 [004 架构影响地图](../architecture/004_architecture_impact_map.md) 的 dashboard 模块章节。
> 保留用于历史参考，不再维护。

---

# 教师仪表盘实现与 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):**

*修改前 (有风险):*
```tsx
<p className="text-sm font-medium leading-none">
  {item.studentName}
</p>
```

*修改后 (安全):*
```tsx
<div className="text-sm font-medium leading-none">
  {item.studentName}
</div>
```

**受影响的组件:**
1.  `recent-submissions.tsx`
2.  `teacher-stats.tsx`
3.  `teacher-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.tsx`
- `src/app/(dashboard)/teacher/classes/students/page.tsx`
- `src/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 数据库表与迁移

新增班级领域表：
- `classes`
- `class_enrollments`
- `class_schedule`

对应 Drizzle Schema：
- `src/shared/db/schema.ts`
- `src/shared/db/relations.ts`

对应迁移文件：
- `drizzle/0003_petite_newton_destine.sql`

外键关系（核心）：
- `classes.teacher_id` -> `users.id`
- `class_enrollments.class_id` -> `classes.id`
- `class_enrollments.student_id` -> `users.id`
- `class_schedule.class_id` -> `classes.id`

索引（核心）：
- `classes_teacher_idx`, `classes_grade_idx`
- `class_enrollments_class_idx`, `class_enrollments_student_idx`
- `class_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 入口。

---

## 9. 教师加入班级学科分配逻辑修复 (2026-03-03)

**日期**: 2026-03-03  
**范围**: 教师通过邀请码加入班级（含学科选择）的校验与分配

### 9.1 行为调整

- 教师已在班级中但选择学科加入时，不再直接返回成功，继续执行学科占用校验。
- 班级未创建该学科映射时，先补齐映射再分配，避免误报“该班级不提供该学科”。
- 学科已被其他老师占用时，返回明确提示。

### 9.2 影响代码

- [data-access.ts](file:///e:/Desktop/CICD/src/modules/classes/data-access.ts)
