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