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