feat
parent
9908a0093a
commit
942fc4a55f
|
|
@ -13,9 +13,9 @@ module.exports = {
|
|||
// API_HOST: "https://gbs-gateway.qhdsafety.com",
|
||||
|
||||
// API_HOST: "http://192.168.0.134",
|
||||
// API_HOST: "http://192.168.0.150", //太浅
|
||||
API_HOST: "http://192.168.0.150", //太浅
|
||||
// API_HOST: "http://192.168.0.152",
|
||||
API_HOST: "http://192.168.0.103", //huwei
|
||||
//API_HOST: "http://192.168.0.103", //huwei
|
||||
},
|
||||
production: {
|
||||
// 应用后端分支名称,部署上线需要
|
||||
|
|
|
|||
|
|
@ -1,72 +1,59 @@
|
|||
import { Button, Descriptions, Image, Modal, Table, Upload } from "antd";
|
||||
import { Button, Descriptions, Image, Modal, Table } from "antd";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import FilePreviewModal, {
|
||||
FilePreviewContent,
|
||||
} from "~/components/FilePreviewModal";
|
||||
import { resolvePreviewSrc } from "~/components/CertPreviewImg";
|
||||
import {
|
||||
fetchStaffCertDetail,
|
||||
fetchStaffCertListByPersonnelId,
|
||||
} from "~/api/staffCertificate";
|
||||
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
|
||||
import {
|
||||
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
|
||||
REGISTER_ENGINEER_CATEGORY_MAP,
|
||||
PERSONNEL_POST_TYPE_MAP,
|
||||
ABILITY_SOURCE_MAP,
|
||||
TITLE_LEVEL_MAP,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import {
|
||||
CERT_LEVEL_LABEL_BY_TYPE,
|
||||
CAPABILITY_MAP,
|
||||
} from "~/enumerate/constant";
|
||||
|
||||
const GENDER_MAP = { 1: "男", 2: "女" };
|
||||
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
||||
const EVALUATOR_MAP = { 1: "是", 2: "否" };
|
||||
|
||||
const parseFileList = (raw) => {
|
||||
if (!raw) return [];
|
||||
if (Array.isArray(raw)) return raw;
|
||||
try {
|
||||
const parsed = JSON.parse(raw);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
export default function StaffViewModal({
|
||||
open,
|
||||
currentId,
|
||||
requestDetails = fetchOrgPersonnelDetail,
|
||||
requestCertList = fetchStaffCertListByPersonnelId,
|
||||
requestCertDetails = fetchStaffCertDetail,
|
||||
onCancel,
|
||||
}) {
|
||||
const [info, setInfo] = useState({});
|
||||
const [certificates, setCertificates] = useState([]);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
const [attachmentPreview, setAttachmentPreview] = useState(null);
|
||||
const [certPreview, setCertPreview] = useState(null);
|
||||
const [certPreviewLoading, setCertPreviewLoading] = useState(false);
|
||||
const [certPreviewInfo, setCertPreviewInfo] = useState(null);
|
||||
const requestDetailsRef = useRef(requestDetails);
|
||||
const requestCertListRef = useRef(requestCertList);
|
||||
const requestCertDetailsRef = useRef(requestCertDetails);
|
||||
requestDetailsRef.current = requestDetails;
|
||||
requestCertListRef.current = requestCertList;
|
||||
requestCertDetailsRef.current = requestCertDetails;
|
||||
|
||||
useEffect(() => {
|
||||
if (!open || !currentId) {
|
||||
setInfo({});
|
||||
setCertificates([]);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setDetailLoading(true);
|
||||
|
||||
const loadCertList = requestCertListRef.current;
|
||||
Promise.all([
|
||||
Promise.resolve(requestDetailsRef.current({ id: currentId })).catch(
|
||||
(err) => {
|
||||
Promise.resolve(requestDetailsRef.current({ id: currentId }))
|
||||
.catch((err) => {
|
||||
console.warn("[StaffViewModal] load detail failed:", err);
|
||||
return { data: {} };
|
||||
},
|
||||
),
|
||||
typeof loadCertList === "function"
|
||||
? Promise.resolve(loadCertList(currentId)).catch((err) => {
|
||||
console.warn("[StaffViewModal] load certificates failed:", err);
|
||||
return [];
|
||||
})
|
||||
: Promise.resolve([]),
|
||||
])
|
||||
.then(([detailRes, certList]) => {
|
||||
.then((detailRes) => {
|
||||
if (!cancelled) {
|
||||
setInfo(detailRes?.data || {});
|
||||
setCertificates(Array.isArray(certList) ? certList : []);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
|
|
@ -79,51 +66,35 @@ export default function StaffViewModal({
|
|||
};
|
||||
}, [open, currentId]);
|
||||
|
||||
const openCertPreview = async (record) => {
|
||||
setCertPreview(record);
|
||||
setCertPreviewInfo(null);
|
||||
if (typeof requestCertDetailsRef.current !== "function") {
|
||||
setCertPreviewInfo(record);
|
||||
return;
|
||||
}
|
||||
setCertPreviewLoading(true);
|
||||
try {
|
||||
const res = await requestCertDetailsRef.current({ id: record.id });
|
||||
setCertPreviewInfo(res?.data || record);
|
||||
} catch (err) {
|
||||
console.warn("[StaffViewModal] load cert detail failed:", err);
|
||||
setCertPreviewInfo(record);
|
||||
} finally {
|
||||
setCertPreviewLoading(false);
|
||||
}
|
||||
};
|
||||
const proofMaterialFiles = parseFileList(info.proofMaterialUrl);
|
||||
|
||||
const proofMaterialUrl = info.proofMaterialUrl
|
||||
? JSON.parse(info.proofMaterialUrl)
|
||||
const titleInfo = Array.isArray(info.titleCodeArr)
|
||||
? info.titleCodeArr.map((item) => ({
|
||||
titleName: TITLE_LEVEL_MAP[item.titleCode] || item.titleCode || "-",
|
||||
proveUrlList: parseFileList(item.proveUrl),
|
||||
}))
|
||||
: [];
|
||||
|
||||
const titleCodeArr = Array.isArray(info.titleCodeArr)
|
||||
? info.titleCodeArr.map((item) => {
|
||||
let proveUrlList = [];
|
||||
if (item.proveUrl) {
|
||||
try {
|
||||
proveUrlList =
|
||||
typeof item.proveUrl === "string"
|
||||
? JSON.parse(item.proveUrl)
|
||||
: item.proveUrl;
|
||||
} catch {
|
||||
proveUrlList = [];
|
||||
}
|
||||
}
|
||||
return {
|
||||
industryName:
|
||||
QUALIFICATION_INDUSTRY_OPTIONS_MAP[item.industryCode] ||
|
||||
item.industryCode ||
|
||||
const regCert = info.registeredSafetyEngineerCert;
|
||||
const regCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.registered_safety_engineer;
|
||||
const regCertFiles = parseFileList(regCert?.certAttachmentUrl);
|
||||
|
||||
const evaluatorCert = info.evaluatorCert;
|
||||
const evaluatorFlag = info.evaluatorFlag || (evaluatorCert ? 1 : 2);
|
||||
const evalCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.evaluator;
|
||||
const evalCertFiles = parseFileList(evaluatorCert?.certAttachmentUrl);
|
||||
|
||||
const capabilityList = Array.isArray(info.capabilityAssessment)
|
||||
? info.capabilityAssessment.map((item) => ({
|
||||
capabilityName:
|
||||
CAPABILITY_MAP[item.professionalCapabilityCode] ||
|
||||
item.professionalCapabilityCode ||
|
||||
"-",
|
||||
titleName: TITLE_LEVEL_MAP[item.titleCode] || item.titleCode || "-",
|
||||
proveUrlList,
|
||||
};
|
||||
})
|
||||
sourceName:
|
||||
ABILITY_SOURCE_MAP[item.professionalCapabilitySourceCode] ||
|
||||
item.professionalCapabilitySourceCode ||
|
||||
"-",
|
||||
}))
|
||||
: [];
|
||||
|
||||
const renderFileList = (files) => {
|
||||
|
|
@ -150,17 +121,7 @@ export default function StaffViewModal({
|
|||
});
|
||||
};
|
||||
|
||||
const certPreviewFiles = certPreviewInfo?.certImgs?.length
|
||||
? certPreviewInfo.certImgs
|
||||
: certPreviewInfo?.certImgFiles || [];
|
||||
const certPreviewUrl = resolvePreviewSrc(certPreviewFiles[0]);
|
||||
const certPreviewName =
|
||||
certPreviewFiles[0]?.fileName ||
|
||||
certPreviewFiles[0]?.name ||
|
||||
certPreviewInfo?.certName;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
open={open}
|
||||
destroyOnHidden
|
||||
|
|
@ -206,67 +167,56 @@ export default function StaffViewModal({
|
|||
<Descriptions.Item label="基础学科专业">
|
||||
{info.basicDisciplineMajorName || "-"}
|
||||
</Descriptions.Item>
|
||||
|
||||
<Descriptions.Item label="是否注册安全工程师">
|
||||
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="是否技术负责人">
|
||||
{info.technicalDirectorFlag === true
|
||||
? "是"
|
||||
: info.technicalDirectorFlag === false
|
||||
? "否"
|
||||
: "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="是否过程控制负责人">
|
||||
{info.processControlLeaderFlag === true
|
||||
? "是"
|
||||
: info.processControlLeaderFlag === false
|
||||
? "否"
|
||||
: "-"}
|
||||
</Descriptions.Item>
|
||||
|
||||
|
||||
<Descriptions.Item
|
||||
label="出版学术专著、专利、获奖、发表学术论文等"
|
||||
span={2}
|
||||
>
|
||||
{info.publications || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="自我申报的专业能力及认定方式" span={2}>
|
||||
{info.abilityDeclaration || "-"}
|
||||
<Descriptions.Item label="人员岗位类型">
|
||||
{PERSONNEL_POST_TYPE_MAP[info.personnelPositionType] || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="主要学习工作经历" span={2}>
|
||||
{info.workExperience || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="学历证明材料" span={2}>
|
||||
{proofMaterialUrl.length
|
||||
? proofMaterialUrl.map((item) => {
|
||||
const isImage =
|
||||
/\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i.test(item.url);
|
||||
return (
|
||||
<div key={item.id}>
|
||||
{isImage ? (
|
||||
<Image
|
||||
src={item.url}
|
||||
style={{ maxWidth: 200, cursor: "pointer" }}
|
||||
preview={{
|
||||
mask: item.fileName || item.name || "预览",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() => window.open(item.url)}
|
||||
>
|
||||
{item.fileName || item.name || item.url}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: "-"}
|
||||
{renderFileList(proofMaterialFiles)}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
||||
注安信息
|
||||
</div>
|
||||
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
|
||||
<Descriptions.Item label="是否注册安全工程师">
|
||||
{REGISTER_ENGINEER_MAP[info.registerEngineerFlag] ?? "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="注安专业类别">
|
||||
{REGISTER_ENGINEER_CATEGORY_MAP[regCert?.certCategoryCode] || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="注安等级">
|
||||
{regCertLevelMap[regCert?.certLevel] || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">
|
||||
{regCert?.certNo || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="注安证书" span={2}>
|
||||
{renderFileList(regCertFiles)}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
||||
评价师信息
|
||||
</div>
|
||||
<Descriptions bordered column={2} labelStyle={{ width: 120 }}>
|
||||
<Descriptions.Item label="是否评价师">
|
||||
{EVALUATOR_MAP[evaluatorFlag] ?? "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="评价师等级">
|
||||
{evalCertLevelMap[evaluatorCert?.certLevel] || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">
|
||||
{evaluatorCert?.certNo || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="评价师证书" span={2}>
|
||||
{renderFileList(evalCertFiles)}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
||||
职称信息
|
||||
</div>
|
||||
|
|
@ -275,10 +225,9 @@ export default function StaffViewModal({
|
|||
bordered
|
||||
pagination={false}
|
||||
rowKey={(_, idx) => idx}
|
||||
dataSource={titleCodeArr}
|
||||
dataSource={titleInfo}
|
||||
columns={[
|
||||
{ title: "行业", dataIndex: "industryName", width: 150 },
|
||||
{ title: "职称", dataIndex: "titleName", width: 100 },
|
||||
{ title: "职称", dataIndex: "titleName", width: 120 },
|
||||
{
|
||||
title: "证明材料",
|
||||
dataIndex: "proveUrlList",
|
||||
|
|
@ -288,120 +237,20 @@ export default function StaffViewModal({
|
|||
/>
|
||||
|
||||
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
||||
证照信息
|
||||
专业能力信息
|
||||
</div>
|
||||
<Table
|
||||
size="small"
|
||||
bordered
|
||||
rowKey="id"
|
||||
pagination={false}
|
||||
dataSource={certificates}
|
||||
locale={{ emptyText: "暂无证照信息" }}
|
||||
rowKey={(_, idx) => idx}
|
||||
dataSource={capabilityList}
|
||||
locale={{ emptyText: "暂无专业能力信息" }}
|
||||
columns={[
|
||||
{ title: "证照名称", dataIndex: "certName", ellipsis: true },
|
||||
{ title: "证书编号", dataIndex: "certNo", ellipsis: true },
|
||||
{ title: "证书类别", dataIndex: "certTypeName", ellipsis: true },
|
||||
{ title: "发证机关", dataIndex: "issueOrg", ellipsis: true },
|
||||
{ title: "有效开始日期", dataIndex: "validStartDate", width: 120 },
|
||||
{ title: "有效结束日期", dataIndex: "validEndDate", width: 120 },
|
||||
{
|
||||
title: "操作",
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => openCertPreview(record)}
|
||||
>
|
||||
查看证书
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
{ title: "专业能力", dataIndex: "capabilityName" },
|
||||
{ title: "来源", dataIndex: "sourceName", width: 280 },
|
||||
]}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
<FilePreviewModal
|
||||
open={!!attachmentPreview}
|
||||
title="附件预览"
|
||||
fileName={attachmentPreview?.fileName}
|
||||
url={attachmentPreview?.url}
|
||||
onCancel={() => setAttachmentPreview(null)}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
open={!!certPreview}
|
||||
destroyOnHidden
|
||||
title="证书预览"
|
||||
width={720}
|
||||
loading={certPreviewLoading}
|
||||
cancelText="关闭"
|
||||
okText={certPreviewUrl ? "新窗口打开" : "关闭"}
|
||||
okButtonProps={{
|
||||
style: certPreviewUrl ? undefined : { display: "none" },
|
||||
}}
|
||||
onCancel={() => {
|
||||
setCertPreview(null);
|
||||
setCertPreviewInfo(null);
|
||||
}}
|
||||
onOk={() => {
|
||||
if (certPreviewUrl) {
|
||||
window.open(certPreviewUrl, "_blank");
|
||||
}
|
||||
setCertPreview(null);
|
||||
setCertPreviewInfo(null);
|
||||
}}
|
||||
>
|
||||
{certPreviewInfo && (
|
||||
<div>
|
||||
<Descriptions
|
||||
bordered
|
||||
column={1}
|
||||
size="small"
|
||||
style={{ marginBottom: 12 }}
|
||||
>
|
||||
<Descriptions.Item label="证照名称">
|
||||
{certPreviewInfo.certName}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="证书编号">
|
||||
{certPreviewInfo.certNo}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="证书类别">
|
||||
{certPreviewInfo.certTypeName || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="发证机关">
|
||||
{certPreviewInfo.issueOrg || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="有效开始日期">
|
||||
{certPreviewInfo.validStartDate || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="有效结束日期">
|
||||
{certPreviewInfo.validEndDate || "-"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="查看证书">
|
||||
{certPreviewInfo.certAttachmentUrl &&
|
||||
certPreviewInfo.certAttachmentUrl.split(",").map((item) => {
|
||||
const isImage =
|
||||
/\.(png|jpe?g|gif|bmp|webp|svg)(\?|#|$)/i.test(item);
|
||||
return (
|
||||
<div key={item} style={{ marginBottom: 4 }}>
|
||||
{isImage ? (
|
||||
<Image
|
||||
src={item}
|
||||
width={100}
|
||||
style={{ cursor: "pointer" }}
|
||||
/>
|
||||
) : (
|
||||
<a onClick={() => window.open(item)}>{item}</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,50 @@ export const REGISTER_ENGINEER_OPTIONS = [
|
|||
{ label: "否", value: 2 },
|
||||
];
|
||||
|
||||
/** 注安专业类别 */
|
||||
export const REGISTER_ENGINEER_CATEGORY_OPTIONS = [
|
||||
{ label: "煤矿安全", value: "COAL_MINE" },
|
||||
{ label: "金属非金属矿山安全", value: "METAL_NONMETAL_MINE" },
|
||||
{ label: "化工安全", value: "CHEMICAL" },
|
||||
{ label: "金属冶炼安全", value: "METAL_SMELTING" },
|
||||
{ label: "建筑施工安全", value: "CONSTRUCTION" },
|
||||
{ label: "道路运输安全", value: "ROAD_TRANSPORT" },
|
||||
{ label: "其他安全", value: "OTHER" },
|
||||
];
|
||||
export const REGISTER_ENGINEER_CATEGORY_MAP =
|
||||
REGISTER_ENGINEER_CATEGORY_OPTIONS.reduce(
|
||||
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
|
||||
{},
|
||||
);
|
||||
|
||||
/** 人员岗位类型 */
|
||||
export const PERSONNEL_POST_TYPE_OPTIONS = [
|
||||
{ label: "技术负责人", value: "TECHNICAL_LEADER" },
|
||||
{ label: "过程控制负责人", value: "PROCESS_CONTROL_LEADER" },
|
||||
];
|
||||
export const PERSONNEL_POST_TYPE_MAP = PERSONNEL_POST_TYPE_OPTIONS.reduce(
|
||||
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
|
||||
{},
|
||||
);
|
||||
|
||||
/** 能力来源 */
|
||||
export const ABILITY_SOURCE_OPTIONS = [
|
||||
{ label: "注册安全工程师", value: "REGISTERED_SAFETY_ENGINEER" },
|
||||
{ label: "学历", value: "EDUCATION" },
|
||||
{ label: "职称", value: "PROFESSIONAL_TITLE" },
|
||||
{ label: "出版学术专著、专利、获奖、发表学术论文等", value: "ACADEMIC_ACHIEVEMENT" },
|
||||
];
|
||||
export const ABILITY_SOURCE_MAP = ABILITY_SOURCE_OPTIONS.reduce(
|
||||
(acc, cur) => ({ ...acc, [cur.value]: cur.label }),
|
||||
{},
|
||||
);
|
||||
|
||||
/** 是否评价师 */
|
||||
export const EVALUATOR_OPTIONS = [
|
||||
{ label: "是", value: 1 },
|
||||
{ label: "否", value: 2 },
|
||||
];
|
||||
|
||||
/** 职称等级 */
|
||||
export const TITLE_LEVEL_OPTIONS = [
|
||||
{ value: "SENIOR", label: "高级" },
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
Table,
|
||||
TreeSelect,
|
||||
Upload,
|
||||
Divider,
|
||||
} from "antd";
|
||||
import { InboxOutlined } from "@ant-design/icons";
|
||||
import { Get } from "@cqsjjb/jjb-common-lib/http";
|
||||
|
|
@ -29,14 +30,19 @@ import { NS_STAFF_INFO, NS_ORG_DEPARTMENT } from "~/enumerate/namespace";
|
|||
import {
|
||||
EDUCATION_LEVEL_OPTIONS,
|
||||
EDUCATION_TYPE_OPTIONS,
|
||||
QUALIFICATION_INDUSTRY_OPTIONS,
|
||||
REGISTER_ENGINEER_OPTIONS,
|
||||
REGISTER_ENGINEER_CATEGORY_OPTIONS,
|
||||
PERSONNEL_POST_TYPE_OPTIONS,
|
||||
ABILITY_SOURCE_OPTIONS,
|
||||
EVALUATOR_OPTIONS,
|
||||
TITLE_LEVEL_OPTIONS,
|
||||
} from "~/enumerate/enterpriseOptions";
|
||||
import { CERT_LEVEL_OPTIONS_BY_TYPE, CAPABILITY_OPTIONS } from "~/enumerate/constant";
|
||||
import { getBirthDateFromIdCard } from "~/utils";
|
||||
import { idCardRule, mobileRule } from "~/utils/validators";
|
||||
import AttachmentUpload from "~/components/AttachmentUpload";
|
||||
import StaffViewModal from "~/components/StaffViewModal";
|
||||
import "./index.less";
|
||||
|
||||
const { router } = tools;
|
||||
const API_HOST = window.process?.env?.app?.API_HOST || "";
|
||||
|
|
@ -218,7 +224,7 @@ function PersonnelInfoPage(props) {
|
|||
width: 260,
|
||||
dataIndex: "abilityNames",
|
||||
ellipsis: true,
|
||||
render: (text) => text || '暂无',
|
||||
render: (text) => text || "暂无",
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
|
|
@ -245,16 +251,7 @@ function PersonnelInfoPage(props) {
|
|||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
setCurrentId(record.id);
|
||||
goCertificate(record.id, record.userName);
|
||||
}}
|
||||
>
|
||||
证书能力
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
|
|
@ -361,6 +358,11 @@ function StaffFormModal({
|
|||
const [deptPositionOptions, setDeptPositionOptions] = useState([]);
|
||||
const [positionLoading, setPositionLoading] = useState(false);
|
||||
const watchedDeptId = Form.useWatch("deptId", form);
|
||||
const watchedRegisterEngineerFlag = Form.useWatch(
|
||||
"registerEngineerFlag",
|
||||
form,
|
||||
);
|
||||
const watchedEvaluatorFlag = Form.useWatch("evaluatorFlag", form);
|
||||
const wasOpenRef = useRef(false);
|
||||
const inflightDeptRef = useRef(null);
|
||||
|
||||
|
|
@ -396,7 +398,6 @@ function StaffFormModal({
|
|||
};
|
||||
|
||||
const loadBasicDisciplineMajors = async () => {
|
||||
|
||||
await queryBasicDisciplineMajor({ pageSize: 999 });
|
||||
};
|
||||
|
||||
|
|
@ -438,21 +439,37 @@ function StaffFormModal({
|
|||
setFields.proofMaterialUrl = JSON.parse(setFields.proofMaterialUrl);
|
||||
}
|
||||
|
||||
if (Array.isArray(setFields.titleCodeArr)) {
|
||||
setFields.titleCodeArr = setFields.titleCodeArr.map((item) => {
|
||||
if (item.proveUrl && typeof item.proveUrl === "string") {
|
||||
const parseCertAttachment = (cert) => {
|
||||
if (cert?.certAttachmentUrl) {
|
||||
try {
|
||||
return { ...item, proveUrl: JSON.parse(item.proveUrl) };
|
||||
cert.certAttachmentUrl = JSON.parse(cert.certAttachmentUrl);
|
||||
} catch {}
|
||||
}
|
||||
};
|
||||
|
||||
if (setFields.registeredSafetyEngineerCert) {
|
||||
parseCertAttachment(setFields.registeredSafetyEngineerCert);
|
||||
}
|
||||
|
||||
if (setFields.evaluatorCert) {
|
||||
setFields.evaluatorFlag = 1;
|
||||
parseCertAttachment(setFields.evaluatorCert);
|
||||
}
|
||||
|
||||
if (Array.isArray(setFields.titleCodeArr) && setFields.titleCodeArr[0]) {
|
||||
const first = setFields.titleCodeArr[0];
|
||||
setFields.titleCode = first.titleCode;
|
||||
if (first.proveUrl) {
|
||||
try {
|
||||
setFields.proveUrl = JSON.parse(first.proveUrl);
|
||||
} catch {
|
||||
return item;
|
||||
setFields.proveUrl = first.proveUrl;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
delete setFields.titleCodeArr;
|
||||
}
|
||||
|
||||
form.setFieldsValue(setFields);
|
||||
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setDetailLoading(false);
|
||||
|
|
@ -488,6 +505,15 @@ function StaffFormModal({
|
|||
const handleSubmit = async (values) => {
|
||||
try {
|
||||
setSubmitting(true);
|
||||
const stringifyCert = (cert) =>
|
||||
cert
|
||||
? {
|
||||
...cert,
|
||||
certAttachmentUrl: Array.isArray(cert.certAttachmentUrl)
|
||||
? JSON.stringify(cert.certAttachmentUrl)
|
||||
: undefined,
|
||||
}
|
||||
: undefined;
|
||||
const payload = {
|
||||
...values,
|
||||
genderName:
|
||||
|
|
@ -499,13 +525,28 @@ function StaffFormModal({
|
|||
proofMaterialUrl: Array.isArray(values.proofMaterialUrl)
|
||||
? JSON.stringify(values.proofMaterialUrl)
|
||||
: undefined,
|
||||
titleCodeArr: Array.isArray(values.titleCodeArr)
|
||||
? values.titleCodeArr.map((item) => ({
|
||||
...item,
|
||||
proveUrl: Array.isArray(item.proveUrl)
|
||||
? JSON.stringify(item.proveUrl)
|
||||
: item.proveUrl,
|
||||
}))
|
||||
titleCodeArr: [
|
||||
{
|
||||
titleCode: values.titleCode,
|
||||
proveUrl: Array.isArray(values.proveUrl)
|
||||
? JSON.stringify(values.proveUrl)
|
||||
: undefined,
|
||||
},
|
||||
],
|
||||
registeredSafetyEngineerCert:
|
||||
values.registerEngineerFlag === 1
|
||||
? stringifyCert(values.registeredSafetyEngineerCert)
|
||||
: undefined,
|
||||
evaluatorCert:
|
||||
values.evaluatorFlag === 1
|
||||
? stringifyCert(values.evaluatorCert)
|
||||
: undefined,
|
||||
capabilityAssessment: Array.isArray(values.capabilityAssessment)
|
||||
? values.capabilityAssessment.filter(
|
||||
(item) =>
|
||||
item?.professionalCapabilityCode ||
|
||||
item?.professionalCapabilitySourceCode,
|
||||
)
|
||||
: undefined,
|
||||
};
|
||||
if (payload.birthDate && typeof payload.birthDate !== "string") {
|
||||
|
|
@ -515,7 +556,6 @@ function StaffFormModal({
|
|||
payload.joinWorkDate = payload.joinWorkDate.format("YYYY-MM-DD");
|
||||
}
|
||||
if (currentId) payload.id = currentId;
|
||||
delete payload.proofMaterials;
|
||||
|
||||
const request = currentId ? staffInfoEdit : staffInfoAdd;
|
||||
const res = await request(payload);
|
||||
|
|
@ -540,14 +580,6 @@ function StaffFormModal({
|
|||
confirmLoading={submitting}
|
||||
onCancel={handleCancel}
|
||||
onOk={() => {
|
||||
const titleArr = form.getFieldValue("titleCodeArr") || [];
|
||||
const hasTitleInfo = titleArr.some(
|
||||
(item) => item?.industryCode && item?.titleCode,
|
||||
);
|
||||
if (!hasTitleInfo) {
|
||||
message.warning("请添加职称信息");
|
||||
return;
|
||||
}
|
||||
form.submit();
|
||||
}}
|
||||
>
|
||||
|
|
@ -586,7 +618,11 @@ function StaffFormModal({
|
|||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="birthDate" label="出生日期" rules={[{ required: true, message: "请选择出生日期" }]}>
|
||||
<Form.Item
|
||||
name="birthDate"
|
||||
label="出生日期"
|
||||
rules={[{ required: true, message: "请选择出生日期" }]}
|
||||
>
|
||||
<DatePicker style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
|
@ -745,7 +781,21 @@ function StaffFormModal({
|
|||
maxCount={5}
|
||||
/>
|
||||
</Col>
|
||||
|
||||
<Col span={12}>
|
||||
<Form.Item name="personnelPositionType" label="人员岗位类型">
|
||||
<Select
|
||||
placeholder="请选择人员岗位类型"
|
||||
allowClear
|
||||
options={PERSONNEL_POST_TYPE_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<div className="register-engineer-section">
|
||||
<div className="register-engineer-section-title">注安信息</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="registerEngineerFlag"
|
||||
|
|
@ -758,39 +808,163 @@ function StaffFormModal({
|
|||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{watchedRegisterEngineerFlag === 1 && (
|
||||
<>
|
||||
<Col span={12}>
|
||||
<Form.Item name="technicalDirectorFlag" label="是否技术负责人">
|
||||
<Form.Item
|
||||
name={["registeredSafetyEngineerCert", "certCategoryCode"]}
|
||||
label="注安专业类别"
|
||||
rules={[{ required: true, message: "请选择注安专业类别" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
placeholder="请选择注安专业类别"
|
||||
allowClear
|
||||
options={[
|
||||
{ label: "是", value: true },
|
||||
{ label: "否", value: false },
|
||||
]}
|
||||
options={REGISTER_ENGINEER_CATEGORY_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="processControlLeaderFlag"
|
||||
label="是否过程控制负责人"
|
||||
name={["registeredSafetyEngineerCert", "certLevel"]}
|
||||
label="注安等级"
|
||||
rules={[{ required: true, message: "请选择注安等级" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
placeholder="请选择注安等级"
|
||||
allowClear
|
||||
options={[
|
||||
{ label: "是", value: true },
|
||||
{ label: "否", value: false },
|
||||
]}
|
||||
options={TITLE_LEVEL_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={["registeredSafetyEngineerCert", "certNo"]}
|
||||
label="证书编号"
|
||||
rules={[
|
||||
{ required: true, message: "请输入证书编号" },
|
||||
{ max: 50, message: "证书编号不能超过50个字符" },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入证书编号" maxLength={50} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="职称信息" required>
|
||||
<AttachmentUpload
|
||||
name={["registeredSafetyEngineerCert", "certAttachmentUrl"]}
|
||||
label="注安证书"
|
||||
extra="最多上传10个pdf,doc,docx,图片格式文件"
|
||||
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
|
||||
maxCount={10}
|
||||
rules={[{ required: true, message: "请上传注安证书" }]}
|
||||
/>
|
||||
</Col>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<div className="register-engineer-section">
|
||||
<div className="register-engineer-section-title">
|
||||
评价师信息
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="evaluatorFlag"
|
||||
label="是否评价师"
|
||||
initialValue={2}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
options={EVALUATOR_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{watchedEvaluatorFlag === 1 && (
|
||||
<>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={["evaluatorCert", "certLevel"]}
|
||||
label="评价师等级"
|
||||
rules={[{ required: true, message: "请选择评价师等级" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择评价师等级"
|
||||
allowClear
|
||||
options={CERT_LEVEL_OPTIONS_BY_TYPE.evaluator}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name={["evaluatorCert", "certNo"]}
|
||||
label="证书编号"
|
||||
rules={[
|
||||
{ required: true, message: "请输入证书编号" },
|
||||
{ max: 50, message: "证书编号不能超过50个字符" },
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入证书编号" maxLength={50} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<AttachmentUpload
|
||||
name={["evaluatorCert", "certAttachmentUrl"]}
|
||||
label="评价师证书"
|
||||
extra="最多上传10个pdf,doc,docx,图片格式文件"
|
||||
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
|
||||
maxCount={10}
|
||||
rules={[{ required: true, message: "请上传评价师证书" }]}
|
||||
/>
|
||||
</Col>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={16}>
|
||||
<Col span={24}>
|
||||
<div className="register-engineer-section">
|
||||
<div className="register-engineer-section-title">
|
||||
职称信息
|
||||
</div>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
name="titleCode"
|
||||
label="职称"
|
||||
rules={[{ required: true, message: "请选择职称" }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择职称"
|
||||
allowClear
|
||||
options={TITLE_LEVEL_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<AttachmentUpload
|
||||
name="proveUrl"
|
||||
label="证明材料"
|
||||
extra="最多上传1个pdf,doc,docx,图片格式文件"
|
||||
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
|
||||
maxCount={1}
|
||||
rules={[{ required: true, message: "请上传证明材料" }]}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item label="专业能力信息">
|
||||
<Form.List
|
||||
name="titleCodeArr"
|
||||
name="capabilityAssessment"
|
||||
initialValue={[{}]}
|
||||
rules={[{ required: true, message: "请添加职称信息" }]}
|
||||
>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
|
|
@ -800,60 +974,38 @@ function StaffFormModal({
|
|||
pagination={false}
|
||||
size="small"
|
||||
bordered
|
||||
locale={{ emptyText: "暂无职称信息,点击下方按钮添加" }}
|
||||
columns={[
|
||||
{
|
||||
title: "行业",
|
||||
width: 200,
|
||||
title: "专业能力",
|
||||
render: (_, field) => (
|
||||
<Form.Item
|
||||
name={[field.name, "industryCode"]}
|
||||
name={[field.name, "professionalCapabilityCode"]}
|
||||
style={{ marginBottom: 0 }}
|
||||
rules={[
|
||||
{ required: true, message: "请选择行业" },
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择行业"
|
||||
placeholder="请选择专业能力"
|
||||
allowClear
|
||||
options={QUALIFICATION_INDUSTRY_OPTIONS}
|
||||
options={CAPABILITY_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "职称",
|
||||
width: 120,
|
||||
title: "来源",
|
||||
width: 280,
|
||||
render: (_, field) => (
|
||||
<Form.Item
|
||||
name={[field.name, "titleCode"]}
|
||||
name={[field.name, "professionalCapabilitySourceCode"]}
|
||||
style={{ marginBottom: 0 }}
|
||||
rules={[
|
||||
{ required: true, message: "请选择职称" },
|
||||
]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择职称"
|
||||
placeholder="请选择来源"
|
||||
allowClear
|
||||
options={TITLE_LEVEL_OPTIONS}
|
||||
options={ABILITY_SOURCE_OPTIONS}
|
||||
/>
|
||||
</Form.Item>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "证明材料",
|
||||
render: (_, field) => (
|
||||
<AttachmentUpload
|
||||
name={[field.name, "proveUrl"]}
|
||||
rules={[
|
||||
{ required: true, message: "请上传证明材料" },
|
||||
]}
|
||||
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
|
||||
maxCount={1}
|
||||
style={{ marginBottom: 0 }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 80,
|
||||
|
|
@ -871,47 +1023,21 @@ function StaffFormModal({
|
|||
},
|
||||
]}
|
||||
/>
|
||||
{fields.length < 2 && (
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add()}
|
||||
block
|
||||
style={{ marginTop: 12 }}
|
||||
>
|
||||
添加职称信息
|
||||
添加专业能力信息
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
name="publications"
|
||||
label="出版学术专著、专利、获奖、发表学术论文等"
|
||||
rules={[{ max: 500, message: "不能超过1000个字符" }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
placeholder="请输入"
|
||||
maxLength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item
|
||||
name="abilityDeclaration"
|
||||
label="自我申报的专业能力及认定方式"
|
||||
rules={[{ max: 500, message: "不能超过1000个字符" }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
placeholder="请输入"
|
||||
maxLength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item name="workExperience" label="主要学习工作经历">
|
||||
<Input.TextArea
|
||||
|
|
@ -1073,7 +1199,12 @@ function StaffImportModal({ open, onCancel, onSuccess }) {
|
|||
);
|
||||
}
|
||||
|
||||
function ResetPasswordModal({ open, currentId, staffInfoResetPassword, onCancel }) {
|
||||
function ResetPasswordModal({
|
||||
open,
|
||||
currentId,
|
||||
staffInfoResetPassword,
|
||||
onCancel,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
.register-engineer-section {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 8px;
|
||||
padding: 16px 16px 0;
|
||||
margin-bottom: 24px;
|
||||
|
||||
&-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue