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

304 lines
10 KiB
JavaScript
Raw Normal View History

2026-08-08 13:51:42 +08:00
import { Button, Descriptions, Image, Modal, Table } from "antd";
import { useEffect, useRef, useState } from "react";
2026-07-07 17:13:33 +08:00
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
2026-08-18 19:39:11 +08:00
import { getAgeByIdCard } from "~/utils";
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);
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;
};
}, [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 19:39:11 +08:00
<Descriptions.Item label="身份证号">
{info.idCardNo}
</Descriptions.Item>
<Descriptions.Item label="年龄">
{getAgeByIdCard(info.idCardNo) ?? "-"}
</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.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
}