feat
parent
15847d7933
commit
b8dfddebe5
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
姓名
|
||||||
|
{userName}
|
||||||
|
性别
|
||||||
|
{gender}
|
||||||
|
出生日期
|
||||||
|
{birthDate}
|
||||||
|
现住址
|
||||||
|
{currentAddress}
|
||||||
|
办公地址
|
||||||
|
{officeAddress}
|
||||||
|
职业资格等级及证书号码
|
||||||
|
{qualificationText}
|
||||||
|
注册安全工程师证书编号
|
||||||
|
{certNo}
|
||||||
|
身份证件号码
|
||||||
|
{idCardNo}
|
||||||
|
联系电话
|
||||||
|
{phone}
|
||||||
|
学历学位
|
||||||
|
{education}
|
||||||
|
毕业院校及专业
|
||||||
|
{schoolMajor}
|
||||||
|
出版学术专著、专利、科技发明及获奖情况
|
||||||
|
{publications}
|
||||||
|
自我申报的专业能力及认定方式
|
||||||
|
{capability}
|
||||||
|
申报专业能力证明材料
|
||||||
|
{certFiles}
|
||||||
|
主要学习工作经历
|
||||||
|
{workExperience}
|
||||||
|
本人签字
|
||||||
|
|
||||||
|
从业机构确认
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,3 +5,4 @@
|
||||||
|
|
||||||
部门经理或负责人:
|
部门经理或负责人:
|
||||||
年 月 日
|
年 月 日
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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 && (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue