321 lines
15 KiB
JavaScript
321 lines
15 KiB
JavaScript
import { useEffect, useState } from "react";
|
||
import {
|
||
Button,
|
||
Col,
|
||
DatePicker,
|
||
Form,
|
||
Input,
|
||
InputNumber,
|
||
Modal,
|
||
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";
|
||
import { NS_COURSEWARE, NS_ORG_INFO } from "~/enumerate/namespace";
|
||
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);
|
||
const [orgOptions, setOrgOptions] = useState([]);
|
||
const [activeTab, setActiveTab] = useState("base");
|
||
const isEdit = !!router.query.classId;
|
||
|
||
const unlocked = !!savedClassId;
|
||
|
||
const confirmLoading = props.courseware?.classConfirmLoading;
|
||
const completeLoading = props.courseware?.classCompleteLoading;
|
||
const detailLoading = props.courseware?.classDetailLoading;
|
||
|
||
const {
|
||
classDetailData,
|
||
} = props.courseware || {};
|
||
|
||
/** 是否开启考试:选否时隐藏其余考试相关字段 */
|
||
const openExam = Form.useWatch("openExam", form);
|
||
|
||
const handleUnitChange = (unitId) => {
|
||
const target = orgOptions.find((item) => item.value === unitId);
|
||
form.setFieldValue("unitName", target?.label);
|
||
};
|
||
|
||
useEffect(() => {
|
||
(async () => {
|
||
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);
|
||
})
|
||
|
||
|
||
if (!isEdit) return;
|
||
const res = await props.classDetail({ id: router.query.classId });
|
||
if (res?.success !== false && res?.data) {
|
||
const data = res.data;
|
||
let unitId = data.unitId;
|
||
|
||
form.setFieldsValue({
|
||
...data,
|
||
unitId,
|
||
trainingDateRange:
|
||
data.startDate && data.endDate
|
||
? [dayjs(data.startDate), dayjs(data.endDate)]
|
||
: undefined,
|
||
});
|
||
|
||
} else {
|
||
props.history.goBack();
|
||
}
|
||
})();
|
||
}, []);
|
||
|
||
const handleSubmit = async () => {
|
||
try {
|
||
const values = await form.validateFields();
|
||
// 培训日期范围拆为后端 startDate/endDate;结束时间为当天最大值(与延期一致)
|
||
const { trainingDateRange, ...rest } = values;
|
||
const payload = {
|
||
...rest,
|
||
startDate: trainingDateRange?.[0]?.format("YYYY-MM-DD"),
|
||
endDate: trainingDateRange?.[1]
|
||
?.endOf("day")
|
||
.format("YYYY-MM-DD HH:mm:ss"),
|
||
...(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 层提示
|
||
}
|
||
};
|
||
|
||
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();
|
||
},
|
||
});
|
||
};
|
||
|
||
return (
|
||
<PageLayout
|
||
title={isEdit ? "编辑班级" : "新增班级"}
|
||
history={props.history}
|
||
previous
|
||
footer={
|
||
<Space>
|
||
<Button onClick={() => props.history.goBack()}>取消</Button>
|
||
<Button type="primary" loading={confirmLoading} onClick={handleSubmit}>
|
||
保存
|
||
</Button>
|
||
{activeTab === "course" && unlocked && classDetailData?.status === 0 && (
|
||
<Button type="primary" loading={completeLoading} onClick={handleComplete}>
|
||
完成
|
||
</Button>
|
||
)}
|
||
</Space>
|
||
}
|
||
>
|
||
<Tabs
|
||
activeKey={activeTab}
|
||
onChange={setActiveTab}
|
||
items={[
|
||
{
|
||
key: "base",
|
||
label: "基础信息",
|
||
children: (
|
||
<Spin spinning={isEdit && !!detailLoading}>
|
||
<Form
|
||
form={form}
|
||
disabled={classDetailData?.status === 2}
|
||
layout="vertical"
|
||
scrollToFirstError
|
||
initialValues={{
|
||
openExam: 0,
|
||
examFaceRecognition: 0,
|
||
faceRecognition: 0,
|
||
shuffleQuestions: 0,
|
||
}}
|
||
>
|
||
<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>
|
||
|
||
|
||
|
||
<Col span={12}>
|
||
<Form.Item
|
||
label="培训类型"
|
||
name="trainingType"
|
||
rules={[{ required: true, message: "请选择培训类型" }]}
|
||
>
|
||
<Select
|
||
placeholder="请选择培训类型"
|
||
allowClear
|
||
options={COURSEWARE_TRAINING_TYPE_OPTIONS}
|
||
/>
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
|
||
<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>
|
||
</Form>
|
||
</Spin>
|
||
),
|
||
},
|
||
{
|
||
key: "student",
|
||
label: "学员",
|
||
disabled: !unlocked,
|
||
children: unlocked ? (
|
||
<StudentTab classId={savedClassId} history={props.history} form={form} />
|
||
) : null,
|
||
},
|
||
{
|
||
key: "course",
|
||
label: "课程",
|
||
disabled: !unlocked,
|
||
children: unlocked ? (
|
||
<CourseTab classId={savedClassId} history={props.history} />
|
||
) : null,
|
||
},
|
||
]}
|
||
/>
|
||
</PageLayout>
|
||
);
|
||
}
|
||
|
||
export default Connect([NS_COURSEWARE, NS_ORG_INFO], true)(ClassAddPage);
|