safety-eval-service-frontend/src/pages/Container/EduTraining/ClassManage/Add/index.js

318 lines
15 KiB
JavaScript
Raw Normal View History

2026-08-21 17:43:57 +08:00
import { useEffect, useState } from "react";
import {
Button,
Col,
DatePicker,
Form,
Input,
InputNumber,
2026-08-24 18:29:53 +08:00
Modal,
2026-08-21 17:43:57 +08:00
Radio,
Row,
Select,
Space,
Spin,
Tabs,
message,
} from "antd";
import dayjs from "dayjs";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { tools } from "@cqsjjb/jjb-common-lib";
2026-08-24 18:29:53 +08:00
import { NS_COURSEWARE, NS_ORG_INFO } from "~/enumerate/namespace";
2026-08-21 17:43:57 +08:00
import {
CLASS_UNIT_TYPE_OPTIONS,
CLASS_YES_NO_OPTIONS,
COURSEWARE_TRAINING_TYPE_OPTIONS,
} from "~/enumerate/constant";
import StudentTab from "./StudentTab";
import CourseTab from "./CourseTab";
const { router } = tools;
const { RangePicker } = DatePicker;
/** 教育培训 — 新增/编辑班级 */
function ClassAddPage(props) {
const [form] = Form.useForm();
/** 已保存的班级 id有值时解锁学员/课程 Tab */
const [savedClassId, setSavedClassId] = useState(router.query.classId);
2026-08-24 18:29:53 +08:00
const [orgOptions, setOrgOptions] = useState([]);
const [activeTab, setActiveTab] = useState("base");
2026-08-21 17:43:57 +08:00
const isEdit = !!router.query.classId;
2026-08-25 11:12:49 +08:00
2026-08-21 17:43:57 +08:00
const unlocked = !!savedClassId;
const confirmLoading = props.courseware?.classConfirmLoading;
2026-08-24 18:29:53 +08:00
const completeLoading = props.courseware?.classCompleteLoading;
2026-08-21 17:43:57 +08:00
const detailLoading = props.courseware?.classDetailLoading;
2026-08-25 11:12:49 +08:00
const {
classDetailData,
} = props.courseware || {};
2026-08-21 17:43:57 +08:00
/** 是否开启考试:选否时隐藏其余考试相关字段 */
const openExam = Form.useWatch("openExam", form);
2026-08-24 18:29:53 +08:00
const handleUnitChange = (unitId) => {
const target = orgOptions.find((item) => item.value === unitId);
form.setFieldValue("unitName", target?.label);
};
2026-08-21 17:43:57 +08:00
useEffect(() => {
(async () => {
2026-08-25 11:12:49 +08:00
props.registeredOrgList({ current: 1, size: 200, state: 0 }).then(orgRes => {
const list = orgRes?.data || [];
const options = list.map((item) => ({
label: item.unitName,
value: item.id,
}));
setOrgOptions(options);
})
2026-08-24 18:29:53 +08:00
if (!isEdit) return;
2026-08-21 17:43:57 +08:00
const res = await props.classDetail({ id: router.query.classId });
if (res?.success !== false && res?.data) {
const data = res.data;
2026-08-24 18:29:53 +08:00
let unitId = data.unitId;
2026-08-25 11:12:49 +08:00
2026-08-21 17:43:57 +08:00
form.setFieldsValue({
...data,
2026-08-24 18:29:53 +08:00
unitId,
2026-08-21 17:43:57 +08:00
trainingDateRange:
data.startDate && data.endDate
? [dayjs(data.startDate), dayjs(data.endDate)]
: undefined,
});
2026-08-25 11:12:49 +08:00
2026-08-21 17:43:57 +08:00
} else {
props.history.goBack();
}
})();
}, []);
const handleSubmit = async () => {
try {
const values = await form.validateFields();
// 培训日期范围拆为后端 startDate/endDateyyyy-MM-dd
const { trainingDateRange, ...rest } = values;
const payload = {
...rest,
startDate: trainingDateRange?.[0]?.format("YYYY-MM-DD"),
endDate: trainingDateRange?.[1]?.format("YYYY-MM-DD"),
...(isEdit ? { id: router.query.classId } : {}),
};
const res = await (isEdit
? props.classModify(payload)
: props.classAdd(payload));
if (res?.success === false) return;
message.success("保存成功");
if (isEdit) return;
// 新增save 接口返回新班级 id同步 URL 并解锁学员/课程 Tab
const newId = res?.data;
if (newId) {
router.query = { ...router.query, classId: newId };
setSavedClassId(newId);
}
} catch {
// 表单校验失败或请求异常,由 antd / http 层提示
}
};
2026-08-24 18:29:53 +08:00
const handleComplete = () => {
Modal.confirm({
title: "提示",
content: "完成之后将开班,不能再进行其它更改,确认继续吗?",
okText: "确定",
cancelText: "取消",
onOk: async () => {
const res = await props.classComplete({ id: savedClassId });
if (res?.success === false) return;
message.success("完成成功");
props.history.goBack();
},
});
};
2026-08-21 17:43:57 +08:00
return (
<PageLayout
title={isEdit ? "编辑班级" : "新增班级"}
history={props.history}
previous
footer={
<Space>
<Button onClick={() => props.history.goBack()}>取消</Button>
<Button type="primary" loading={confirmLoading} onClick={handleSubmit}>
保存
</Button>
2026-08-25 11:12:49 +08:00
{activeTab === "course" && unlocked && classDetailData?.status === 0 && (
2026-08-24 18:29:53 +08:00
<Button type="primary" loading={completeLoading} onClick={handleComplete}>
完成
</Button>
)}
2026-08-21 17:43:57 +08:00
</Space>
}
>
<Tabs
2026-08-24 18:29:53 +08:00
activeKey={activeTab}
onChange={setActiveTab}
2026-08-21 17:43:57 +08:00
items={[
{
key: "base",
label: "基础信息",
children: (
<Spin spinning={isEdit && !!detailLoading}>
<Form
form={form}
layout="vertical"
scrollToFirstError
initialValues={{
openExam: 0,
examFaceRecognition: 0,
faceRecognition: 0,
shuffleQuestions: 0,
}}
>
2026-08-25 11:12:49 +08:00
<h3>基本信息</h3>
<Row gutter={24}>
<Col span={12}>
<Form.Item label="参训单位类型" name="unitType">
<Select
placeholder="请选择"
allowClear
options={CLASS_UNIT_TYPE_OPTIONS}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="班级名称"
name="className"
rules={[{ required: true, message: "请输入班级名称" }]}
>
<Input placeholder="请输入班级名称" allowClear maxLength={50} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="参训单位"
name="unitId"
rules={[{ required: true, message: "请选择参训单位" }]}
>
<Select
placeholder="请选择参训单位"
allowClear
showSearch
optionFilterProp="label"
options={orgOptions}
onChange={handleUnitChange}
/>
</Form.Item>
<Form.Item name="unitName" hidden>
<Input />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item
label="培训日期"
name="trainingDateRange"
rules={[{ required: true, message: "请选择培训日期" }]}
>
<RangePicker style={{ width: "100%" }} />
</Form.Item>
</Col>
2026-08-21 17:43:57 +08:00
2026-08-25 11:12:49 +08:00
<Col span={12}>
<Form.Item
label="培训类型"
name="trainingType"
rules={[{ required: true, message: "请选择培训类型" }]}
>
<Select
placeholder="请选择培训类型"
allowClear
options={COURSEWARE_TRAINING_TYPE_OPTIONS}
/>
</Form.Item>
</Col>
</Row>
2026-08-21 17:43:57 +08:00
2026-08-25 11:12:49 +08:00
<h3>基本设置</h3>
<Row gutter={24}>
<Col span={12}>
<Form.Item label="是否开启考试" name="openExam" extra='不考试的班级,学员学习完所有课程,即为完成学业。'>
<Radio.Group options={CLASS_YES_NO_OPTIONS} />
</Form.Item>
</Col>
{openExam === 1 && (
<>
<Col span={12}>
<Form.Item label="考试次数" name="examCount">
<InputNumber
placeholder="请输入考试次数"
min={1}
max={10}
precision={0}
style={{ width: "100%" }}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="考试前人脸识别" name="examFaceRecognition">
<Radio.Group options={CLASS_YES_NO_OPTIONS} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="考试中人脸识别" name="faceRecognition">
<Radio.Group options={CLASS_YES_NO_OPTIONS} />
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="人脸识别时间(分钟)" name="faceRecognitionTime">
<InputNumber
placeholder="请输入人脸识别时间"
min={1}
max={60}
precision={0}
style={{ width: "100%" }}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="是否打乱试题顺序" name="shuffleQuestions">
<Radio.Group options={CLASS_YES_NO_OPTIONS} />
</Form.Item>
</Col>
</>
)}
</Row>
2026-08-21 17:43:57 +08:00
</Form>
</Spin>
),
},
{
key: "student",
label: "学员",
disabled: !unlocked,
children: unlocked ? (
2026-08-25 11:12:49 +08:00
<StudentTab classId={savedClassId} history={props.history} />
2026-08-21 17:43:57 +08:00
) : null,
},
{
key: "course",
label: "课程",
disabled: !unlocked,
children: unlocked ? (
2026-08-24 11:46:24 +08:00
<CourseTab classId={savedClassId} history={props.history} />
2026-08-21 17:43:57 +08:00
) : null,
},
]}
/>
</PageLayout>
);
}
2026-08-24 18:29:53 +08:00
export default Connect([NS_COURSEWARE, NS_ORG_INFO], true)(ClassAddPage);