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",
"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",

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 { 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({
</Button>,
]}
>
<div ref={resumeRef} style={{ background: "#fff", padding: 8 }}>
<div style={{ background: "#fff", padding: 8 }}>
<table className="staff-resume-table">
<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 { 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) => (
<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 = (
<div className="epm-detail-band">
<div className="epm-detail-band-item">
@ -501,6 +537,11 @@ const EvalProjectMonitorDetail = (props) => {
setActiveKey(key);
}}
items={[
{
key: "RISK_ANALYSIS",
label: renderTabLabel("风险分析", "RISK_ANALYSIS"),
children: <RiskAnalysisContent readOnly />,
},
{
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: <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 && (