182 lines
5.7 KiB
TypeScript
182 lines
5.7 KiB
TypeScript
import React from 'react';
|
||
import { App, Descriptions, Drawer, Form, Input, Modal, Select } from 'antd';
|
||
import JinshujuMatchModal from '../../components/JinshujuMatchModal';
|
||
import StudentProfileContent from '../../components/StudentProfileContent';
|
||
import { SENSITIVE_LABELS } from './StudentColumns';
|
||
import { useSubmitShortcut } from '../../hooks/useSubmitShortcut';
|
||
|
||
type AppModal = ReturnType<typeof App.useApp>['modal'];
|
||
|
||
export const showCreateImportResult = (
|
||
modal: AppModal,
|
||
result: { message?: string; imported?: number; skipped?: number },
|
||
) => {
|
||
const imported = result.imported ?? 0;
|
||
const skipped = result.skipped ?? 0;
|
||
modal.success({
|
||
title: '导入完成',
|
||
okText: '知道了',
|
||
content: (
|
||
<div>
|
||
<Descriptions column={1} size="small">
|
||
<Descriptions.Item label="成功新增">{imported} 人</Descriptions.Item>
|
||
<Descriptions.Item label="跳过">{skipped} 人</Descriptions.Item>
|
||
</Descriptions>
|
||
<div style={{ marginTop: 12, fontWeight: 600 }}>跳过原因:</div>
|
||
<ul style={{ marginBottom: 0, paddingLeft: 20 }}>
|
||
<li>姓名为空</li>
|
||
<li>已存在同名学生</li>
|
||
</ul>
|
||
<div style={{ marginTop: 8, color: '#8c8c8c', fontSize: 12 }}>
|
||
当前后端只返回统计汇总,暂时无法列出具体哪几行被跳过。
|
||
</div>
|
||
</div>
|
||
),
|
||
});
|
||
};
|
||
|
||
export const showUpdateImportResult = (
|
||
modal: AppModal,
|
||
result: { message?: string; matched?: number; skipped?: number },
|
||
) => {
|
||
const matched = result.matched ?? 0;
|
||
const skipped = result.skipped ?? 0;
|
||
modal.success({
|
||
title: '更新完成',
|
||
okText: '知道了',
|
||
content: (
|
||
<div>
|
||
<Descriptions column={1} size="small">
|
||
<Descriptions.Item label="成功更新">{matched} 人</Descriptions.Item>
|
||
<Descriptions.Item label="未匹配">{skipped} 人</Descriptions.Item>
|
||
</Descriptions>
|
||
<div style={{ marginTop: 12, fontWeight: 600 }}>匹配规则:</div>
|
||
<div>手机号优先,身份证号其次</div>
|
||
<div style={{ marginTop: 8, color: '#8c8c8c', fontSize: 12 }}>
|
||
当前后端只返回统计汇总,暂时无法列出具体哪几行未匹配。
|
||
</div>
|
||
</div>
|
||
),
|
||
});
|
||
};
|
||
|
||
export const StudentEditModal: React.FC<{
|
||
open: boolean;
|
||
editing: boolean;
|
||
saving: boolean;
|
||
form: ReturnType<typeof Form.useForm>[0];
|
||
canChooseOrganization: boolean;
|
||
organizations: Array<{ id: number; name: string; isHost?: boolean }>;
|
||
onOk?: () => void;
|
||
onCancel: () => void;
|
||
}> = ({
|
||
open,
|
||
editing,
|
||
saving,
|
||
form,
|
||
canChooseOrganization,
|
||
organizations,
|
||
onOk,
|
||
onCancel,
|
||
}) => {
|
||
useSubmitShortcut(open && !saving, () => onOk?.());
|
||
return (
|
||
<Modal
|
||
title={editing ? '编辑学生' : '添加学生'}
|
||
className="student-form-modal"
|
||
width={720}
|
||
open={open}
|
||
onOk={onOk}
|
||
onCancel={onCancel}
|
||
okText="保存"
|
||
confirmLoading={saving}
|
||
>
|
||
<Form form={form} layout="vertical" scrollToFirstError className="student-form-grid">
|
||
<Form.Item name="name" label="姓名" rules={[{ required: true }]}>
|
||
<Input />
|
||
</Form.Item>
|
||
<Form.Item name="studentNo" label="学号">
|
||
<Input placeholder="学生的学号" />
|
||
</Form.Item>
|
||
<Form.Item name="gender" label="性别">
|
||
<Select
|
||
allowClear
|
||
options={[
|
||
{ value: '男', label: '男' },
|
||
{ value: '女', label: '女' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name="phone" label={SENSITIVE_LABELS.phone}>
|
||
<Input />
|
||
</Form.Item>
|
||
<Form.Item name="idNumber" label="身份证">
|
||
<Input />
|
||
</Form.Item>
|
||
<Form.Item name="ethnicity" label="民族">
|
||
<Input placeholder="如:汉族" />
|
||
</Form.Item>
|
||
<Form.Item name="emergencyContact" label="紧急联系人">
|
||
<Input />
|
||
</Form.Item>
|
||
<Form.Item name="emergencyPhone" label={SENSITIVE_LABELS.emergencyPhone}>
|
||
<Input />
|
||
</Form.Item>
|
||
{canChooseOrganization ? (
|
||
<Form.Item
|
||
name="organizationId"
|
||
label="所属机构"
|
||
rules={[{ required: true, message: '请选择所属机构' }]}
|
||
>
|
||
<Select
|
||
showSearch
|
||
optionFilterProp="label"
|
||
placeholder="选择所属机构"
|
||
options={organizations.map((organization) => ({
|
||
value: organization.id,
|
||
label: organization.isHost
|
||
? `${organization.name}(本机构)`
|
||
: organization.name,
|
||
}))}
|
||
/>
|
||
</Form.Item>
|
||
) : null}
|
||
<Form.Item name="supervisor" label="负责人/班主任">
|
||
<Input />
|
||
</Form.Item>
|
||
{editing && (
|
||
<Form.Item name="status" label="状态">
|
||
<Select
|
||
options={[
|
||
{ value: 'active', label: '在读' },
|
||
{ value: 'graduated', label: '已毕业' },
|
||
{ value: 'withdrawn', label: '已退训' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
)}
|
||
</Form>
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
export const JinshujuModal: React.FC<{
|
||
open: boolean;
|
||
onClose: () => void;
|
||
onApplied: () => void;
|
||
}> = ({ open, onClose, onApplied }) => {
|
||
return <JinshujuMatchModal open={open} onClose={onClose} onApplied={onApplied} />;
|
||
};
|
||
|
||
export const StudentDrawer: React.FC<{
|
||
open: boolean;
|
||
studentId: number | null;
|
||
onClose: () => void;
|
||
}> = ({ open, studentId, onClose }) => {
|
||
return (
|
||
<Drawer title={null} open={open} onClose={onClose} size={720}>
|
||
{studentId && <StudentProfileContent studentId={studentId} inDrawer onClose={onClose} />}
|
||
</Drawer>
|
||
);
|
||
};
|