dev-tmp1
tangjie 2026-08-20 17:26:37 +08:00
parent 15847d7933
commit b8dfddebe5
5 changed files with 107 additions and 41 deletions

View File

@ -32,8 +32,8 @@
"docxtemplater": "latest", "docxtemplater": "latest",
"echarts": "^6.1.0", "echarts": "^6.1.0",
"history": "^4.10.1", "history": "^4.10.1",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"pizzip": "latest", "pizzip": "latest",
"react": "^18.2.0", "react": "^18.2.0",

View File

@ -0,0 +1,35 @@
姓名
{userName}
性别
{gender}
出生日期
{birthDate}
现住址
{currentAddress}
办公地址
{officeAddress}
职业资格等级及证书号码
{qualificationText}
注册安全工程师证书编号
{certNo}
身份证件号码
{idCardNo}
联系电话
{phone}
学历学位
{education}
毕业院校及专业
{schoolMajor}
出版学术专著、专利、科技发明及获奖情况
{publications}
自我申报的专业能力及认定方式
{capability}
申报专业能力证明材料
{certFiles}
主要学习工作经历
{workExperience}
本人签字
从业机构确认

View File

@ -5,3 +5,4 @@
部门经理或负责人: 部门经理或负责人:
年 月 日 年 月 日

View File

@ -1,8 +1,7 @@
import { Button, Modal, message } from "antd"; import { Button, Modal, message } from "antd";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import html2canvas from "html2canvas";
import { jsPDF } from "jspdf";
import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper"; import { fetchOrgPersonnelDetail } from "~/utils/qualFiling/personnelHelper";
import { fillDocxTemplate } from "~/utils/fillDocxTemplate";
import { ABILITY_SOURCE_MAP } from "~/enumerate/enterpriseOptions"; import { ABILITY_SOURCE_MAP } from "~/enumerate/enterpriseOptions";
import { import {
CERT_LEVEL_LABEL_BY_TYPE, CERT_LEVEL_LABEL_BY_TYPE,
@ -10,6 +9,8 @@ import {
} from "~/enumerate/constant"; } from "~/enumerate/constant";
import "./index.less"; import "./index.less";
const RESUME_TEMPLATE_URL = "/安全评价师简历表模板.docx";
const GENDER_MAP = { 1: "男", 2: "女" }; const GENDER_MAP = { 1: "男", 2: "女" };
const parseFileList = (raw) => { const parseFileList = (raw) => {
@ -50,7 +51,6 @@ export default function StaffResumeModal({
const [info, setInfo] = useState({}); const [info, setInfo] = useState({});
const [detailLoading, setDetailLoading] = useState(false); const [detailLoading, setDetailLoading] = useState(false);
const [downloading, setDownloading] = useState(false); const [downloading, setDownloading] = useState(false);
const resumeRef = useRef(null);
const requestDetailsRef = useRef(requestDetails); const requestDetailsRef = useRef(requestDetails);
requestDetailsRef.current = requestDetails; requestDetailsRef.current = requestDetails;
@ -120,45 +120,40 @@ export default function StaffResumeModal({
: "-"; : "-";
const handleDownload = async () => { const handleDownload = async () => {
if (!resumeRef.current) return;
setDownloading(true); setDownloading(true);
try { try {
const canvas = await html2canvas(resumeRef.current, { const data = {
scale: 2, userName: display(info.userName),
useCORS: true, gender: GENDER_MAP[info.genderCode] || "-",
backgroundColor: "#ffffff", birthDate: display(info.birthDate),
logging: false, currentAddress: display(info.currentAddress),
}); officeAddress: display(info.officeAddress),
const imgData = canvas.toDataURL("image/jpeg", 0.95); qualificationText,
const pdf = new jsPDF({ certNo: display(regCert?.certNo),
orientation: "portrait", idCardNo: maskIdCard(info.idCardNo),
unit: "mm", phone: maskPhone(info.account),
format: "a4", education: educationText,
}); schoolMajor: schoolMajorText,
const pageWidth = pdf.internal.pageSize.getWidth(); publications: info.publications || "无",
const pageHeight = pdf.internal.pageSize.getHeight(); capability: capabilityText,
const margin = 10; certFiles:
const contentWidth = pageWidth - margin * 2; regCertFiles
const contentHeight = (canvas.height * contentWidth) / canvas.width; .map(
(item) =>
let heightLeft = contentHeight; item.fileName || item.name || "查看注册安全工程师资格证",
let position = margin; )
.join("\n") || "-",
pdf.addImage(imgData, "JPEG", margin, position, contentWidth, contentHeight); workExperience: display(info.workExperience),
heightLeft -= pageHeight - margin * 2; };
await fillDocxTemplate(
while (heightLeft > 0) { RESUME_TEMPLATE_URL,
position = margin - (contentHeight - heightLeft); data,
pdf.addPage(); `安全评价师简历表_${info.userName || currentId || ""}.docx`,
pdf.addImage(imgData, "JPEG", margin, position, contentWidth, contentHeight); { download: true },
heightLeft -= pageHeight - margin * 2; );
}
const fileName = `安全评价师简历表_${info.userName || currentId || ""}.pdf`;
pdf.save(fileName);
message.success("简历下载成功"); message.success("简历下载成功");
} catch (err) { } catch (err) {
console.warn("[StaffResumeModal] download pdf failed:", err); console.warn("[StaffResumeModal] download word failed:", err);
message.error("简历下载失败"); message.error("简历下载失败");
} finally { } finally {
setDownloading(false); setDownloading(false);
@ -183,7 +178,7 @@ export default function StaffResumeModal({
</Button>, </Button>,
]} ]}
> >
<div ref={resumeRef} style={{ background: "#fff", padding: 8 }}> <div style={{ background: "#fff", padding: 8 }}>
<table className="staff-resume-table"> <table className="staff-resume-table">
<tbody> <tbody>

View File

@ -22,6 +22,12 @@ import { tools } from "@cqsjjb/jjb-common-lib";
import { EVAL_TYPE_MAP, ROLE_DEFINITIONS_MAP } from "~/enumerate/constant"; import { EVAL_TYPE_MAP, ROLE_DEFINITIONS_MAP } from "~/enumerate/constant";
import { PROJECT_TYPE_MAP } from "~/enumerate/constant"; import { PROJECT_TYPE_MAP } from "~/enumerate/constant";
import WpsEditor from "~/pages/Container/WpsEditor"; 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"; import "./index.less";
const { router } = tools; const { router } = tools;
@ -453,6 +459,36 @@ const EvalProjectMonitorDetail = (props) => {
); );
}; };
/** 可锁定 tab 的标签:前置未完成时展示警告态 */
const renderLockableTabLabel = (label, done, locked) => (
<span>
{label}
<Tag
color={locked ? "warning" : done ? "success" : "default"}
style={{ marginLeft: 6 }}
>
{locked ? "前置未完成" : done ? "已完成" : "待办理"}
</Tag>
</span>
);
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 = ( const projectInfo = (
<div className="epm-detail-band"> <div className="epm-detail-band">
<div className="epm-detail-band-item"> <div className="epm-detail-band-item">
@ -501,6 +537,11 @@ const EvalProjectMonitorDetail = (props) => {
setActiveKey(key); setActiveKey(key);
}} }}
items={[ items={[
{
key: "RISK_ANALYSIS",
label: renderTabLabel("风险分析", "RISK_ANALYSIS"),
children: <RiskAnalysisContent readOnly />,
},
{ {
key: "BUSINESS_CONTRACT", key: "BUSINESS_CONTRACT",
label: renderTabLabel("项目合同", "BUSINESS_CONTRACT"), label: renderTabLabel("项目合同", "BUSINESS_CONTRACT"),
@ -530,6 +571,36 @@ const EvalProjectMonitorDetail = (props) => {
label: renderTabLabel("报告编制", "REPORT_PREPARATION"), label: renderTabLabel("报告编制", "REPORT_PREPARATION"),
children: null, children: null,
}, },
{
key: "INTERNAL_REVIEW",
label: renderTabLabel("内部审核", "INTERNAL_REVIEW"),
children: <InternalReviewContent readOnly />,
},
{
key: "TECHNICAL_REVIEW",
label: renderTabLabel("技术审核", "TECHNICAL_REVIEW"),
children: <TechnicalReviewContent readOnly />,
},
{
key: "PROCESS_CONTROL",
label: renderLockableTabLabel(
"过程控制",
nodeMap["PROCESS_CONTROL"]?.state === 1,
controlLocked,
),
disabled: controlLocked,
children: <ControlContent readOnly />,
},
{
key: "ARCHIVE",
label: renderLockableTabLabel(
"项目归档",
evalProjectDetailData?.archiveFlag === 1,
archiveLocked,
),
disabled: archiveLocked,
children: <ArchiveContent readOnly />,
},
]} ]}
/> />
{wpsVisible && ( {wpsVisible && (