feat(admin): 学生基本信息枚举化并按区块分组展示

This commit is contained in:
2026-08-10 17:29:48 +08:00
parent de5843475f
commit 7df28f46e0

View File

@@ -34,7 +34,7 @@ import EditableCell from '../EditableCell';
import { usePermission } from '../../hooks/usePermission';
import { QueryErrorState } from '../QueryState';
import { ADMISSION_STATUS_MAP, ATTENDANCE_STATUS_MAP, SESSION_LABELS, getOptionLabel } from './shared';
import { ADMISSION_STATUS_MAP, ATTENDANCE_STATUS_MAP, GENDER_OPTIONS, GRADE_OPTIONS, SESSION_LABELS, SUBJECT_DIRECTION_OPTIONS, getOptionLabel } from './shared';
import type { AttendanceRecordItem, ProfileData, ResultData, StudentInfo, StudentProfileAggregate, StudentProfileContentProps } from './shared';
import { EnrollmentsTab } from './EnrollmentsTab';
import { ExamScoresTab } from './ExamScoresTab';
@@ -45,13 +45,15 @@ const EditableField: React.FC<{
value: unknown;
onSave: (value: unknown) => Promise<void> | void;
editor?: React.ComponentProps<typeof EditableCell>['editor'];
options?: React.ComponentProps<typeof EditableCell>['options'];
min?: number;
required?: boolean;
children?: React.ReactNode;
}> = ({ value, onSave, editor, min, required, children }) => (
}> = ({ value, onSave, editor, options, min, required, children }) => (
<EditableCell
value={value}
editor={editor}
options={options}
min={min}
required={required}
permission="student:edit"
@@ -188,277 +190,296 @@ const InlineArchiveSummary: React.FC<{
);
return (
<Descriptions bordered column={{ xs: 1, sm: 2, lg: 3 }} size="small" style={{ marginBottom: 24 }}>
<Descriptions.Item label="手机号">
<EditableCell
value={student.phone}
permission="student:edit"
onSave={(next) => saveStudent('phone', next)}
>
{student.phone ? (
<span>
<span style={{ marginRight: 8 }}>{maskPhone(student.phone)}</span>
{canViewSensitive ? (
<a onClick={() => onViewSensitive('电话', student.phone)}>
<EyeOutlined style={{ fontSize: 12, color: '#999' }} />
</a>
) : null}
</span>
) : (
'-'
)}
</EditableCell>
</Descriptions.Item>
<div style={{ marginBottom: 24 }}>
<Card size="small" title="基本信息" style={{ marginBottom: 16 }}>
<Descriptions bordered column={{ xs: 1, sm: 2, lg: 3 }} size="small">
<Descriptions.Item label="手机号">
<EditableCell
value={student.phone}
permission="student:edit"
onSave={(next) => saveStudent('phone', next)}
>
{student.phone ? (
<span>
<span style={{ marginRight: 8 }}>{maskPhone(student.phone)}</span>
{canViewSensitive ? (
<a onClick={() => onViewSensitive('电话', student.phone)}>
<EyeOutlined style={{ fontSize: 12, color: '#999' }} />
</a>
) : null}
</span>
) : (
'-'
)}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="姓名">
<EditableField value={student.name} required onSave={(next) => saveStudent('name', next)}>
{student.name || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="姓名">
<EditableField value={student.name} required onSave={(next) => saveStudent('name', next)}>
{student.name || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="学号">
<EditableField value={student.studentNo} onSave={(next) => saveStudent('studentNo', next)}>
{student.studentNo || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="学号">
<EditableField value={student.studentNo} onSave={(next) => saveStudent('studentNo', next)}>
{student.studentNo || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="性别">
<EditableField value={student.gender} onSave={(next) => saveStudent('gender', next)}>
{student.gender || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="性别">
<EditableField
value={student.gender}
editor="select"
options={GENDER_OPTIONS}
onSave={(next) => saveStudent('gender', next)}
>
{student.gender || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="身份证号">
<EditableCell
value={student.idNumber}
permission="student:edit"
onSave={(next) => saveStudent('idNumber', next)}
>
{student.idNumber ? (
<span>
<span style={{ marginRight: 8 }}>{maskIdNumber(student.idNumber)}</span>
{canViewSensitive ? (
<a onClick={() => onViewSensitive('身份证号', student.idNumber)}>
<EyeOutlined style={{ fontSize: 12, color: '#999' }} />
</a>
) : null}
</span>
) : (
'-'
)}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="身份证号">
<EditableCell
value={student.idNumber}
permission="student:edit"
onSave={(next) => saveStudent('idNumber', next)}
>
{student.idNumber ? (
<span>
<span style={{ marginRight: 8 }}>{maskIdNumber(student.idNumber)}</span>
{canViewSensitive ? (
<a onClick={() => onViewSensitive('身份证号', student.idNumber)}>
<EyeOutlined style={{ fontSize: 12, color: '#999' }} />
</a>
) : null}
</span>
) : (
'-'
)}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="民族">
<EditableField value={student.ethnicity} onSave={(next) => saveStudent('ethnicity', next)}>
{student.ethnicity || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="民族">
<EditableField value={student.ethnicity} onSave={(next) => saveStudent('ethnicity', next)}>
{student.ethnicity || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="紧急联系人">
<EditableCell
value={student.emergencyContact}
permission="student:edit"
onSave={(next) => saveStudent('emergencyContact', next)}
>
{student.emergencyContact || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="紧急联系人">
<EditableCell
value={student.emergencyContact}
permission="student:edit"
onSave={(next) => saveStudent('emergencyContact', next)}
>
{student.emergencyContact || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="紧急联系人电话">
<EditableCell
value={student.emergencyPhone}
permission="student:edit"
onSave={(next) => saveStudent('emergencyPhone', next)}
>
{student.emergencyPhone ? (
<span>
<span style={{ marginRight: 8 }}>{maskPhone(student.emergencyPhone)}</span>
{canViewSensitive ? (
<a onClick={() => onViewSensitive('紧急联系人电话', student.emergencyPhone || '')}>
<EyeOutlined style={{ fontSize: 12, color: '#999' }} />
</a>
) : null}
</span>
) : (
'-'
)}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="紧急联系人电话">
<EditableCell
value={student.emergencyPhone}
permission="student:edit"
onSave={(next) => saveStudent('emergencyPhone', next)}
>
{student.emergencyPhone ? (
<span>
<span style={{ marginRight: 8 }}>{maskPhone(student.emergencyPhone)}</span>
{canViewSensitive ? (
<a onClick={() => onViewSensitive('紧急联系人电话', student.emergencyPhone || '')}>
<EyeOutlined style={{ fontSize: 12, color: '#999' }} />
</a>
) : null}
</span>
) : (
'-'
)}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="所属机构">
{canChooseOrganization ? (
<EditableCell
value={student.organizationId}
editor="select"
options={organizations.map((item) => ({ value: item.id, label: item.name }))}
permission="student:edit"
onSave={(next) => saveStudent('organizationId', next)}
>
{student.organization?.name ? (
<Descriptions.Item label="所属机构">
{canChooseOrganization ? (
<EditableCell
value={student.organizationId}
editor="select"
options={organizations.map((item) => ({ value: item.id, label: item.name }))}
permission="student:edit"
onSave={(next) => saveStudent('organizationId', next)}
>
{student.organization?.name ? (
<Tag color="purple">{student.organization.name}</Tag>
) : (
'-'
)}
</EditableCell>
) : student.organization?.name ? (
<Tag color="purple">{student.organization.name}</Tag>
) : (
'-'
)}
</EditableCell>
) : student.organization?.name ? (
<Tag color="purple">{student.organization.name}</Tag>
) : (
'-'
)}
</Descriptions.Item>
</Descriptions.Item>
<Descriptions.Item label="负责人">
<EditableCell
value={student.supervisor}
permission="student:edit"
onSave={(next) => saveStudent('supervisor', next)}
>
{student.supervisor || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="负责人">
<EditableCell
value={student.supervisor}
permission="student:edit"
onSave={(next) => saveStudent('supervisor', next)}
>
{student.supervisor || '-'}
</EditableCell>
</Descriptions.Item>
</Descriptions>
</Card>
<Descriptions.Item label="目标院校">
<EditableCell
value={profile?.targetCollege}
permission="student:edit"
onSave={(next) => saveProfile('targetCollege', next)}
>
{profile?.targetCollege || '-'}
</EditableCell>
</Descriptions.Item>
<Card size="small" title="学业规划" style={{ marginBottom: 16 }}>
<Descriptions bordered column={{ xs: 1, sm: 2, lg: 3 }} size="small">
<Descriptions.Item label="目标院校">
<EditableCell
value={profile?.targetCollege}
permission="student:edit"
onSave={(next) => saveProfile('targetCollege', next)}
>
{profile?.targetCollege || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="目标专业">
<EditableCell
value={profile?.targetMajor}
permission="student:edit"
onSave={(next) => saveProfile('targetMajor', next)}
>
{profile?.targetMajor || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="目标专业">
<EditableCell
value={profile?.targetMajor}
permission="student:edit"
onSave={(next) => saveProfile('targetMajor', next)}
>
{profile?.targetMajor || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="大专院校">
<EditableCell
value={profile?.collegeSchool}
permission="student:edit"
onSave={(next) => saveProfile('collegeSchool', next)}
>
{profile?.collegeSchool || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="大专院校">
<EditableCell
value={profile?.collegeSchool}
permission="student:edit"
onSave={(next) => saveProfile('collegeSchool', next)}
>
{profile?.collegeSchool || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="大专专业">
<EditableCell
value={profile?.collegeMajor}
permission="student:edit"
onSave={(next) => saveProfile('collegeMajor', next)}
>
{profile?.collegeMajor || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="大专专业">
<EditableCell
value={profile?.collegeMajor}
permission="student:edit"
onSave={(next) => saveProfile('collegeMajor', next)}
>
{profile?.collegeMajor || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="选科方向">
<EditableCell
value={profile?.subjectDirection}
permission="student:edit"
onSave={(next) => saveProfile('subjectDirection', next)}
>
{profile?.subjectDirection || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="选科方向">
<EditableField
value={profile?.subjectDirection}
editor="select"
options={SUBJECT_DIRECTION_OPTIONS}
onSave={(next) => saveProfile('subjectDirection', next)}
>
{profile?.subjectDirection || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="年级">
<EditableCell
value={profile?.grade}
permission="student:edit"
onSave={(next) => saveProfile('grade', next)}
>
{profile?.grade || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="年级">
<EditableField
value={profile?.grade}
editor="select"
options={GRADE_OPTIONS}
onSave={(next) => saveProfile('grade', next)}
>
{profile?.grade || '-'}
</EditableField>
</Descriptions.Item>
<Descriptions.Item label="建档日期">
<EditableCell
value={profile?.profileDate}
editor="date"
permission="student:edit"
onSave={(next) => saveProfile('profileDate', next)}
>
{profile?.profileDate || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="建档日期">
<EditableCell
value={profile?.profileDate}
editor="date"
permission="student:edit"
onSave={(next) => saveProfile('profileDate', next)}
>
{profile?.profileDate || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="档案备注">
<EditableCell
value={profile?.notes}
editor="textarea"
permission="student:edit"
onSave={(next) => saveProfile('notes', next)}
>
{profile?.notes || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="档案备注">
<EditableCell
value={profile?.notes}
editor="textarea"
permission="student:edit"
onSave={(next) => saveProfile('notes', next)}
>
{profile?.notes || '-'}
</EditableCell>
</Descriptions.Item>
</Descriptions>
</Card>
<Descriptions.Item label="文化课最终分">
<EditableCell
value={result?.cultureFinalScore}
editor="number"
min={0}
permission="student:edit"
onSave={(next) => saveResult('cultureFinalScore', next)}
>
{result?.cultureFinalScore ?? '-'}
</EditableCell>
</Descriptions.Item>
<Card size="small" title="录取结果">
<Descriptions bordered column={{ xs: 1, sm: 2, lg: 3 }} size="small">
<Descriptions.Item label="文化课最终分">
<EditableCell
value={result?.cultureFinalScore}
editor="number"
min={0}
permission="student:edit"
onSave={(next) => saveResult('cultureFinalScore', next)}
>
{result?.cultureFinalScore ?? '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="专业课最终分">
<EditableCell
value={result?.professionalFinalScore}
editor="number"
min={0}
permission="student:edit"
onSave={(next) => saveResult('professionalFinalScore', next)}
>
{result?.professionalFinalScore ?? '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="专业课最终分">
<EditableCell
value={result?.professionalFinalScore}
editor="number"
min={0}
permission="student:edit"
onSave={(next) => saveResult('professionalFinalScore', next)}
>
{result?.professionalFinalScore ?? '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="录取状态">
<EditableCell
value={result?.admissionStatus}
editor="select"
options={Object.entries(ADMISSION_STATUS_MAP).map(([value, meta]) => ({
value,
label: meta.text,
}))}
permission="student:edit"
onSave={(next) => saveResult('admissionStatus', next)}
>
{admissionStatus}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="录取状态">
<EditableCell
value={result?.admissionStatus}
editor="select"
options={Object.entries(ADMISSION_STATUS_MAP).map(([value, meta]) => ({
value,
label: meta.text,
}))}
permission="student:edit"
onSave={(next) => saveResult('admissionStatus', next)}
>
{admissionStatus}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="录取院校">
<EditableCell
value={result?.admittedCollege}
permission="student:edit"
onSave={(next) => saveResult('admittedCollege', next)}
>
{result?.admittedCollege || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="录取院校">
<EditableCell
value={result?.admittedCollege}
permission="student:edit"
onSave={(next) => saveResult('admittedCollege', next)}
>
{result?.admittedCollege || '-'}
</EditableCell>
</Descriptions.Item>
<Descriptions.Item label="录取专业">
<EditableCell
value={result?.admittedMajor}
permission="student:edit"
onSave={(next) => saveResult('admittedMajor', next)}
>
{result?.admittedMajor || '-'}
</EditableCell>
</Descriptions.Item>
</Descriptions>
<Descriptions.Item label="录取专业">
<EditableCell
value={result?.admittedMajor}
permission="student:edit"
onSave={(next) => saveResult('admittedMajor', next)}
>
{result?.admittedMajor || '-'}
</EditableCell>
</Descriptions.Item>
</Descriptions>
</Card>
</div>
);
};