2026-06-23 18:07:30 +08:00
|
|
|
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
2026-07-07 09:07:19 +08:00
|
|
|
|
import { Button, DatePicker, Descriptions, Form, Input, message, Modal, Select, Table, Upload } from "antd";
|
|
|
|
|
|
import { UploadOutlined } from "@ant-design/icons";
|
2026-06-23 18:07:30 +08:00
|
|
|
|
import { useEffect, useState } from "react";
|
2026-07-07 13:38:33 +08:00
|
|
|
|
import dayjs from "dayjs";
|
2026-06-29 16:45:58 +08:00
|
|
|
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
2026-07-07 09:07:19 +08:00
|
|
|
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
|
|
|
|
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
|
|
|
|
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
|
|
|
|
|
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
|
|
|
|
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
2026-06-23 18:07:30 +08:00
|
|
|
|
import { NS_STAFF_CERTIFICATE } from "~/enumerate/namespace";
|
2026-07-07 09:07:19 +08:00
|
|
|
|
import CertPreviewImg from "~/components/CertPreviewImg";
|
2026-07-07 13:54:55 +08:00
|
|
|
|
import AttachmentUpload from "~/components/AttachmentUpload";
|
2026-06-23 18:07:30 +08:00
|
|
|
|
import { dateRangeRule } from "~/utils/validators";
|
2026-06-29 16:45:58 +08:00
|
|
|
|
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const { router } = tools;
|
2026-06-23 18:07:30 +08:00
|
|
|
|
|
|
|
|
|
|
function StaffCertificatePage(props) {
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const urlParams = Object.fromEntries(new URLSearchParams(window.location.search));
|
|
|
|
|
|
const staffId = urlParams.staffId;
|
|
|
|
|
|
const staffName = urlParams.staffName ? decodeURIComponent(urlParams.staffName) : "";
|
2026-06-23 18:07:30 +08:00
|
|
|
|
const [addModalOpen, setAddModalOpen] = useState(false);
|
|
|
|
|
|
const [viewModalOpen, setViewModalOpen] = useState(false);
|
|
|
|
|
|
const [currentId, setCurrentId] = useState("");
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const [searchForm] = Form.useForm();
|
|
|
|
|
|
const { staffCertificate } = props;
|
|
|
|
|
|
const {
|
|
|
|
|
|
staffCertificateList: dataSource,
|
|
|
|
|
|
staffCertificateTotal: total,
|
|
|
|
|
|
staffCertificateLoading: loading,
|
|
|
|
|
|
} = staffCertificate || {};
|
2026-06-23 18:07:30 +08:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-07-07 09:07:19 +08:00
|
|
|
|
searchForm.setFieldsValue(router.query);
|
|
|
|
|
|
handleSearch();
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
const handleSearch = () => {
|
|
|
|
|
|
props.staffCertificateList({ ...router.query, personnelId: staffId });
|
|
|
|
|
|
};
|
2026-06-23 18:07:30 +08:00
|
|
|
|
|
|
|
|
|
|
const goBack = () => {
|
|
|
|
|
|
window.location.href = window.location.pathname.replace(/\/Certificate.*$/, "/List");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const onDelete = (id) => {
|
|
|
|
|
|
Modal.confirm({
|
|
|
|
|
|
title: "提示",
|
|
|
|
|
|
content: "数据将会删除,您是否确认删除?",
|
|
|
|
|
|
okText: "是",
|
|
|
|
|
|
cancelText: "否",
|
|
|
|
|
|
onOk: async () => {
|
2026-07-08 20:31:28 +08:00
|
|
|
|
const res = await props.staffCertificateRemove({ data: id });
|
2026-06-23 18:07:30 +08:00
|
|
|
|
if (res?.success !== false) {
|
|
|
|
|
|
message.success("删除成功");
|
2026-07-07 09:07:19 +08:00
|
|
|
|
handleSearch();
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
2026-07-07 11:52:21 +08:00
|
|
|
|
<PageLayout
|
2026-07-08 15:23:50 +08:00
|
|
|
|
history={props.history}
|
|
|
|
|
|
previous
|
2026-07-07 11:52:21 +08:00
|
|
|
|
title={`人员证书 - ${staffName || ""}`}
|
|
|
|
|
|
extra={
|
|
|
|
|
|
<Button type="primary" onClick={() => { setCurrentId(""); setAddModalOpen(true); }}>新增证书</Button>
|
|
|
|
|
|
}
|
|
|
|
|
|
>
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<SearchForm
|
|
|
|
|
|
style={{ marginBottom: 24 }}
|
|
|
|
|
|
form={searchForm}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
formLine={[
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
key="certName"
|
|
|
|
|
|
name="certName"
|
|
|
|
|
|
rules={[{ max: 200, message: "证照名称不能超过200个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<ControlWrapper.Input label="证照名称" placeholder="请输入证照名称" allowClear maxLength={200} />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>,
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
key="certNo"
|
|
|
|
|
|
name="certNo"
|
|
|
|
|
|
rules={[{ max: 100, message: "证书编号不能超过100个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<ControlWrapper.Input label="证书编号" placeholder="请输入证书编号" allowClear maxLength={100} />
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form.Item>,
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
key="certCategoryName"
|
|
|
|
|
|
name="certCategoryName"
|
|
|
|
|
|
rules={[{ max: 50, message: "证书类别名称不能超过50个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<ControlWrapper.Input label="证书类别名称" placeholder="请输入证书类别名称" allowClear maxLength={50} />
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form.Item>,
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
key="operationCategoryName"
|
|
|
|
|
|
name="operationCategoryName"
|
|
|
|
|
|
rules={[{ max: 50, message: "证书作业类别名称不能超过50个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<ControlWrapper.Input label="证书作业类别名称" placeholder="请输入证书作业类别名称" allowClear maxLength={50} />
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form.Item>,
|
2026-06-23 18:07:30 +08:00
|
|
|
|
]}
|
2026-07-07 15:58:05 +08:00
|
|
|
|
onReset={(values) => {
|
|
|
|
|
|
router.query = { ...values, current: 1, size: 10 };
|
2026-07-07 09:07:19 +08:00
|
|
|
|
handleSearch();
|
|
|
|
|
|
}}
|
2026-07-07 15:58:05 +08:00
|
|
|
|
onFinish={(values) => {
|
|
|
|
|
|
router.query = { ...values, current: 1, size: 10 };
|
2026-07-07 09:07:19 +08:00
|
|
|
|
handleSearch();
|
|
|
|
|
|
}}
|
2026-06-23 18:07:30 +08:00
|
|
|
|
/>
|
|
|
|
|
|
<Table
|
2026-07-07 09:07:19 +08:00
|
|
|
|
rowKey="id"
|
2026-06-23 18:07:30 +08:00
|
|
|
|
columns={[
|
2026-07-07 11:52:21 +08:00
|
|
|
|
{ title: "证照名称", dataIndex: "certName" },
|
|
|
|
|
|
{ title: "证书类型", dataIndex: "certTypeName" },
|
|
|
|
|
|
{ title: "证书类别", dataIndex: "certCategoryName" },
|
|
|
|
|
|
{ title: "证书作业类别", dataIndex: "operationCategoryName" },
|
2026-06-23 18:07:30 +08:00
|
|
|
|
{ title: "证书编号", dataIndex: "certNo" },
|
|
|
|
|
|
{
|
|
|
|
|
|
title: "操作",
|
|
|
|
|
|
width: 200,
|
|
|
|
|
|
render: (_, record) => (
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<TableAction>
|
|
|
|
|
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); setViewModalOpen(true); }}>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
查看
|
|
|
|
|
|
</Button>
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<Button type="link" size="small" onClick={() => { setCurrentId(record.id); setAddModalOpen(true); }}>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
编辑
|
|
|
|
|
|
</Button>
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<Button danger type="link" size="small" onClick={() => onDelete(record.id)}>删除</Button>
|
|
|
|
|
|
</TableAction>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
]}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
dataSource={dataSource}
|
|
|
|
|
|
scroll={{ y: props.scrollY }}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
pagination={{
|
|
|
|
|
|
total,
|
|
|
|
|
|
showSizeChanger: true,
|
|
|
|
|
|
showQuickJumper: true,
|
|
|
|
|
|
showTotal: (t) => `共 ${t} 条`,
|
|
|
|
|
|
current: Number(router.query.current) || 1,
|
|
|
|
|
|
pageSize: Number(router.query.size) || 10,
|
|
|
|
|
|
onChange: (page, pageSize) => {
|
|
|
|
|
|
router.query = { ...router.query, current: page, size: pageSize };
|
|
|
|
|
|
handleSearch();
|
|
|
|
|
|
},
|
|
|
|
|
|
}}
|
2026-06-23 18:07:30 +08:00
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
{addModalOpen && (
|
|
|
|
|
|
<CertModal
|
|
|
|
|
|
open={addModalOpen}
|
|
|
|
|
|
staffId={staffId}
|
|
|
|
|
|
currentId={currentId}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
staffCertificateAdd={props.staffCertificateAdd}
|
|
|
|
|
|
staffCertificateEdit={props.staffCertificateEdit}
|
|
|
|
|
|
staffCertificateInfo={props.staffCertificateInfo}
|
2026-06-23 18:07:30 +08:00
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setAddModalOpen(false);
|
|
|
|
|
|
setCurrentId("");
|
|
|
|
|
|
}}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
onSuccess={handleSearch}
|
2026-06-23 18:07:30 +08:00
|
|
|
|
/>
|
|
|
|
|
|
)}
|
|
|
|
|
|
{viewModalOpen && (
|
|
|
|
|
|
<ViewModal
|
|
|
|
|
|
open={viewModalOpen}
|
|
|
|
|
|
currentId={currentId}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
staffCertificateInfo={props.staffCertificateInfo}
|
2026-06-23 18:07:30 +08:00
|
|
|
|
onCancel={() => {
|
|
|
|
|
|
setViewModalOpen(false);
|
|
|
|
|
|
setCurrentId("");
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
)}
|
2026-06-29 16:45:58 +08:00
|
|
|
|
</PageLayout>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function CertModal({
|
2026-07-07 09:07:19 +08:00
|
|
|
|
open, staffId, currentId, staffCertificateAdd, staffCertificateEdit, staffCertificateInfo, onCancel, onSuccess,
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}) {
|
|
|
|
|
|
const [form] = Form.useForm();
|
|
|
|
|
|
const [submitting, setSubmitting] = useState(false);
|
|
|
|
|
|
const [detailLoading, setDetailLoading] = useState(false);
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const [uploadFileList, setUploadFileList] = useState([]);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
2026-07-07 09:07:19 +08:00
|
|
|
|
if (!open) return;
|
2026-06-23 18:07:30 +08:00
|
|
|
|
if (!currentId) {
|
|
|
|
|
|
form.resetFields();
|
2026-07-07 09:07:19 +08:00
|
|
|
|
setUploadFileList([]);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
|
(async () => {
|
|
|
|
|
|
setDetailLoading(true);
|
|
|
|
|
|
try {
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const res = await staffCertificateInfo({ id: currentId });
|
|
|
|
|
|
if (cancelled || !res?.data) return;
|
2026-06-23 18:07:30 +08:00
|
|
|
|
const data = { ...res.data };
|
2026-07-07 13:38:33 +08:00
|
|
|
|
// 将 validStartDate/validEndDate 合并为 validDate(RangePicker 需要 dayjs 对象)
|
2026-07-07 09:07:19 +08:00
|
|
|
|
if (data.validStartDate || data.validEndDate) {
|
2026-07-07 13:38:33 +08:00
|
|
|
|
data.validDate = [
|
|
|
|
|
|
data.validStartDate ? dayjs(data.validStartDate) : undefined,
|
|
|
|
|
|
data.validEndDate ? dayjs(data.validEndDate) : undefined,
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
// 复核日期需要转为 dayjs 对象
|
|
|
|
|
|
if (data.reviewDate) {
|
|
|
|
|
|
data.reviewDate = dayjs(data.reviewDate);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
// 处理证书附件 fileList
|
|
|
|
|
|
let fileList = [];
|
|
|
|
|
|
if (Array.isArray(data.certImgFiles)) {
|
|
|
|
|
|
fileList = data.certImgFiles.map((f, i) => ({
|
|
|
|
|
|
uid: f.uid || `-${i}`,
|
|
|
|
|
|
name: f.fileName || f.name || `证书附件${i + 1}`,
|
|
|
|
|
|
status: "done",
|
|
|
|
|
|
url: f.url,
|
|
|
|
|
|
}));
|
|
|
|
|
|
} else if (data.certAttachmentUrl) {
|
|
|
|
|
|
fileList = data.certAttachmentUrl.split(",").filter(Boolean).map((url, i) => ({
|
|
|
|
|
|
uid: `-${i}`,
|
|
|
|
|
|
name: url.split("/").pop() || `证书附件${i + 1}`,
|
|
|
|
|
|
status: "done",
|
|
|
|
|
|
url,
|
|
|
|
|
|
}));
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
data.certAttachmentUrl = fileList;
|
|
|
|
|
|
setUploadFileList(fileList);
|
|
|
|
|
|
form.setFieldsValue(data);
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.warn("[StaffCertificate] load detail failed:", err);
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
if (!cancelled) setDetailLoading(false);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
})();
|
2026-07-07 09:07:19 +08:00
|
|
|
|
return () => { cancelled = true; };
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}, [open, currentId]);
|
|
|
|
|
|
|
|
|
|
|
|
const handleCancel = () => {
|
|
|
|
|
|
form.resetFields();
|
2026-07-07 09:07:19 +08:00
|
|
|
|
setUploadFileList([]);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
onCancel();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleSubmit = async (values) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setSubmitting(true);
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const payload = {
|
|
|
|
|
|
...values,
|
|
|
|
|
|
personnelId: staffId,
|
2026-07-07 13:38:33 +08:00
|
|
|
|
validStartDate: values.validDate?.[0] ? dayjs(values.validDate[0]).format("YYYY-MM-DD") : undefined,
|
|
|
|
|
|
validEndDate: values.validDate?.[1] ? dayjs(values.validDate[1]).format("YYYY-MM-DD") : undefined,
|
|
|
|
|
|
reviewDate: values.reviewDate ? dayjs(values.reviewDate).format("YYYY-MM-DD") : undefined,
|
2026-07-07 09:07:19 +08:00
|
|
|
|
certAttachmentUrl: Array.isArray(values.certAttachmentUrl)
|
|
|
|
|
|
? values.certAttachmentUrl.map((f) => f.url || f.response?.url).filter(Boolean).join(",")
|
|
|
|
|
|
: "",
|
|
|
|
|
|
};
|
|
|
|
|
|
delete payload.validDate;
|
|
|
|
|
|
delete payload.certImgFiles;
|
|
|
|
|
|
if (currentId) payload.id = currentId;
|
|
|
|
|
|
const request = currentId ? staffCertificateEdit : staffCertificateAdd;
|
|
|
|
|
|
const res = await request(payload);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
if (res?.success !== false) {
|
|
|
|
|
|
message.success(currentId ? "编辑成功" : "添加成功");
|
|
|
|
|
|
handleCancel();
|
|
|
|
|
|
onSuccess();
|
2026-07-07 09:07:19 +08:00
|
|
|
|
} else {
|
2026-07-10 10:02:53 +08:00
|
|
|
|
message.error(res?.message || res?.errMessage || "保存失败,请重试");
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
} finally {
|
2026-06-23 18:07:30 +08:00
|
|
|
|
setSubmitting(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
open={open}
|
2026-07-06 18:06:49 +08:00
|
|
|
|
destroyOnHidden
|
2026-06-23 18:07:30 +08:00
|
|
|
|
title={currentId ? "编辑证书" : "添加证书"}
|
|
|
|
|
|
width={800}
|
|
|
|
|
|
loading={detailLoading}
|
|
|
|
|
|
confirmLoading={submitting}
|
|
|
|
|
|
onCancel={handleCancel}
|
|
|
|
|
|
onOk={form.submit}
|
|
|
|
|
|
>
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<Form form={form} layout="vertical" onFinish={handleSubmit}>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="certName"
|
|
|
|
|
|
label="证照名称"
|
|
|
|
|
|
rules={[
|
|
|
|
|
|
{ required: true, message: "请输入证照名称" },
|
|
|
|
|
|
{ max: 200, message: "证照名称不能超过200个字符" },
|
|
|
|
|
|
]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证照名称" maxLength={200} showCount />
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="certNo"
|
|
|
|
|
|
label="证书编号"
|
|
|
|
|
|
rules={[
|
|
|
|
|
|
{ required: true, message: "请输入证书编号" },
|
|
|
|
|
|
{ max: 100, message: "证书编号不能超过100个字符" },
|
|
|
|
|
|
]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书编号" maxLength={100} showCount />
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="issueOrg"
|
|
|
|
|
|
label="发证机关"
|
|
|
|
|
|
rules={[
|
|
|
|
|
|
{ required: true, message: "请输入发证机关" },
|
|
|
|
|
|
{ max: 200, message: "发证机关不能超过200个字符" },
|
|
|
|
|
|
]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入发证机关" maxLength={200} showCount />
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form.Item>
|
|
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="validDate"
|
|
|
|
|
|
label="证书有效开始/结束日期"
|
|
|
|
|
|
rules={[{ required: true, message: "请选择证书有效期" }, dateRangeRule()]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<DatePicker.RangePicker style={{ width: "100%" }} />
|
|
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="certTypeCode"
|
|
|
|
|
|
label="证书类型编码"
|
|
|
|
|
|
rules={[{ max: 32, message: "证书类型编码不能超过32个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书类型编码" maxLength={32} showCount />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="certTypeName"
|
|
|
|
|
|
label="证书类型名称"
|
|
|
|
|
|
rules={[{ max: 50, message: "证书类型名称不能超过50个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书类型名称" maxLength={50} showCount />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="certCategoryCode"
|
|
|
|
|
|
label="证书类别编码"
|
|
|
|
|
|
rules={[{ max: 32, message: "证书类别编码不能超过32个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书类别编码" maxLength={32} showCount />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="certCategoryName"
|
|
|
|
|
|
label="证书类别名称"
|
|
|
|
|
|
rules={[{ max: 50, message: "证书类别名称不能超过50个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书类别名称" maxLength={50} showCount />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="operationCategoryCode"
|
|
|
|
|
|
label="证书作业类别编码"
|
|
|
|
|
|
rules={[{ max: 32, message: "证书作业类别编码不能超过32个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书作业类别编码" maxLength={32} showCount />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<Form.Item
|
|
|
|
|
|
name="operationCategoryName"
|
|
|
|
|
|
label="证书作业类别名称"
|
|
|
|
|
|
rules={[{ max: 50, message: "证书作业类别名称不能超过50个字符" }]}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Input placeholder="请输入证书作业类别名称" maxLength={50} showCount />
|
2026-07-07 11:52:21 +08:00
|
|
|
|
</Form.Item>
|
|
|
|
|
|
<Form.Item name="reviewDate" label="复核日期">
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<DatePicker style={{ width: "100%" }} />
|
|
|
|
|
|
</Form.Item>
|
2026-07-10 10:02:53 +08:00
|
|
|
|
<AttachmentUpload
|
|
|
|
|
|
name="certAttachmentUrl"
|
|
|
|
|
|
label="证书附件"
|
|
|
|
|
|
maxCount={10}
|
|
|
|
|
|
accept=".jpg,.jpeg,.png,.gif,.bmp,.webp,.pdf"
|
|
|
|
|
|
extra="最多上传10个附件,支持jpg、jpeg、png、gif、bmp、webp、pdf格式"
|
|
|
|
|
|
/>
|
2026-07-07 13:54:55 +08:00
|
|
|
|
|
2026-07-07 09:07:19 +08:00
|
|
|
|
</Form>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
</Modal>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 09:07:19 +08:00
|
|
|
|
function ViewModal({ open, currentId, staffCertificateInfo, onCancel }) {
|
2026-06-23 18:07:30 +08:00
|
|
|
|
const [info, setInfo] = useState({});
|
|
|
|
|
|
const [detailLoading, setDetailLoading] = useState(false);
|
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!open || !currentId) {
|
|
|
|
|
|
setInfo({});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
|
(async () => {
|
|
|
|
|
|
setDetailLoading(true);
|
|
|
|
|
|
try {
|
2026-07-07 09:07:19 +08:00
|
|
|
|
const res = await staffCertificateInfo({ id: currentId });
|
|
|
|
|
|
if (cancelled || !res?.data) return;
|
2026-06-23 18:07:30 +08:00
|
|
|
|
const data = { ...res.data };
|
2026-07-07 09:07:19 +08:00
|
|
|
|
// 处理 certImgFiles
|
|
|
|
|
|
if (Array.isArray(data.certImgFiles)) {
|
|
|
|
|
|
data.certDisplayFiles = data.certImgFiles;
|
|
|
|
|
|
} else if (data.certAttachmentUrl) {
|
|
|
|
|
|
data.certDisplayFiles = data.certAttachmentUrl.split(",").filter(Boolean).map((url, i) => ({
|
|
|
|
|
|
uid: `-${i}`,
|
|
|
|
|
|
fileName: url.split("/").pop() || `证书附件${i + 1}`,
|
|
|
|
|
|
url,
|
|
|
|
|
|
}));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
data.certDisplayFiles = [];
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
2026-07-07 09:07:19 +08:00
|
|
|
|
setInfo(data);
|
|
|
|
|
|
} catch (err) {
|
2026-06-23 18:07:30 +08:00
|
|
|
|
console.warn("[StaffCertificate] load view failed:", err);
|
2026-07-07 09:07:19 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
if (!cancelled) setDetailLoading(false);
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
})();
|
2026-07-07 09:07:19 +08:00
|
|
|
|
return () => { cancelled = true; };
|
2026-06-23 18:07:30 +08:00
|
|
|
|
}, [open, currentId]);
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<Modal
|
|
|
|
|
|
open={open}
|
2026-07-06 18:06:49 +08:00
|
|
|
|
destroyOnHidden
|
2026-06-23 18:07:30 +08:00
|
|
|
|
title="证书详情"
|
|
|
|
|
|
width={800}
|
|
|
|
|
|
loading={detailLoading}
|
|
|
|
|
|
cancelText="返回"
|
|
|
|
|
|
okButtonProps={{ style: { display: "none" } }}
|
|
|
|
|
|
onCancel={onCancel}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Descriptions bordered column={1} labelStyle={{ width: 160 }}>
|
|
|
|
|
|
<Descriptions.Item label="证照名称">{info.certName}</Descriptions.Item>
|
2026-07-07 11:52:21 +08:00
|
|
|
|
<Descriptions.Item label="证书类型编码">{info.certTypeCode}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书类型名称">{info.certTypeName}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书类别编码">{info.certCategoryCode}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书类别名称">{info.certCategoryName}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书作业类别编码">{info.operationCategoryCode}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书作业类别名称">{info.operationCategoryName}</Descriptions.Item>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
<Descriptions.Item label="证书编号">{info.certNo}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="发证机关">{info.issueOrg}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书有效开始日期">{info.validStartDate}</Descriptions.Item>
|
|
|
|
|
|
<Descriptions.Item label="证书有效结束日期">{info.validEndDate}</Descriptions.Item>
|
2026-07-07 11:52:21 +08:00
|
|
|
|
<Descriptions.Item label="复核日期">{info.reviewDate}</Descriptions.Item>
|
2026-07-07 09:07:19 +08:00
|
|
|
|
<Descriptions.Item label="证书图片"><CertPreviewImg files={info.certDisplayFiles} /></Descriptions.Item>
|
2026-06-23 18:07:30 +08:00
|
|
|
|
</Descriptions>
|
|
|
|
|
|
</Modal>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-07 09:07:19 +08:00
|
|
|
|
export default Connect([NS_STAFF_CERTIFICATE], true)(AntdTableFuncControl(StaffCertificatePage));
|