/** * Admin Lesson Plans 数据变换工具(ARCHITECTURE.md §11.3 DoD - 纯函数单测) * * 所有格式化/映射函数均为纯函数,便于 vitest 单测。 * 关联:ARCHITECTURE.md §11.3 DoD "数据变换/权限判断等纯函数有 vitest 单测" */ import type { AdminLessonPlanListItem } from "@/lib/api/admin-p5"; /** 教案状态中文标签映射 */ export const LESSON_PLAN_STATUS_LABEL: Record = { DRAFT: "草稿", PUBLISHED: "已发布", ARCHIVED: "已归档", SUBMITTED: "已提交", }; /** * 将教案状态枚举值映射为中文标签。 * 未知状态回退为原始值。 */ export function formatLessonPlanStatus(status: string): string { return LESSON_PLAN_STATUS_LABEL[status] ?? status; } /** * 格式化 ISO 日期字符串为本地化展示(zh-CN,含年月日时分)。 * 输入无效(null/undefined/空/非法)时返回占位符 "--"。 */ export function formatLessonPlanDate( isoDate: string | null | undefined, ): string { if (!isoDate) return "--"; const d = new Date(isoDate); if (Number.isNaN(d.getTime())) return "--"; return d.toLocaleString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", }); } /** * 判断教案是否可编辑(草稿与已发布状态允许编辑,归档后不可编辑)。 */ export function isLessonPlanEditable(status: string): boolean { return status === "DRAFT" || status === "PUBLISHED"; } /** * 判断教案是否已发布(PUBLISHED)。 */ export function isLessonPlanPublished(status: string): boolean { return status === "PUBLISHED"; } /** * 判断教案是否已归档(ARCHIVED)。 */ export function isLessonPlanArchived(status: string): boolean { return status === "ARCHIVED"; } /** * 从教案详情中提取列表项视图模型(裁剪字段)。 * * 用于详情→列表裁剪场景;详情字段(textbookId/chapterId/content 等)被剥离。 */ export function toAdminLessonPlanListItem(detail: { id: string; title: string; subjectId: string; subjectName: string; teacherId: string; teacherName: string; classId: string; className: string; status: string; createdAt: string; updatedAt: string; }): AdminLessonPlanListItem { return { id: detail.id, title: detail.title, subjectId: detail.subjectId, subjectName: detail.subjectName, teacherId: detail.teacherId, teacherName: detail.teacherName, classId: detail.classId, className: detail.className, status: detail.status, createdAt: detail.createdAt, updatedAt: detail.updatedAt, }; } /** * 格式化数量为展示字符串。 * 输入无效(null/undefined/NaN/负数)返回 "0"。 */ export function formatCount(count: number | null | undefined): string { if (count === null || count === undefined) return "0"; if (!Number.isFinite(count) || count < 0) return "0"; return `${count}`; } /** * 根据教案状态返回 Tailwind 徽章语义类名。 */ export function lessonPlanStatusToBadgeClass(status: string): string { switch (status) { case "DRAFT": return "bg-muted text-muted-foreground"; case "PUBLISHED": return "bg-primary/10 text-primary"; case "ARCHIVED": return "bg-muted text-muted-foreground"; case "SUBMITTED": return "bg-amber-500/10 text-amber-600 dark:text-amber-400"; default: return "bg-muted text-muted-foreground"; } }