feat(portal-shell): 学生域全页面迁移与规范合规修复
- 学生域 32 页全量迁移(含作答/自动保存/提交/诊断) - 补齐 4 个 MSW mock 缺口,修 diagnostic case 名 - 修 4 处 Tailwind 任意值;新增共享组件与路由
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user