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

277 lines
9.3 KiB
JavaScript
Raw Normal View History

import { Button, Modal, message } from "antd";
import { useEffect, useRef, useState } from "react";
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
2026-08-20 17:26:37 +08:00
import { fillDocxTemplate } from "~/utils/fillDocxTemplate";
import { ABILITY_SOURCE_MAP } from "~/enumerate/enterpriseOptions";
import {
CERT_LEVEL_LABEL_BY_TYPE,
CAPABILITY_MAP,
} from "~/enumerate/constant";
import "./index.less";
2026-08-21 11:17:29 +08:00
const RESUME_TEMPLATE_URL = "https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a87b61be4b063db1579e790.docx";
2026-08-20 17:26:37 +08:00
const GENDER_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 [];
}
};
const maskIdCard = (val) => {
if (!val || String(val).length < 8) return val || "-";
const s = String(val);
return `${s.slice(0, 14)}****`;
};
const maskPhone = (val) => {
if (!val || String(val).length < 7) return val || "-";
const s = String(val);
return `${s.slice(0, 3)}****${s.slice(-4)}`;
};
const display = (val) => {
if (val === 0) return "0";
if (val == null || val === "") return "-";
return val;
};
export default function StaffResumeModal({
open,
currentId,
requestDetails = fetchOrgPersonnelDetail,
onCancel,
}) {
const [info, setInfo] = useState({});
const [detailLoading, setDetailLoading] = useState(false);
const [downloading, setDownloading] = useState(false);
const requestDetailsRef = useRef(requestDetails);
requestDetailsRef.current = requestDetails;
useEffect(() => {
if (!open || !currentId) {
setInfo({});
return;
}
let cancelled = false;
setDetailLoading(true);
Promise.resolve(requestDetailsRef.current({ id: currentId }))
.catch((err) => {
console.warn("[StaffResumeModal] load detail failed:", err);
return { data: {} };
})
.then((detailRes) => {
if (!cancelled) {
setInfo(detailRes?.data || {});
}
})
.finally(() => {
if (!cancelled) {
setDetailLoading(false);
}
});
return () => {
cancelled = true;
};
}, [open, currentId]);
const evalCert = info.evaluatorCert;
const evalLevelMap = CERT_LEVEL_LABEL_BY_TYPE.evaluator;
const evalLevelName = evalLevelMap[evalCert?.certLevel] || "";
const qualificationText =
evalLevelName || evalCert?.certNo
? [evalLevelName, evalCert?.certNo].filter(Boolean).join(" / ")
: "-";
const regCert = info.registeredSafetyEngineerCert;
const regCertFiles = parseFileList(regCert?.certAttachmentUrl);
2026-08-21 11:17:29 +08:00
const isZyEducation = info.educationTypeName === "在职教育";
const educationText = [info.educationTypeName, info.educationLevelName]
.filter(Boolean)
.join("") || "-";
const schoolMajorText = [info.graduateSchool, info.basicDisciplineMajorName]
.filter(Boolean)
.join(" / ") || "-";
const capabilityText = Array.isArray(info.capabilityAssessment)
? info.capabilityAssessment
.map((item) => {
const name =
CAPABILITY_MAP[item.professionalCapabilityCode] ||
item.professionalCapabilityCode ||
"";
const source =
ABILITY_SOURCE_MAP[item.professionalCapabilitySourceCode] ||
item.professionalCapabilitySourceCode ||
"";
if (!name && !source) return "";
return [name, source].filter(Boolean).join(" / ");
})
.filter(Boolean)
.join("") || "-"
: "-";
const handleDownload = async () => {
setDownloading(true);
try {
2026-08-20 17:26:37 +08:00
const data = {
userName: display(info.userName),
gender: GENDER_MAP[info.genderCode] || "-",
birthDate: display(info.birthDate),
currentAddress: display(info.currentAddress),
officeAddress: display(info.officeAddress),
qualificationText,
certNo: display(regCert?.certNo),
idCardNo: maskIdCard(info.idCardNo),
phone: maskPhone(info.account),
2026-08-21 11:17:29 +08:00
// 全日制填 education/schoolMajor在职教育填 ZYeducation/ZYschoolMajor
education: isZyEducation ? "" : info.educationLevelName,
schoolMajor: isZyEducation ? "" : schoolMajorText,
ZYeducation: isZyEducation ? info.educationLevelName : "",
ZYschoolMajor: isZyEducation ? schoolMajorText : "",
2026-08-20 17:26:37 +08:00
publications: info.publications || "无",
capability: capabilityText,
certFiles:
regCertFiles
.map(
(item) =>
item.fileName || item.name || "查看注册安全工程师资格证",
)
.join("\n") || "-",
workExperience: display(info.workExperience),
};
await fillDocxTemplate(
RESUME_TEMPLATE_URL,
data,
`安全评价师简历表_${info.userName || currentId || ""}.docx`,
{ download: true },
);
message.success("简历下载成功");
} catch (err) {
2026-08-20 17:26:37 +08:00
console.warn("[StaffResumeModal] download word failed:", err);
message.error("简历下载失败");
} finally {
setDownloading(false);
}
};
return (
<Modal
open={open}
destroyOnHidden
2026-08-11 11:20:20 +08:00
title={'安全评价师简历表'}
width={860}
loading={detailLoading}
className="staff-resume-modal"
onCancel={onCancel}
footer={[
<Button key="download" type="primary" loading={downloading} onClick={handleDownload}>
下载简历
</Button>,
<Button key="close" onClick={onCancel}>
关闭
</Button>,
]}
>
2026-08-20 17:26:37 +08:00
<div style={{ background: "#fff", padding: 8 }}>
2026-08-11 11:20:20 +08:00
<table className="staff-resume-table">
<tbody>
<tr>
<td className="resume-label">姓名</td>
<td className="resume-value">{display(info.userName)}</td>
<td className="resume-label">性别</td>
<td className="resume-value">
{GENDER_MAP[info.genderCode] || "-"}
</td>
</tr>
<tr>
<td className="resume-label">出生日期</td>
<td className="resume-value">{display(info.birthDate)}</td>
<td className="resume-label">现住址</td>
<td className="resume-value">{display(info.currentAddress)}</td>
</tr>
<tr>
<td className="resume-label">办公地址</td>
<td className="resume-value">{display(info.officeAddress)}</td>
<td className="resume-label">职业资格等级及证书号码</td>
<td className="resume-value">{qualificationText}</td>
</tr>
<tr>
<td className="resume-label">注册安全工程师证书编号</td>
<td className="resume-value">{display(regCert?.certNo)}</td>
<td className="resume-label">身份证件号码</td>
<td className="resume-value">{maskIdCard(info.idCardNo)}</td>
</tr>
<tr>
<td className="resume-label">联系电话</td>
<td className="resume-value">{maskPhone(info.account)}</td>
<td className="resume-label">学历学位</td>
<td className="resume-value">{educationText}</td>
</tr>
<tr>
<td className="resume-label">毕业院校及专业</td>
<td className="resume-value">{schoolMajorText}</td>
<td className="resume-label">
出版学术专著专利科技发明及获奖情况
</td>
<td className="resume-value">
{info.publications ? info.publications : "无"}
</td>
</tr>
<tr>
<td className="resume-label-wide">
自我申报的专业能力及认定方式
</td>
<td className="resume-value-full" colSpan={3}>
{capabilityText}
</td>
</tr>
<tr>
<td className="resume-label-wide">申报专业能力证明材料</td>
<td className="resume-value-full" colSpan={3}>
{regCertFiles.length
? regCertFiles.map((item) => (
<div key={item.uid || item.url}>
<a
className="resume-link"
href={item.url}
target="_blank"
rel="noreferrer"
>
{item.fileName || item.name || "查看注册安全工程师资格证"}
</a>
</div>
))
: "-"}
</td>
</tr>
<tr>
<td className="resume-label-wide">主要学习工作经历</td>
<td className="resume-value-full" colSpan={3}>
{display(info.workExperience)}
</td>
</tr>
2026-08-10 18:14:55 +08:00
<tr>
<td className="resume-label">本人签字</td>
<td className="resume-sign"></td>
<td className="resume-label">从业机构确认</td>
<td className="resume-sign"></td>
</tr>
</tbody>
</table>
</div>
</Modal>
);
}