feat: 重构各业务模块管理页面与服务

This commit is contained in:
2026-08-05 17:12:00 +08:00
parent 80e6fccf05
commit fd39e1686a
163 changed files with 18409 additions and 13449 deletions

View File

@@ -0,0 +1,179 @@
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';
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,
}) => {
return (
<Modal
title={editing ? '编辑学生' : '添加学生'}
className="student-form-modal"
width={720}
open={open}
onOk={onOk}
onCancel={onCancel}
okText="保存"
confirmLoading={saving}
>
<Form form={form} layout="vertical" 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>
);
};