feat(portal-shell): 学生域全页面迁移与规范合规修复

- 学生域 32 页全量迁移(含作答/自动保存/提交/诊断)

- 补齐 4 个 MSW mock 缺口,修 diagnostic case 名

- 修 4 处 Tailwind 任意值;新增共享组件与路由
This commit is contained in:
SpecialX
2026-08-31 11:25:21 +08:00
parent 039db5efdd
commit 04b7a40bdc
493 changed files with 70985 additions and 2112 deletions

View File

@@ -0,0 +1,112 @@
"use client";
/**
* 教师详情对话框ARCHITECTURE.md §7.3 详情页 / §9.4 / §10 P5
*
* 数据契约:
* - 详情数据复用列表中的 AdminTeacher 对象(@contract-pending单查契约待补齐
*
* 关联ARCHITECTURE.md §5.5 / §7.3 / §9.4 / §10 P5 / §11.3
*/
import { School } from "lucide-react";
import { useTranslations } from "next-intl";
import type { AdminTeacher } from "@/lib/api/admin-p5";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/shared/components/ui/dialog";
import { DetailField, DetailSection } from "@/shared/components/page-templates";
import {
formatTeacherCount,
formatTeacherStatus,
teacherStatusToBadgeClass,
} from "@/features/admin/teachers/transformations";
/**
* 教师详情对话框。受控组件,由父组件管理 open 状态与选中数据。
*/
export function TeacherDetailDialog({
open,
onOpenChange,
teacher,
}: {
open: boolean;
onOpenChange: (open: boolean) => void;
teacher: AdminTeacher | null;
}): React.ReactElement {
const t = useTranslations("admin.teachers.detail");
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<School className="size-5" />
{t("title")}
</DialogTitle>
<DialogDescription>{t("description")}</DialogDescription>
</DialogHeader>
{teacher ? <TeacherDetailBody teacher={teacher} /> : null}
</DialogContent>
</Dialog>
);
}
/**
* 详情内容区(基本信息 + 教学信息)。
*/
function TeacherDetailBody({
teacher,
}: {
teacher: AdminTeacher;
}): React.ReactElement {
const t = useTranslations("admin.teachers.detail");
return (
<div className="space-y-4">
<DetailSection title={t("sectionBasic")}>
<DetailField label={t("fieldName")} value={teacher.name} />
<DetailField label={t("fieldStaffNo")} value={teacher.staffNo} />
<DetailField label={t("fieldEmail")} value={teacher.email} />
<DetailField
label={t("fieldStatus")}
value={<StatusBadge status={teacher.status} />}
/>
</DetailSection>
<DetailSection title={t("sectionTeaching")}>
<DetailField label={t("fieldDepartment")} value={teacher.department} />
<DetailField label={t("fieldTitle")} value={teacher.title} />
<DetailField
label={t("fieldClassCount")}
value={formatTeacherCount(teacher.classCount)}
/>
<DetailField
label={t("fieldSubjectCount")}
value={String(teacher.subjectCount)}
/>
<DetailField label={t("fieldId")} value={teacher.id} />
</DetailSection>
</div>
);
}
/**
* 状态徽章(按状态色阶展示)。
*/
function StatusBadge({ status }: { status: string }): React.ReactElement {
const label = formatTeacherStatus(status);
const cls = teacherStatusToBadgeClass(status);
return (
<span
className={`inline-flex h-6 items-center rounded-full px-2 text-xs font-medium ${cls}`}
>
{label}
</span>
);
}