safety-eval-service-frontend/src/components/PersonInChargeList/index.js

666 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space } from "antd";
import { useEffect, useRef, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import Search from "zy-react-library/components/Search";
import DictionarySelect from "zy-react-library/components/Select/Dictionary";
import PersonnelSelect from "zy-react-library/components/Select/Personnel/Gwj";
import Table from "zy-react-library/components/Table";
import TooltipPreviewImg from "zy-react-library/components/TooltipPreviewImg";
import Upload from "zy-react-library/components/Upload";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import { UPLOAD_FILE_TYPE_ENUM } from "zy-react-library/enum/uploadFile/gwj";
import useDeleteFile from "zy-react-library/hooks/useDeleteFile";
import useGetFile from "zy-react-library/hooks/useGetFile";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import useGetUserInfo from "zy-react-library/hooks/useGetUserInfo";
import useTable from "zy-react-library/hooks/useTable";
import useUploadFile from "zy-react-library/hooks/useUploadFile";
import { NS_USER_CERTIFICATE } from "~/enumerate/namespace";
import { useDebounce } from "~/utils";
import { useLocation } from 'react-router-dom';
const JOB_STATUS = {
1: "在职",
0: "离职",
2: "信息变更中",
3: "未入职",
4: "实习生",
5: "实习结束",
6: "退休",
7: "劳务派遣",
8: "劳务派遣结束",
11: "入职待审核",
10: "离职待审核",
};
function List({ props, certificatePhotoType, displayType, personnelType, permissionAdd, permissionEdit, permissionView, permissionDel, dictionaryType }) {
const [addModalOpen, setAddModalOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const queryParams = useGetUrlQuery();
const { loading: getFileLoading, getFile } = useGetFile();
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["userCertificateList"], {
form,
transform: (formData) => {
return {
...formData,
eqCorpinfoId: queryParams["corpinfoId"],
eqType: personnelType,
};
},
});
const onDelete = async (row) => {
props["projectHasUser"]({ eqUserId: row.userId }).then((res) => {
console.log(res);
if (res.data && res.data.length > 0) {
const arr = [];
const userArr = [];
res.data.forEach((item) => {
arr.push(item.projectName);
userArr.push(item.userName);
});
Modal.confirm({
title: "提示",
content: (
<div>
<div>
{[...new Set(userArr)].join(",")}
有正在进行的项目包含
{
[...new Set(arr)].join(",")
}
确定删除吗
</div>
<div style={{ fontSize: 14, color: "red" }}>删除可能会对正在进行的项目造成异常状态请谨慎操作</div>
</div>
),
onOk: () => {
props["userCertificateRemove"]({
id: row.id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
},
});
}
else {
Modal.confirm({
title: "提示",
content: "确定删除吗?",
onOk: () => {
props["userCertificateRemove"]({
id: row.id,
}).then((res) => {
if (res.success) {
message.success("删除成功");
getData();
}
});
},
});
}
});
};
const [fileCache, setFileCache] = useState({});
const [loadingKeys, setLoadingKeys] = useState(new Set());
const pendingLoadIdsRef = useRef(new Set());
const requestQueueRef = useRef([]);
const activeRequestsRef = useRef(0);
const MAX_CONCURRENT_REQUESTS = 3; // 最大并发请求数
const processQueue = () => {
while (
requestQueueRef.current.length > 0
&& activeRequestsRef.current < MAX_CONCURRENT_REQUESTS
) {
const { id, resolve, reject } = requestQueueRef.current.shift();
activeRequestsRef.current++;
getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[certificatePhotoType],
eqForeignKey: id,
})
.then((res) => {
setFileCache(prev => ({
...prev,
[id]: res || [],
}));
resolve(res);
})
.catch((err) => {
console.error("图片加载失败:", err);
reject(err);
})
.finally(() => {
activeRequestsRef.current--;
setLoadingKeys((prev) => {
const newSet = new Set(prev);
newSet.delete(id);
return newSet;
});
processQueue();
});
}
};
const loadFileForRecord = (userQualificationinfoId) => {
if (!userQualificationinfoId)
return Promise.resolve();
if (fileCache[userQualificationinfoId])
return Promise.resolve();
if (loadingKeys.has(userQualificationinfoId))
return Promise.resolve();
if (pendingLoadIdsRef.current.has(userQualificationinfoId))
return Promise.resolve();
pendingLoadIdsRef.current.add(userQualificationinfoId);
setLoadingKeys(prev => new Set([...prev, userQualificationinfoId]));
return new Promise((resolve, reject) => {
requestQueueRef.current.push({ id: userQualificationinfoId, resolve, reject });
processQueue();
}).finally(() => {
pendingLoadIdsRef.current.delete(userQualificationinfoId);
});
};
// 清除伪类
useEffect(() => {
if (displayType === "View") {
const style = document.createElement("style");
style.innerHTML = `
.search-layout::after {
content: none !important;
display: none !important;
}
`;
document.head.appendChild(style);
// 清理函数,在组件卸载时移除样式
return () => {
document.head.removeChild(style);
};
}
}, []);
// 缓存数据变化时清空图片缓存
useEffect(() => {
if (tableProps.dataSource) {
const currentIds = new Set(tableProps.dataSource.map(item => item.userCertificateId).filter(Boolean));
setFileCache((prev) => {
const newCache = {};
Object.keys(prev).forEach((id) => {
if (currentIds.has(id)) {
newCache[id] = prev[id];
}
});
return newCache;
});
}
}, [tableProps.dataSource]);
// 记录已经触发过加载的 ID避免 render 重复触发
const triggeredLoadIdsRef = useRef(new Set());
useEffect(() => {
if (tableProps.dataSource) {
tableProps.dataSource.forEach((record) => {
const id = record.userCertificateId;
if (id && !triggeredLoadIdsRef.current.has(id)) {
triggeredLoadIdsRef.current.add(id);
loadFileForRecord(id);
}
});
}
// 组件卸载或数据源变化时重置
return () => {
triggeredLoadIdsRef.current.clear();
};
}, [tableProps.dataSource]);
return (
<PageLayout>
<Search
form={form}
options={[
{
name: "likeUserName",
label: "姓名",
},
{
name: "likePostName",
label: "岗位名称",
},
]}
onFinish={getData}
/>
<Table
loding={getFileLoading}
options={displayType !== "View"}
toolBarRender={() => (
<>
{
(displayType !== "View" && props.permission(permissionAdd))
&& (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setAddModalOpen(true);
}}
>
新增
</Button>
)
}
</>
)}
columns={[
{
title: "姓名",
dataIndex: "name",
},
{
title: "证书名称",
dataIndex: "certificateName",
},
{
title: "证书编号",
dataIndex: "certificateCode",
},
{
title: "岗位名称",
dataIndex: "postName",
},
{
title: "有效期至",
dataIndex: "certificateNo",
width: 280,
render: (_, record) =>
<div>{`${record.certificateDateStart ?? ""} - ${record.certificateDateEnd ?? ""}`}</div>,
},
{
title: "就职状态",
dataIndex: "certificateNo",
render: (_, record) => (
<div>{JOB_STATUS[record.employmentStatus] || "-"}</div>
),
},
{
title: "图片",
dataIndex: "name",
render: (_, record) => {
const id = record.userCertificateId;
const files = fileCache[id] || [];
if (!files.length)
return <span></span>;
return <TooltipPreviewImg files={files} />;
},
},
{
title: "操作",
width: 200,
render: (_, record) => (
<Space>
{
props.permission(permissionView)
&& (
<Button
type="link"
onClick={() => props.history.push(`./View?id=${record.id}`)}
>
查看
</Button>
)
}
{
(displayType !== "View" && props.permission(permissionEdit))
&& (
<Button
type="link"
onClick={() => {
setAddModalOpen(true);
setCurrentId(record.id);
}}
>
编辑
</Button>
)
}
{
(displayType !== "View" && props.permission(permissionDel))
&& (
<Button
danger
type="link"
onClick={() => onDelete(record)}
>
删除
</Button>
)
}
</Space>
),
},
]}
{...tableProps}
/>
{addModalOpen && (
<AddModal
open={addModalOpen}
loding={props.userCertificate.userCertificateLoading}
getData={getData}
currentId={currentId}
requestAdd={props["userCertificateAdd"]}
requestEdit={props["userCertificateEdit"]}
requestDetails={props["userCertificateInfo"]}
userCertificateIsExistCertNo={props["userCertificateIsExistCertNo"]}
getUserlistAll={props["getUserlistAll"]}
certificatePhotoType={certificatePhotoType}
personnelType={personnelType}
dictionaryType={dictionaryType}
onCancel={() => {
setAddModalOpen(false);
setCurrentId("");
}}
onSuccess={(userQualificationinfoId) => {
// 清除该记录的图片缓存,强制下次 render 时重新加载
setFileCache((prev) => {
const newCache = { ...prev };
delete newCache[userQualificationinfoId];
return newCache;
});
}}
/>
)}
</PageLayout>
);
}
function AddModalComponent(props) {
const [form] = Form.useForm();
const [userQualificationinfoId, setUserQualificationinfoId] = useState("");
const [userObj, setUserObj] = useState({});
const { loading: deleteFileLoading, deleteFile } = useDeleteFile();
const { loading: uploadFileLoading, uploadFile } = useUploadFile();
const { loading: getFileLoading, getFile } = useGetFile();
const { getUserInfo } = useGetUserInfo();
const [deleteCardImageFiles, setDeleteCardImageFiles] = useState([]);
const [CertificateCodeValue, setPertificateCodeValue] = useState(null);
const debouncedCertificateCodeValue = useDebounce(CertificateCodeValue, 100);
const [personnelData, setPersonnelData] = useState([]);
const [isSubmit, setIsSubmit] = useState(true);
const location = useLocation();
const getUserInfoFun = async () => {
const userInfo = await getUserInfo();
setUserObj(userInfo);
};
const getUserlistFun = async () => {
props.getUserlistAll({menuPath:"/certificate"+location.pathname}).then(res => {
if(res.data) {
res.data.forEach(item => {
item.bianma=item.id;
})
setPersonnelData(res.data)
}
})
};
useEffect(() => {
if (props.currentId) {
const fetchData = async () => {
const { data } = await props.requestDetails({
id: props.currentId,
});
const certificateImgs = await getFile({
eqType: UPLOAD_FILE_TYPE_ENUM[props.certificatePhotoType],
eqForeignKey: data.userCertificateId,
});
data.certificateDate = [data.certificateDateStart, data.certificateDateEnd];
data.certificateImgs = certificateImgs;
form.setFieldsValue(data);
setUserQualificationinfoId(data.userCertificateId);
};
fetchData();
}
getUserInfoFun();
getUserlistFun()
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
const onSubmit = async (values) => {
if (!isSubmit) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
return;
}
if (values.certificateImgs.length !== 2) {
message.error("证照照片必须上传正反面两张");
return;
}
const maxSizeInBytes = 10 * 1024 * 1024;
const isOverSize = values.certificateImgs.some(file => file.size > maxSizeInBytes);
if (isOverSize) {
message.error("选择的图片不能大于10MB");
return;
}
await deleteFile({
single: false,
files: deleteCardImageFiles,
});
const { id } = await uploadFile({
single: false,
files: values.certificateImgs,
params: {
type: UPLOAD_FILE_TYPE_ENUM[props.certificatePhotoType],
foreignKey: userQualificationinfoId,
},
});
values.certificateDateStart = values.certificateDate[0];
values.certificateDateEnd = values.certificateDate[1];
values.type = props.personnelType;
const personnelTypeMap = {
tezhongzuoye: "特种作业人员",
tzsbczry: "特种设备操作人员",
zyfzr: "主要负责人",
aqscglry: "安全生产管理人员",
};
values.typeName = personnelTypeMap[props.personnelType];
if (props.currentId) {
values.id = props.currentId;
values.userCertificateId = userQualificationinfoId;
await props.requestEdit(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
props.onSuccess(userQualificationinfoId);
message.success("编辑成功");
}
});
}
else {
values.userCertificateId = id;
await props.requestAdd(values).then((res) => {
if (res.success) {
onCancel();
props.getData();
props.onSuccess(userQualificationinfoId);
message.success("新增成功");
}
});
}
};
// 校验重复
useEffect(() => {
if (!debouncedCertificateCodeValue) {
form.setFields([
{
name: "certificateCode",
errors: [],
},
]);
return;
}
props["userCertificateIsExistCertNo"]({
certNo: debouncedCertificateCodeValue,
id: props.currentId ?? "",
}).then((res) => {
if (res.data) {
form.setFields([
{
name: "certificateCode",
errors: ["证书编号重复"],
},
]);
setIsSubmit(false);
}
else {
setIsSubmit(true);
}
});
}, [debouncedCertificateCodeValue]);
const onValuesChange = (changedValues) => {
if ("certificateCode" in changedValues) {
setPertificateCodeValue(changedValues.certificateCode ?? "");
}
};
return (
<Modal
open={props.open}
maskClosable={false}
title={props.currentId ? "编辑" : "新增"}
width={800}
confirmLoading={
deleteFileLoading || uploadFileLoading || getFileLoading || props.loding
}
onOk={form.submit}
onCancel={onCancel}
>
<FormBuilder
form={form}
onValuesChange={onValuesChange}
span={24}
values={{
securityFlag: 0,
}}
options={[
{
name: "userId",
label: "选择人员",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items:personnelData
},
{ name: "userName", label: "人员名称", onlyForLabel: true },
{
name: "certificateName",
label: "证书名称",
},
{
name: "certificateCode",
label: "证书编号",
},
{
name: "postId",
label: "岗位名称",
render: (
<DictionarySelect
dictValue={props.dictionaryType}
onGetLabel={label => form.setFieldValue("postName", label)}
/>
),
},
{ name: "postName", label: "岗位名称", onlyForLabel: true },
{
name: "issuingAuthority",
label: "发证机构",
},
{
name: "dateIssue",
label: "发证日期",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
{
name: "certificateDate",
label: "有效期",
render: FORM_ITEM_RENDER_ENUM.DATE_RANGE,
rules: [
{
validator: (_, value) => {
const allEmptyStrings = Array.isArray(value) && value.every(item => item === "");
if (allEmptyStrings) {
return Promise.reject(new Error("请选择有效期"));
}
return Promise.resolve();
},
},
],
},
{
name: "certificateImgs",
label: "证照照片",
render: (
<Upload
maxCount={2}
onGetRemoveFile={(file) => {
setDeleteCardImageFiles([...deleteCardImageFiles, file]);
}}
tipContent={(
<div
style={{
lineHeight: 1.6,
color: "red",
fontSize: 12,
}}
>
<div>
温馨提示用户要上传证照照片正反面证照照片数量是2张单张大小不超过10MB支持jpgjpegpng格式
</div>
</div>
)}
/>
),
},
]}
labelCol={{
span: 10,
}}
showActionButtons={false}
onFinish={onSubmit}
/>
</Modal>
);
}
const AddModal = AddModalComponent;
export default Connect([NS_USER_CERTIFICATE], true)(Permission(List));