P2-2: 新增 OrgTreeNav 组件(学校→年级→班级三级树形导航,支持搜索过滤/选中高亮/展开折叠) P2-3: 新增 promoteGradesAction 年级升级功能(中文数字/阿拉伯数字识别,按 order 降序避免冲突) P2-4: 新增 bulkEnrollStudentsAction(CSV 批量导入学生)+ bulkAssignSubjectTeachersAction(CSV 批量分配教师) P2-5: 为 department/academicYear/grade 的 9 个 CRUD Action 补充 logAudit 审计日志 同步更新架构图文档 004/005
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { z } from "zod"
|
|
|
|
export const UpsertDepartmentSchema = z.object({
|
|
name: z.string().trim().min(1).max(255),
|
|
description: z.string().trim().max(5000).optional().nullable(),
|
|
})
|
|
|
|
export const UpsertAcademicYearSchema = z
|
|
.object({
|
|
name: z.string().trim().min(1).max(100),
|
|
startDate: z.string().min(1),
|
|
endDate: z.string().min(1),
|
|
isActive: z.union([z.literal("on"), z.literal("true"), z.literal("false"), z.string()]).optional(),
|
|
})
|
|
.transform((v) => ({
|
|
name: v.name,
|
|
startDate: v.startDate,
|
|
endDate: v.endDate,
|
|
isActive: v.isActive === "on" || v.isActive === "true",
|
|
}))
|
|
.refine((v) => new Date(v.startDate).getTime() <= new Date(v.endDate).getTime(), {
|
|
message: "startDate must be before endDate",
|
|
})
|
|
|
|
export const UpsertSchoolSchema = z.object({
|
|
name: z.string().trim().min(1).max(255),
|
|
code: z.string().trim().max(50).optional().nullable(),
|
|
})
|
|
|
|
export const UpsertGradeSchema = z
|
|
.object({
|
|
schoolId: z.string().trim().min(1),
|
|
name: z.string().trim().min(1).max(100),
|
|
order: z.union([z.string(), z.number()]).optional().nullable(),
|
|
gradeHeadId: z.string().trim().optional().nullable(),
|
|
teachingHeadId: z.string().trim().optional().nullable(),
|
|
})
|
|
.transform((v) => ({
|
|
schoolId: v.schoolId,
|
|
name: v.name,
|
|
order:
|
|
typeof v.order === "number"
|
|
? v.order
|
|
: typeof v.order === "string" && v.order.trim().length > 0
|
|
? Number(v.order)
|
|
: 0,
|
|
gradeHeadId: v.gradeHeadId && v.gradeHeadId.length > 0 ? v.gradeHeadId : null,
|
|
teachingHeadId: v.teachingHeadId && v.teachingHeadId.length > 0 ? v.teachingHeadId : null,
|
|
}))
|
|
.refine((v) => Number.isFinite(v.order) && Number.isInteger(v.order) && v.order >= 0, {
|
|
message: "order must be a non-negative integer",
|
|
})
|
|
|
|
export const PromoteGradesSchema = z.object({
|
|
schoolId: z.string().trim().min(1),
|
|
})
|