diff --git a/public/QAPJ-CX-2-JL02 评价人员任命及专家聘用书.docx b/public/QAPJ-CX-2-JL02 评价人员任命及专家聘用书.docx new file mode 100644 index 0000000..e9c2522 --- /dev/null +++ b/public/QAPJ-CX-2-JL02 评价人员任命及专家聘用书.docx @@ -0,0 +1,49 @@ + 评价人员任命及专家聘用书 + + 根据市场部下达的评价项目为: {projectName} {evalTypeText} 评价任务,结合项目的特性,现任命 {projectLeader} 担任该安全评价项目组长(负责人), {compiler} 担任该项目报告编制人。 + 根据评价部项目组组建原则及风险分析记录情况,项目组成员如下: + + 人员组成 + 姓名 + 专业 + 项目负责人 + {projectLeaderName} + {projectLeaderCapacity} + + 项目组成员 + {firstMemberName} + {firstMemberMajor} + + {#memberRows}{name} + {major}{/memberRows} + 报告编制人 + {firstCompilerName} + {firstCompilerCapacity} + + {#compilerRows}{compilerName} + {compilerCapacity}{/compilerRows} + 内审人员 + {firstAuditorName} + {firstAuditorCapacity} + + {#auditorRows}{auditorName} + {auditorCapacity}{/auditorRows} + 技术负责人 + {firstTechLeadName} + {firstTechLeadCapacity} + + {#techLeadRows}{techLeadName} + {techLeadCapacity}{/techLeadRows} + 过程控制负责人 + {processControlLeadName} + {processControlLeadCapacity} + 聘用专家组成员 + + + + + + + 评价部经理: + 年 月 日 + diff --git a/src/enumerate/constant/index.js b/src/enumerate/constant/index.js index 655a538..238f47a 100644 --- a/src/enumerate/constant/index.js +++ b/src/enumerate/constant/index.js @@ -600,6 +600,17 @@ export const CAPABILITY_MAP = CAPABILITY_OPTIONS.reduce((acc, cur) => { return acc; }, {}); +/** 业务范围与所需专业能力配备标准对照(依据《安全评价机构资质条件》应急〔2026〕60号附件) */ +export const INDUSTRY_REQUIRED_CAPABILITIES = { + COAL_MINING: ["SAFETY", "MECHANICAL", "ELECTRICAL", "MINING", "VENTILATION", "GEOLOGY", "MINE_CONSTRUCTION"], + METAL_NONMETAL_MINING: ["SAFETY", "MECHANICAL", "ELECTRICAL", "MINING", "VENTILATION", "GEOLOGY", "HYDRAULIC_STRUCTURE"], + ONSHORE_OIL_GAS: ["SAFETY", "MECHANICAL", "ELECTRICAL", "OIL_PRODUCTION", "STORAGE_AND_TRANSPORT"], + ONSHORE_OIL_GAS_PIPELINE: ["OIL_GAS_STORAGE_TRANSPORT", "EQUIPMENT", "INSTRUMENT", "ELECTRICAL", "ANTICORROSION", "SAFETY"], + PETROCHEMICAL_CHEMICAL_PHARMACEUTICAL: ["CHEMICAL_PROCESS", "CHEMICAL_MECHANICAL", "AUTOMATION_INSTRUMENT", "ELECTRICAL", "SAFETY"], + FIREWORKS_FIRECRACKERS: ["EXPLOSIVES", "EXPLOSION_TECHNOLOGY", "MECHANICAL", "ELECTRICAL", "SAFETY"], + METAL_SMELTING: ["SAFETY", "MECHANICAL", "ELECTRICAL", "METALLURGY", "NONFERROUS_METAL"], +}; + /** 专业(职称信息) */ export const PROFESSIONAL_MAJOR_OPTIONS = [ { label: "安全工程", value: "SAFETY_ENGINEERING" }, diff --git a/src/pages/Container/SafetyEvalBusiness/EvalProject/Create/index.js b/src/pages/Container/SafetyEvalBusiness/EvalProject/Create/index.js index 16186e8..0bf6d9c 100644 --- a/src/pages/Container/SafetyEvalBusiness/EvalProject/Create/index.js +++ b/src/pages/Container/SafetyEvalBusiness/EvalProject/Create/index.js @@ -21,7 +21,7 @@ import { Connect } from "@cqsjjb/jjb-dva-runtime"; import useIndustryOptions from "~/hooks/useIndustryOptions"; import BaiduMapPicker from "~/components/BaiduMapPicker"; import { district } from "~/enumerate/constant"; -import {REGISTER_ENGINEER_CATEGORY_OPTIONS} from '~/enumerate/enterpriseOptions'; +import {QUALIFICATION_INDUSTRY_OPTIONS} from '~/enumerate/enterpriseOptions'; import { phoneRule } from "~/utils/validators"; import { renderCapabilityList } from "~/utils"; import StaffPickerModal from "./StaffPickerModal"; @@ -29,6 +29,7 @@ import StaffPickerModal from "./StaffPickerModal"; import { EVAL_TYPE_OPTIONS, CHECKIN_RADIUS_OPTIONS, + INDUSTRY_REQUIRED_CAPABILITIES, } from "~/enumerate/constant"; const { router } = tools; @@ -102,9 +103,26 @@ const EvalProjectCreate = (props) => { setMapPickerVisible(false); }; + /** 按所选业务范围(配备标准)筛选人员能力,仅提交范围内所需的专业能力条目 */ + const filterCapacityByIndustry = (item, industryCodes) => { + const requiredCaps = industryCodes.flatMap( + (code) => INDUSTRY_REQUIRED_CAPABILITIES[code] || [], + ); + if (!requiredCaps.length || !Array.isArray(item.capabilityAssessment)) { + return item.capabilityAssessment?.length + ? renderCapabilityList(item.capabilityAssessment, {capFlag: true}) + : item.capacity; + } + const matched = item.capabilityAssessment.filter((cap) => + requiredCaps.includes(cap.professionalCapabilityCode), + ); + return matched.length ? renderCapabilityList(matched, {capFlag: true}) : item.capacity; + }; + const handleSubmit = () => { form.validateFields().then(async (values) => { const { planStartDate, planEndDate, ...rest } = values; + const industryCodes = String(values.industryCode || "").split(","); const params = { ...rest, planStartDate: planStartDate?.format("YYYY-MM-DD"), @@ -114,9 +132,7 @@ const EvalProjectCreate = (props) => { name: item.userName, deptName: item.deptName, posName: item.postName, - capacity: item.capabilityAssessment - ? renderCapabilityList(item.capabilityAssessment) - : item.capacity, + capacity: filterCapacityByIndustry(item, industryCodes), })), }; if (projectId) { @@ -357,18 +373,18 @@ const EvalProjectCreate = (props) => { } > - + */} diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent.js index 77b76a2..47f021c 100644 --- a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent.js +++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/RiskAnalysisContent.js @@ -304,7 +304,7 @@ const RiskAnalysisContent = ({ readOnly, ...props }) => { Array.isArray(value) ? value.join(",") : value } > - {QUALIFICATION_INDUSTRY_OPTIONS.map((opt) => ( {opt.label} diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/TeamContent.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/TeamContent.js index bd286af..9e16e21 100644 --- a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/TeamContent.js +++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/TeamContent.js @@ -25,7 +25,9 @@ import { fillDocxTemplate } from "~/utils/fillDocxTemplate"; const { router } = tools; -const TEAM_TEMPLATE_URL = "https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a86d2b2e4b072649bfb8a93.docx"; +const TEAM_TEMPLATE_URL = encodeURI( + "https://gbs-cqaqpj.oss-cn-beijing.aliyuncs.com/jjb/6a9165e2e4b0b3767a786858.docx", +); const TeamContent = ({ readOnly, ...props }) => { const { @@ -268,28 +270,57 @@ const TeamContent = ({ readOnly, ...props }) => { setSigners((prev) => prev.filter((s) => s.personnelId !== personnelId)); }; - // 导出Word:按当前项目组配置填充模板(聘请所需技术专家、备注事项、部门经理或负责人、年月日保留空白) + // 导出Word:按当前项目组配置填充模板(任命语句用"姓名(能力)",表格姓名/专业分列展示;聘用专家、评价部经理、年月日留白) const handleExport = async () => { /** 人员展示文本:姓名后追加能力,如"张三(化工)" */ const personText = (m) => `${m.personnelName}${m.capacity ? `(${m.capacity})` : ""}`; + const roleMembers = (role) => + teamMembers.filter((m) => m.roles.some((r) => r.role === role)); + const sentenceByRole = (role) => + roleMembers(role).map(personText).join("、"); const namesByRole = (role) => - teamMembers - .filter((m) => m.roles.some((r) => r.role === role)) - .map(personText) - .join("、"); + roleMembers(role).map((m) => m.personnelName).join("、"); + const capsByRole = (role) => + roleMembers(role).map((m) => m.capacity).filter(Boolean).join("、"); + const auditorList = roleMembers("INTERNAL_AUDITOR").map((m) => ({ + auditorName: m.personnelName, + auditorCapacity: m.capacity || "", + })); + const techLeadList = roleMembers("TECHNICAL_LEAD").map((m) => ({ + techLeadName: m.personnelName, + techLeadCapacity: m.capacity || "", + })); + const compilerList = roleMembers("REPORT_COMPILER").map((m) => ({ + compilerName: m.personnelName, + compilerCapacity: m.capacity || "", + })); + const memberList = teamMembers + .filter((m) => !m.roles.some((r) => r.role === "PROJECT_LEAD")) + .map((m) => ({ name: m.personnelName, major: m.capacity || "" })); const data = { projectName: evalProjectDetailData?.projectName || "", evalTypeText: EVAL_TYPE_MAP[evalProjectDetailData?.evalTypeCode] || "", - projectLeader: namesByRole("PROJECT_LEAD"), - membersText: teamMembers - .filter((m) => !m.roles.some((r) => r.role === "PROJECT_LEAD")) - .map(personText) - .join("、"), - compiler: namesByRole("REPORT_COMPILER"), - auditor: namesByRole("INTERNAL_AUDITOR"), - techLead: namesByRole("TECHNICAL_LEAD"), - processControlLead: namesByRole("PROCESS_CONTROL_LEAD"), + projectLeader: sentenceByRole("PROJECT_LEAD"), + compiler: sentenceByRole("REPORT_COMPILER"), + projectLeaderName: namesByRole("PROJECT_LEAD"), + projectLeaderCapacity: capsByRole("PROJECT_LEAD"), + // 标签合并分组:首行固定放第一人,循环行(continue)逐人一行 + firstAuditorName: auditorList[0]?.auditorName || "", + firstAuditorCapacity: auditorList[0]?.auditorCapacity || "", + auditorRows: auditorList.slice(1), + firstTechLeadName: techLeadList[0]?.techLeadName || "", + firstTechLeadCapacity: techLeadList[0]?.techLeadCapacity || "", + techLeadRows: techLeadList.slice(1), + processControlLeadName: namesByRole("PROCESS_CONTROL_LEAD"), + processControlLeadCapacity: capsByRole("PROCESS_CONTROL_LEAD"), + // 模板中标签列已纵向合并:restart 行放第一名成员,循环行(continue)逐人一行 + firstMemberName: memberList[0]?.name || "", + firstMemberMajor: memberList[0]?.major || "", + memberRows: memberList.slice(1), + firstCompilerName: compilerList[0]?.compilerName || "", + firstCompilerCapacity: compilerList[0]?.compilerCapacity || "", + compilerRows: compilerList.slice(1), }; try { await fillDocxTemplate( @@ -613,7 +644,9 @@ const TeamContent = ({ readOnly, ...props }) => { { ( - + {!readOnly &&