diff --git a/package.json b/package.json index 3af002d..d3ff1f4 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "docxtemplater": "latest", "echarts": "^6.1.0", "history": "^4.10.1", - "html2canvas": "^1.4.1", - "jspdf": "^2.5.2", + + "lodash-es": "^4.17.21", "pizzip": "latest", "react": "^18.2.0", diff --git a/public/安全评价师简历表模板.docx b/public/安全评价师简历表模板.docx new file mode 100644 index 0000000..6b1b6a3 --- /dev/null +++ b/public/安全评价师简历表模板.docx @@ -0,0 +1,35 @@ + 姓名 + {userName} + 性别 + {gender} + 出生日期 + {birthDate} + 现住址 + {currentAddress} + 办公地址 + {officeAddress} + 职业资格等级及证书号码 + {qualificationText} + 注册安全工程师证书编号 + {certNo} + 身份证件号码 + {idCardNo} + 联系电话 + {phone} + 学历学位 + {education} + 毕业院校及专业 + {schoolMajor} + 出版学术专著、专利、科技发明及获奖情况 + {publications} + 自我申报的专业能力及认定方式 + {capability} + 申报专业能力证明材料 + {certFiles} + 主要学习工作经历 + {workExperience} + 本人签字 + + 从业机构确认 + + diff --git a/public/评价人员任命及专家聘用书(空白模板).docx b/public/评价人员任命及专家聘用书(空白模板).docx index 2b940e1..c307cb5 100644 --- a/public/评价人员任命及专家聘用书(空白模板).docx +++ b/public/评价人员任命及专家聘用书(空白模板).docx @@ -5,3 +5,4 @@ 部门经理或负责人: 年 月 日 + diff --git a/src/components/StaffResumeModal/index.js b/src/components/StaffResumeModal/index.js index 2ebc5b5..b1c8d8f 100644 --- a/src/components/StaffResumeModal/index.js +++ b/src/components/StaffResumeModal/index.js @@ -1,8 +1,7 @@ import { Button, Modal, message } from "antd"; import { useEffect, useRef, useState } from "react"; -import html2canvas from "html2canvas"; -import { jsPDF } from "jspdf"; import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper"; +import { fillDocxTemplate } from "~/utils/fillDocxTemplate"; import { ABILITY_SOURCE_MAP } from "~/enumerate/enterpriseOptions"; import { CERT_LEVEL_LABEL_BY_TYPE, @@ -10,6 +9,8 @@ import { } from "~/enumerate/constant"; import "./index.less"; +const RESUME_TEMPLATE_URL = "/安全评价师简历表模板.docx"; + const GENDER_MAP = { 1: "男", 2: "女" }; const parseFileList = (raw) => { @@ -50,7 +51,6 @@ export default function StaffResumeModal({ const [info, setInfo] = useState({}); const [detailLoading, setDetailLoading] = useState(false); const [downloading, setDownloading] = useState(false); - const resumeRef = useRef(null); const requestDetailsRef = useRef(requestDetails); requestDetailsRef.current = requestDetails; @@ -120,45 +120,40 @@ export default function StaffResumeModal({ : "-"; const handleDownload = async () => { - if (!resumeRef.current) return; setDownloading(true); try { - const canvas = await html2canvas(resumeRef.current, { - scale: 2, - useCORS: true, - backgroundColor: "#ffffff", - logging: false, - }); - const imgData = canvas.toDataURL("image/jpeg", 0.95); - const pdf = new jsPDF({ - orientation: "portrait", - unit: "mm", - format: "a4", - }); - const pageWidth = pdf.internal.pageSize.getWidth(); - const pageHeight = pdf.internal.pageSize.getHeight(); - const margin = 10; - const contentWidth = pageWidth - margin * 2; - const contentHeight = (canvas.height * contentWidth) / canvas.width; - - let heightLeft = contentHeight; - let position = margin; - - pdf.addImage(imgData, "JPEG", margin, position, contentWidth, contentHeight); - heightLeft -= pageHeight - margin * 2; - - while (heightLeft > 0) { - position = margin - (contentHeight - heightLeft); - pdf.addPage(); - pdf.addImage(imgData, "JPEG", margin, position, contentWidth, contentHeight); - heightLeft -= pageHeight - margin * 2; - } - - const fileName = `安全评价师简历表_${info.userName || currentId || ""}.pdf`; - pdf.save(fileName); + 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), + education: educationText, + schoolMajor: schoolMajorText, + 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) { - console.warn("[StaffResumeModal] download pdf failed:", err); + console.warn("[StaffResumeModal] download word failed:", err); message.error("简历下载失败"); } finally { setDownloading(false); @@ -183,7 +178,7 @@ export default function StaffResumeModal({ , ]} > -
+
diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js index a96407c..5645875 100644 --- a/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js +++ b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js @@ -22,6 +22,12 @@ import { tools } from "@cqsjjb/jjb-common-lib"; import { EVAL_TYPE_MAP, ROLE_DEFINITIONS_MAP } from "~/enumerate/constant"; import { PROJECT_TYPE_MAP } from "~/enumerate/constant"; import WpsEditor from "~/pages/Container/WpsEditor"; +import RiskAnalysisContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent"; +import InternalReviewContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/InternalReviewContent"; +import TechnicalReviewContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/TechnicalReviewContent"; +import ControlContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent"; +import ArchiveContent from "~/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent"; +import "~/pages/Container/SafetyEvalBusiness/ProjectFlow/index.less"; import "./index.less"; const { router } = tools; @@ -453,6 +459,36 @@ const EvalProjectMonitorDetail = (props) => { ); }; + /** 可锁定 tab 的标签:前置未完成时展示警告态 */ + const renderLockableTabLabel = (label, done, locked) => ( + + {label} + + {locked ? "前置未完成" : done ? "已完成" : "待办理"} + + + ); + + const NODE_TYPES_BEFORE_CONTROL = [ + "RISK_ANALYSIS", + "BUSINESS_CONTRACT", + "PROJECT_GROUP", + "SITE_SURVEY", + "REPORT_PREPARATION", + "INTERNAL_REVIEW", + "TECHNICAL_REVIEW", + ]; + // 过程控制:前 7 个节点全部完成才解锁 + const controlLocked = !NODE_TYPES_BEFORE_CONTROL.every( + (type) => nodeMap[type]?.state === 1, + ); + // 项目归档:前 8 个节点(含过程控制)全部完成才解锁 + const archiveLocked = + controlLocked || nodeMap["PROCESS_CONTROL"]?.state !== 1; + const projectInfo = (
@@ -501,6 +537,11 @@ const EvalProjectMonitorDetail = (props) => { setActiveKey(key); }} items={[ + { + key: "RISK_ANALYSIS", + label: renderTabLabel("风险分析", "RISK_ANALYSIS"), + children: , + }, { key: "BUSINESS_CONTRACT", label: renderTabLabel("项目合同", "BUSINESS_CONTRACT"), @@ -530,6 +571,36 @@ const EvalProjectMonitorDetail = (props) => { label: renderTabLabel("报告编制", "REPORT_PREPARATION"), children: null, }, + { + key: "INTERNAL_REVIEW", + label: renderTabLabel("内部审核", "INTERNAL_REVIEW"), + children: , + }, + { + key: "TECHNICAL_REVIEW", + label: renderTabLabel("技术审核", "TECHNICAL_REVIEW"), + children: , + }, + { + key: "PROCESS_CONTROL", + label: renderLockableTabLabel( + "过程控制", + nodeMap["PROCESS_CONTROL"]?.state === 1, + controlLocked, + ), + disabled: controlLocked, + children: , + }, + { + key: "ARCHIVE", + label: renderLockableTabLabel( + "项目归档", + evalProjectDetailData?.archiveFlag === 1, + archiveLocked, + ), + disabled: archiveLocked, + children: , + }, ]} /> {wpsVisible && (