feat(admin): 学生基本信息枚举化并按区块分组展示
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user