2026-08-08 13:51:42 +08:00
|
|
|
import { Button, Descriptions, Image, Modal, Table } from "antd";
|
2026-06-29 11:59:34 +08:00
|
|
|
import { useEffect, useRef, useState } from "react";
|
2026-07-07 17:13:33 +08:00
|
|
|
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
|
2026-08-03 10:28:20 +08:00
|
|
|
import {
|
2026-08-08 13:51:42 +08:00
|
|
|
REGISTER_ENGINEER_CATEGORY_MAP,
|
|
|
|
|
ABILITY_SOURCE_MAP,
|
2026-08-03 10:28:20 +08:00
|
|
|
TITLE_LEVEL_MAP,
|
2026-08-14 14:40:46 +08:00
|
|
|
QUALIFICATION_INDUSTRY_OPTIONS_MAP,
|
2026-08-03 10:28:20 +08:00
|
|
|
} from "~/enumerate/enterpriseOptions";
|
2026-08-08 13:51:42 +08:00
|
|
|
import {
|
|
|
|
|
CERT_LEVEL_LABEL_BY_TYPE,
|
|
|
|
|
CAPABILITY_MAP,
|
2026-08-08 16:44:49 +08:00
|
|
|
PROFESSIONAL_MAJOR_MAP,
|
2026-08-08 13:51:42 +08:00
|
|
|
} from "~/enumerate/constant";
|
2026-06-26 17:28:06 +08:00
|
|
|
|
|
|
|
|
const GENDER_MAP = { 1: "男", 2: "女" };
|
|
|
|
|
const REGISTER_ENGINEER_MAP = { 1: "是", 2: "否" };
|
2026-08-08 13:51:42 +08:00
|
|
|
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 [];
|
|
|
|
|
}
|
|
|
|
|
};
|
2026-06-26 17:28:06 +08:00
|
|
|
|
|
|
|
|
export default function StaffViewModal({
|
|
|
|
|
open,
|
|
|
|
|
currentId,
|
2026-07-02 16:58:07 +08:00
|
|
|
requestDetails = fetchOrgPersonnelDetail,
|
2026-06-26 17:28:06 +08:00
|
|
|
onCancel,
|
|
|
|
|
}) {
|
|
|
|
|
const [info, setInfo] = useState({});
|
|
|
|
|
const [detailLoading, setDetailLoading] = useState(false);
|
2026-06-29 11:59:34 +08:00
|
|
|
const requestDetailsRef = useRef(requestDetails);
|
|
|
|
|
requestDetailsRef.current = requestDetails;
|
2026-06-26 17:28:06 +08:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!open || !currentId) {
|
|
|
|
|
setInfo({});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
let cancelled = false;
|
|
|
|
|
setDetailLoading(true);
|
2026-07-07 09:07:19 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
Promise.resolve(requestDetailsRef.current({ id: currentId }))
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
console.warn("[StaffViewModal] load detail failed:", err);
|
|
|
|
|
return { data: {} };
|
|
|
|
|
})
|
|
|
|
|
.then((detailRes) => {
|
2026-07-08 14:44:41 +08:00
|
|
|
if (!cancelled) {
|
|
|
|
|
setInfo(detailRes?.data || {});
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.finally(() => {
|
|
|
|
|
if (!cancelled) {
|
|
|
|
|
setDetailLoading(false);
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-06-26 17:28:06 +08:00
|
|
|
return () => {
|
|
|
|
|
cancelled = true;
|
|
|
|
|
};
|
2026-06-29 11:59:34 +08:00
|
|
|
}, [open, currentId]);
|
2026-06-26 17:28:06 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
const proofMaterialFiles = parseFileList(info.proofMaterialUrl);
|
2026-06-26 17:28:06 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
const titleInfo = Array.isArray(info.titleCodeArr)
|
|
|
|
|
? info.titleCodeArr.map((item) => ({
|
|
|
|
|
titleName: TITLE_LEVEL_MAP[item.titleCode] || item.titleCode || "-",
|
2026-08-08 16:44:49 +08:00
|
|
|
industryName:
|
2026-08-18 19:29:45 +08:00
|
|
|
item.industryName,
|
2026-08-08 13:51:42 +08:00
|
|
|
proveUrlList: parseFileList(item.proveUrl),
|
|
|
|
|
}))
|
2026-07-08 14:44:41 +08:00
|
|
|
: [];
|
2026-07-07 15:27:25 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
const regCert = info.registeredSafetyEngineerCert;
|
|
|
|
|
const regCertLevelMap = CERT_LEVEL_LABEL_BY_TYPE.registered_safety_engineer;
|
|
|
|
|
const regCertFiles = parseFileList(regCert?.certAttachmentUrl);
|
2026-08-18 17:26:29 +08:00
|
|
|
const regPractisingFiles = parseFileList(regCert?.practisingCertUrl);
|
2026-08-08 13:51:42 +08:00
|
|
|
|
|
|
|
|
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) => ({
|
2026-08-14 14:40:46 +08:00
|
|
|
industryCode: item.industryCode,
|
2026-08-08 13:51:42 +08:00
|
|
|
capabilityName:
|
|
|
|
|
CAPABILITY_MAP[item.professionalCapabilityCode] ||
|
|
|
|
|
item.professionalCapabilityCode ||
|
|
|
|
|
"-",
|
|
|
|
|
sourceName:
|
|
|
|
|
ABILITY_SOURCE_MAP[item.professionalCapabilitySourceCode] ||
|
|
|
|
|
item.professionalCapabilitySourceCode ||
|
|
|
|
|
"-",
|
|
|
|
|
}))
|
2026-07-31 16:59:40 +08:00
|
|
|
: [];
|
|
|
|
|
|
|
|
|
|
const renderFileList = (files) => {
|
|
|
|
|
if (!files?.length) return "-";
|
|
|
|
|
return files.map((item) => {
|
|
|
|
|
const isImage = /\.(jpg|jpeg|png|gif|webp|bmp|svg)(\?.*)?$/i.test(
|
|
|
|
|
item.url,
|
|
|
|
|
);
|
|
|
|
|
return (
|
|
|
|
|
<div key={item.uid || item.url}>
|
|
|
|
|
{isImage ? (
|
|
|
|
|
<Image
|
|
|
|
|
src={item.url}
|
|
|
|
|
style={{ maxWidth: 150, cursor: "pointer" }}
|
|
|
|
|
preview={{ mask: item.fileName || item.name || "预览" }}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<Button type="link" onClick={() => window.open(item.url)}>
|
|
|
|
|
{item.fileName || item.name || item.url}
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-26 17:28:06 +08:00
|
|
|
return (
|
2026-08-08 13:51:42 +08:00
|
|
|
<Modal
|
|
|
|
|
open={open}
|
|
|
|
|
destroyOnHidden
|
|
|
|
|
title="查看"
|
|
|
|
|
width={900}
|
|
|
|
|
loading={detailLoading}
|
|
|
|
|
cancelText="关闭"
|
|
|
|
|
okButtonProps={{ style: { display: "none" } }}
|
|
|
|
|
onCancel={onCancel}
|
|
|
|
|
>
|
2026-08-18 17:26:29 +08:00
|
|
|
<div style={{ marginBottom: 8, fontWeight: 600 }}>基础信息</div>
|
|
|
|
|
<Descriptions bordered column={2} labelStyle={{ width: 120 }} size="small">
|
2026-08-08 13:51:42 +08:00
|
|
|
<Descriptions.Item label="姓名">{info.userName}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="性别">
|
|
|
|
|
{GENDER_MAP[info.genderCode]}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-18 17:26:29 +08:00
|
|
|
<Descriptions.Item label="本行业工作年限">
|
|
|
|
|
{info.workDateAge}
|
2026-08-08 13:51:42 +08:00
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="部门">{info.deptName}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="岗位">{info.postName}</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="身份证号">
|
|
|
|
|
{info.idCardNo}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="现住地址">
|
|
|
|
|
{info.currentAddress}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="办公地址">
|
|
|
|
|
{info.officeAddress}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-10 09:45:05 +08:00
|
|
|
<Descriptions.Item label="是否技术负责人">
|
|
|
|
|
{info.technicalDirectorFlag === true
|
|
|
|
|
? "是"
|
|
|
|
|
: info.technicalDirectorFlag === false
|
|
|
|
|
? "否"
|
|
|
|
|
: "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="是否过程控制负责人">
|
|
|
|
|
{info.processControlLeaderFlag === true
|
|
|
|
|
? "是"
|
|
|
|
|
: info.processControlLeaderFlag === false
|
|
|
|
|
? "否"
|
|
|
|
|
: "-"}
|
2026-08-08 13:51:42 +08:00
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="主要学习工作经历" span={2}>
|
|
|
|
|
{info.workExperience || "-"}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-11 14:00:14 +08:00
|
|
|
<Descriptions.Item label="出版学术专著专利获奖论文等" span={2}>
|
|
|
|
|
{info.publications || "-"}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-18 17:26:29 +08:00
|
|
|
</Descriptions>
|
|
|
|
|
|
|
|
|
|
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
|
|
|
|
专业能力信息
|
|
|
|
|
</div>
|
|
|
|
|
<Table
|
|
|
|
|
size="small"
|
|
|
|
|
bordered
|
|
|
|
|
pagination={false}
|
|
|
|
|
rowKey={(_, idx) => idx}
|
|
|
|
|
dataSource={capabilityList}
|
|
|
|
|
locale={{ emptyText: "暂无专业能力信息" }}
|
|
|
|
|
columns={[
|
|
|
|
|
{
|
|
|
|
|
title: "行业",
|
|
|
|
|
dataIndex: "industryCode",
|
|
|
|
|
width: 220,
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
render: (code) =>
|
|
|
|
|
QUALIFICATION_INDUSTRY_OPTIONS_MAP[code] || code || "-",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "专业能力",
|
|
|
|
|
dataIndex: "capabilityName",
|
|
|
|
|
width: 80,
|
|
|
|
|
ellipsis: true,
|
|
|
|
|
},
|
|
|
|
|
{ title: "来源", dataIndex: "sourceName", width: 280 },
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
|
|
|
|
学历信息
|
|
|
|
|
</div>
|
|
|
|
|
<Descriptions bordered column={2} labelStyle={{ width: 120 }} size="small">
|
|
|
|
|
<Descriptions.Item label="学历类型">
|
|
|
|
|
{info.educationTypeName || "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="毕业院校">
|
|
|
|
|
{info.graduateSchool}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="学历层次">
|
|
|
|
|
{info.educationLevelName || "-"}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
<Descriptions.Item label="基础学科专业">
|
|
|
|
|
{info.basicDisciplineMajorName || "-"}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-08 13:51:42 +08:00
|
|
|
<Descriptions.Item label="学历证明材料" span={2}>
|
|
|
|
|
{renderFileList(proofMaterialFiles)}
|
|
|
|
|
</Descriptions.Item>
|
|
|
|
|
</Descriptions>
|
2026-08-03 10:28:20 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
|
|
|
|
注安信息
|
|
|
|
|
</div>
|
2026-08-18 17:26:29 +08:00
|
|
|
<Descriptions bordered column={2} labelStyle={{ width: 120 }} size="small">
|
2026-08-08 13:51:42 +08:00
|
|
|
<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>
|
2026-08-18 17:26:29 +08:00
|
|
|
<Descriptions.Item label="执业证书编号">
|
|
|
|
|
{regCert?.practisingCertCode || "-"}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-08 13:51:42 +08:00
|
|
|
<Descriptions.Item label="注安证书" span={2}>
|
|
|
|
|
{renderFileList(regCertFiles)}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-18 17:26:29 +08:00
|
|
|
<Descriptions.Item label="注安执业证书" span={2}>
|
|
|
|
|
{renderFileList(regPractisingFiles)}
|
|
|
|
|
</Descriptions.Item>
|
2026-08-08 13:51:42 +08:00
|
|
|
</Descriptions>
|
2026-08-03 10:28:20 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
|
|
|
|
评价师信息
|
|
|
|
|
</div>
|
2026-08-18 17:26:29 +08:00
|
|
|
<Descriptions bordered column={2} labelStyle={{ width: 120 }} size="small">
|
2026-08-08 13:51:42 +08:00
|
|
|
<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>
|
2026-06-26 17:28:06 +08:00
|
|
|
|
2026-08-08 13:51:42 +08:00
|
|
|
<div style={{ marginTop: 16, marginBottom: 8, fontWeight: 600 }}>
|
|
|
|
|
职称信息
|
|
|
|
|
</div>
|
|
|
|
|
<Table
|
|
|
|
|
size="small"
|
|
|
|
|
bordered
|
|
|
|
|
pagination={false}
|
|
|
|
|
rowKey={(_, idx) => idx}
|
|
|
|
|
dataSource={titleInfo}
|
|
|
|
|
columns={[
|
|
|
|
|
{ title: "职称", dataIndex: "titleName", width: 120 },
|
2026-08-18 19:29:45 +08:00
|
|
|
{ title: "系列/专业", dataIndex: "industryName", width: 150 },
|
2026-08-08 13:51:42 +08:00
|
|
|
{
|
|
|
|
|
title: "证明材料",
|
|
|
|
|
dataIndex: "proveUrlList",
|
|
|
|
|
render: renderFileList,
|
|
|
|
|
},
|
|
|
|
|
]}
|
2026-06-26 17:28:06 +08:00
|
|
|
/>
|
2026-08-08 13:51:42 +08:00
|
|
|
</Modal>
|
2026-06-26 17:28:06 +08:00
|
|
|
);
|
2026-07-08 14:44:41 +08:00
|
|
|
}
|