165 lines
7.6 KiB
Markdown
165 lines
7.6 KiB
Markdown
# 学校基础数据模块(School)实现文档与更新记录
|
||
|
||
**日期**: 2026-01-07
|
||
**作者**: Frontend Team
|
||
**状态**: 已实现
|
||
|
||
## 1. 范围
|
||
|
||
本文档覆盖管理端「School」域的基础数据维护页面(Schools / Departments / Academic Year / Grades),并记录相关实现约束与关键更新,遵循 [003_frontend_engineering_standards.md](file:///c:/Users/xiner/Desktop/CICD/docs/architecture/003_frontend_engineering_standards.md) 的工程规范(Vertical Slice、Server/Client 边界、质量门禁)。
|
||
|
||
## 2. 路由入口(Admin)
|
||
|
||
School 域路由位于 `src/app/(dashboard)/admin/school/*`,均显式声明 `export const dynamic = "force-dynamic"` 以避免构建期预渲染触发数据库访问。
|
||
|
||
- `/admin/school`:入口重定向到 Classes(当前落点不在 `school` 模块内)
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/page.tsx)
|
||
- `/admin/school/schools`:学校维护(增删改)
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/schools/page.tsx)
|
||
- `/admin/school/departments`:部门维护(增删改)
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/departments/page.tsx)
|
||
- `/admin/school/academic-year`:学年维护(增删改 + 设为当前学年)
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/academic-year/page.tsx)
|
||
- `/admin/school/grades`:年级维护(增删改 + 指派年级组长/教研组长)
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/grades/page.tsx)
|
||
- `/admin/school/grades/insights`:年级维度作业统计(跨班级聚合)
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/grades/insights/page.tsx)
|
||
|
||
## 3. 模块结构(Vertical Slice)
|
||
|
||
School 模块位于 `src/modules/school`:
|
||
|
||
```
|
||
src/modules/school/
|
||
├── components/
|
||
│ ├── schools-view.tsx
|
||
│ ├── departments-view.tsx
|
||
│ ├── academic-year-view.tsx
|
||
│ └── grades-view.tsx
|
||
├── actions.ts
|
||
├── data-access.ts
|
||
├── schema.ts
|
||
└── types.ts
|
||
```
|
||
|
||
边界约束:
|
||
- `data-access.ts` 包含 `import "server-only"`,仅用于服务端查询与 DTO 组装。
|
||
- `actions.ts` 包含 `"use server"`,写操作统一通过 Server Actions 并 `revalidatePath`。
|
||
- `components/*` 为 Client 交互层(表单、Dialog、筛选、行级操作),调用 Server Actions 并用 `sonner` toast 反馈。
|
||
|
||
## 4. 数据访问(data-access.ts)
|
||
|
||
实现:[data-access.ts](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/data-access.ts)
|
||
|
||
- `getSchools(): Promise<SchoolListItem[]>`
|
||
- `getDepartments(): Promise<DepartmentListItem[]>`
|
||
- `getAcademicYears(): Promise<AcademicYearListItem[]>`
|
||
- `getGrades(): Promise<GradeListItem[]>`
|
||
- join `schools` 获取 `school.name`
|
||
- 收集 `gradeHeadId/teachingHeadId` 并批量查询 `users` 以组装 `StaffOption`
|
||
- `getStaffOptions(): Promise<StaffOption[]>`
|
||
- 角色过滤 `teacher/admin`
|
||
- 排序 `name/email`,用于 Select 列表可用性
|
||
- `getGradesForStaff(staffId: string): Promise<GradeListItem[]>`
|
||
- 用于按负责人(年级组长/教研组长)反查关联年级
|
||
|
||
返回 DTO 类型定义位于:[types.ts](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/types.ts)
|
||
|
||
## 5. 写操作(actions.ts)
|
||
|
||
实现:[actions.ts](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/actions.ts)
|
||
|
||
通用约束:
|
||
- 输入校验:统一使用 [schema.ts](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/schema.ts) 的 Zod schema
|
||
- 返回结构:统一使用 [ActionState](file:///c:/Users/xiner/Desktop/CICD/src/shared/types/action-state.ts)
|
||
- 刷新策略:对目标页面路径执行 `revalidatePath`
|
||
|
||
Departments:
|
||
- `createDepartmentAction(formData)`
|
||
- `updateDepartmentAction(departmentId, formData)`
|
||
- `deleteDepartmentAction(departmentId)`
|
||
|
||
Academic Year:
|
||
- `createAcademicYearAction(formData)`
|
||
- `updateAcademicYearAction(academicYearId, formData)`
|
||
- `deleteAcademicYearAction(academicYearId)`
|
||
- 当 `isActive=true` 时,通过事务把其它学年置为非激活,保证唯一激活学年
|
||
|
||
Schools:
|
||
- `createSchoolAction(formData)`
|
||
- `updateSchoolAction(schoolId, formData)`
|
||
- `deleteSchoolAction(schoolId)`
|
||
- 删除后会同时刷新 `/admin/school/schools` 与 `/admin/school/grades`
|
||
|
||
Grades:
|
||
- `createGradeAction(formData)`
|
||
- `updateGradeAction(gradeId, formData)`
|
||
- `deleteGradeAction(gradeId)`
|
||
|
||
## 6. UI 组件(components/*)
|
||
|
||
Schools:
|
||
- 实现:[schools-view.tsx](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/components/schools-view.tsx)
|
||
- 交互:列表 + Dialog 表单(新增/编辑)+ 删除确认(AlertDialog)
|
||
|
||
Departments:
|
||
- 实现:[departments-view.tsx](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/components/departments-view.tsx)
|
||
- 交互:列表 + Dialog 表单(新增/编辑)+ 删除确认(AlertDialog)
|
||
|
||
Academic Year:
|
||
- 实现:[academic-year-view.tsx](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/components/academic-year-view.tsx)
|
||
- 交互:列表 + Dialog 表单(新增/编辑)+ 删除确认(AlertDialog)+ 设为当前学年(isActive)
|
||
|
||
Grades:
|
||
- 实现:[grades-view.tsx](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/components/grades-view.tsx)
|
||
- 交互:列表展示 + URL 驱动筛选(搜索/学校/负责人/排序)+ Dialog 表单(新增/编辑)+ 删除确认(AlertDialog)
|
||
- 负责人指派:
|
||
- 年级组长(gradeHeadId)
|
||
- 教研组长(teachingHeadId)
|
||
|
||
## 7. 关键交互与规则(Grades)
|
||
|
||
页面入口(RSC 组装)在服务端并发拉取三类数据:
|
||
- 年级列表:`getGrades()`
|
||
- 学校选项:`getSchools()`
|
||
- 负责人候选:`getStaffOptions()`
|
||
|
||
实现:[page.tsx](file:///c:/Users/xiner/Desktop/CICD/src/app/(dashboard)/admin/school/grades/page.tsx)
|
||
|
||
### 7.1 URL State(筛选/排序)
|
||
|
||
Grades 列表页的筛选状态 URL 化(`nuqs`):
|
||
- `q`:关键字(匹配 grade/school)
|
||
- `school`:学校过滤(`all` 或具体 schoolId)
|
||
- `head`:负责人过滤(全部 / 两者缺失 / 缺年级组长 / 缺教研组长)
|
||
- `sort`:排序(默认/名称/更新时间等)
|
||
|
||
实现:[grades-view.tsx](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/components/grades-view.tsx)
|
||
|
||
### 7.2 表单校验
|
||
|
||
Grades 的新增/编辑表单在客户端做轻量校验:
|
||
- 必填:`schoolId`、`name`
|
||
- `order`:非负整数
|
||
- 去重:同一学校下年级名称不允许重复(忽略大小写 + 规范化空格)
|
||
|
||
说明:
|
||
- 服务端写入前仍会经过 `UpsertGradeSchema` 校验(schema.ts),避免仅依赖客户端校验。
|
||
|
||
### 7.3 负责人选择(Radix Select)
|
||
|
||
Radix Select 约束:`SelectItem` 的 `value` 不能为 `""`(空字符串),否则会触发运行时错误。
|
||
|
||
当前实现策略:
|
||
- UI 中 “未设置” 选项使用占位值 `__none__`
|
||
- 在 `onValueChange` 中将 `__none__` 映射回 `""` 存入本地表单 state
|
||
- 提交时依旧传递空字符串,由 `UpsertGradeSchema` 将其归一为 `null`
|
||
|
||
实现:[grades-view.tsx](file:///c:/Users/xiner/Desktop/CICD/src/modules/school/components/grades-view.tsx)
|
||
|
||
## 2. 更新记录(2026-01-07)
|
||
|
||
- 修复 Add Grades 弹窗报错:将 4 处 `<SelectItem value=\"\">` 替换为占位值 `__none__`,并在 `onValueChange` 中映射回 `\"\"`,保持“可清空选择/显示 placeholder”的行为不变。
|
||
- 修复新建年级按钮不可用:创建/编辑表单在状态变化时触发实时校验更新,避免校验状态滞后导致提交被禁用。
|
||
- 质量门禁:本地通过 `npm run lint` 与 `npm run typecheck`。
|