diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js new file mode 100644 index 0000000..cee17a5 --- /dev/null +++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js @@ -0,0 +1,222 @@ +import React, { useState, useEffect } from "react"; +import { + Tag, + Button, + Card, + Table, + Input, + Flex, + Popover, + Image, + Row, + Col, + DatePicker, + Form, + message, +} from "antd"; +import dayjs from "dayjs"; +import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; +import { tools } from "@cqsjjb/jjb-common-lib"; + +const { router } = tools; + +const ArchiveContent = ({ readOnly, ...props }) => { + const projectId = router.query?.id; + const { processControlDetail, processControlFiles, evalProjectDetailData } = + props.safetyEvalBusiness || {}; + const { processControl } = processControlDetail || {}; + + const [files, setFiles] = useState([]); + const [archiveForm] = Form.useForm(); + + const fetchDetail = () => { + props.evalProcessControlDetail({ projectId }); + }; + + const fetchFiles = () => { + props.evalProcessControlFiles({ projectId }); + }; + + useEffect(() => { + if (projectId) { + fetchDetail(); + fetchFiles(); + } + }, [projectId]); + + useEffect(() => { + if (processControlDetail) { + archiveForm.setFieldsValue({ + projectNo: evalProjectDetailData.projectNo, + archiveTransferorName: processControlDetail.archiveTransferorName || "", + archiveArchivistName: processControlDetail.archiveArchivistName || "", + archiveDate: processControlDetail.archiveDate + ? dayjs(processControlDetail.archiveDate) + : null, + }); + } + }, [processControlDetail]); + + useEffect(() => { + if (processControlFiles) { + setFiles(processControlFiles); + } + }, [processControlFiles]); + + const isSignatureCompleted = + processControl?.reviewerSignatureTaskStatus === "completed"; + const canArchive = isSignatureCompleted; + + const handleArchive = async () => { + if (!projectId) return; + const values = await archiveForm.validateFields(); + const res = await props.evalProcessControlArchive({ projectId, ...values }); + if (res?.success !== false) { + message.success("项目已归档入库"); + fetchDetail(); + props.getDetail?.(); + } + }; + + const isImage = (name) => + /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i.test(name || ""); + + const fileColumns = [ + { title: "流程名称", dataIndex: "processName", ellipsis: true }, + { title: "条目名称", dataIndex: "itemName", ellipsis: true }, + { + title: "文件", + dataIndex: "projectDocUrl", + render: (val) => { + let fileList = []; + if (typeof val === "string") { + try { + fileList = JSON.parse(val); + } catch { + fileList = []; + } + } else if (Array.isArray(val)) { + fileList = val; + } + if (!fileList.length) return "-"; + return ( + + + {fileList.map((f, i) => { + const url = f.url || f.response?.url; + return isImage(f.name) ? ( + + {} + + ) : ( + window.open(url)} + > + {f.name || "查看文件"} + + ); + })} + + } + trigger="hover" + placement="topLeft" + > + e.preventDefault()} + style={{ cursor: "pointer" }} + > + {fileList.length}个附件 + + + + ); + }, + }, + ]; + + return ( + +
+ + 项目归档 + + + 项目过程文件清单,确认签字完成后归档入库 + +
+ + + `${record.processName}-${record.materialName}-${record.itemName}` + } + pagination={false} + size="small" + /> + + + + + + + + + + + + + + + + + + + {processControlDetail?.archiveDate && ( + + + + + + )} + + + + + {!readOnly && ( + + )} + + + ); +}; + +export default Connect([NS_SAFETY_EVAL_BUSINESS], true)(ArchiveContent); diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent.js index 4c07b14..60f3dfe 100644 --- a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent.js +++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ControlContent.js @@ -1,6 +1,5 @@ import React, { useState, useEffect } from "react"; import { - Tag, Button, Card, Table, @@ -9,15 +8,8 @@ import { Flex, Typography, message, - Popover, - Image, Collapse, - Row, - Col, - DatePicker, - Form, } from "antd"; -import dayjs from "dayjs"; import { CheckCircleFilled } from "@ant-design/icons"; import { Connect } from "@cqsjjb/jjb-dva-runtime"; import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; @@ -28,23 +20,18 @@ const { router } = tools; const ControlContent = ({ readOnly, ...props }) => { const projectId = router.query?.id; - const { processControlDetail, processControlFiles, evalProjectDetailData } = - props.safetyEvalBusiness || {}; + const { processControlDetail } = props.safetyEvalBusiness || {}; const { processControl, controllers, reviewItems } = processControlDetail || {}; const [selectPeople, setSelectPeople] = useState(null); - // 当前步骤:1-过程控制审核 2-机构负责人签发 3-项目归档 + // 当前步骤:1-过程控制审核 2-机构负责人签发 const [step, setStep] = useState(1); // 过程控制审核 const [rows, setRows] = useState([]); - // 项目归档 - const [files, setFiles] = useState([]); - const [archiveForm] = Form.useForm(); - // 机构负责人签发 const [approvalOpinion, setApprovalOpinion] = useState(""); @@ -61,14 +48,9 @@ const ControlContent = ({ readOnly, ...props }) => { }); }; - const fetchFiles = () => { - props.evalProcessControlFiles({ projectId }); - }; - useEffect(() => { if (projectId) { fetchDetail(); - fetchFiles(); } }, [projectId]); @@ -85,25 +67,8 @@ const ControlContent = ({ readOnly, ...props }) => { if (processControl?.opinionContent) { setApprovalOpinion(processControl.opinionContent); } - - if (processControl) { - archiveForm.setFieldsValue({ - projectNo: evalProjectDetailData.projectNo, - archiveTransferorName: processControlDetail.archiveTransferorName || "", - archiveArchivistName: processControlDetail.archiveArchivistName || "", - archiveDate: processControlDetail.archiveDate - ? dayjs(processControlDetail.archiveDate) - : null, - }); - } }, [processControlDetail]); - useEffect(() => { - if (processControlFiles) { - setFiles(processControlFiles); - } - }, [processControlFiles]); - const updateRow = (index, field, value) => { const next = [...rows]; next[index] = { ...next[index], [field]: value }; @@ -112,10 +77,6 @@ const ControlContent = ({ readOnly, ...props }) => { const isDispatched = processControl?.reviewerSignatureTaskStatus !== "non_dispatched"; - const isSignatureCompleted = - processControl?.reviewerSignatureTaskStatus === "completed"; - - const canArchive = isSignatureCompleted; // 过程控制签字提交(confirm) const handleConfirm = async (resultCode) => { @@ -165,18 +126,6 @@ const ControlContent = ({ readOnly, ...props }) => { } }; - // 归档项目并入库 - const handleArchive = async () => { - if (!projectId) return; - const values = await archiveForm.validateFields(); - const res = await props.evalProcessControlArchive({ projectId, ...values }); - if (res?.success !== false) { - message.success("项目已归档入库"); - fetchDetail(); - props.getDetail?.(); - } - }; - const reviewColumns = [ { title: "程序审核项", dataIndex: "reviewDimensionName", width: 120 }, { title: "核查内容", dataIndex: "reviewFocus", width: 200 }, @@ -216,73 +165,9 @@ const ControlContent = ({ readOnly, ...props }) => { }, ]; - const isImage = (name) => - /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i.test(name || ""); - - const fileColumns = [ - { title: "流程名称", dataIndex: "processName", ellipsis: true }, - { title: "条目名称", dataIndex: "itemName", ellipsis: true }, - - { - title: "文件", - dataIndex: "projectDocUrl", - - render: (val) => { - let files = []; - if (typeof val === "string") { - try { - files = JSON.parse(val); - } catch { - files = []; - } - } else if (Array.isArray(val)) { - files = val; - } - if (!files.length) return "-"; - return ( - - - {files.map((f, i) => { - const url = f.url || f.response?.url; - return isImage(f.name) ? ( - - {} - - ) : ( - window.open(url)} - > - {f.name || "查看文件"} - - ); - })} - - } - trigger="hover" - placement="topLeft" - > - e.preventDefault()} - style={{ cursor: "pointer" }} - > - {files.length}个附件 - - - - ); - }, - }, - ]; - const STEP_ITEMS = [ { key: 1, label: "过程控制审核" }, { key: 2, label: "机构负责人签发" }, - { key: 3, label: "项目归档" }, ]; return ( @@ -327,7 +212,6 @@ const ControlContent = ({ readOnly, ...props }) => { size="small" /> { )} - - {step === 3 && ( - -
- - 项目归档 - - - 项目过程文件清单,确认签字完成后归档入库 - -
- -
- `${record.processName}-${record.materialName}-${record.itemName}` - } - pagination={false} - size="small" - /> - - - - - - - - - - - - - - - - - - - {processControlDetail?.archiveDate&&- - - - } - - - - - {!readOnly && ( - - )} - - - )} ); }; diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js index 6f68ff6..e4f0d69 100644 --- a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js +++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/index.js @@ -14,6 +14,7 @@ import SurveyContent from "./SurveyContent"; import InternalReviewContent from "./InternalReviewContent"; import TechnicalReviewContent from "./TechnicalReviewContent"; import ControlContent from "./ControlContent"; +import ArchiveContent from "./ArchiveContent"; import "./index.less"; const PROCESS_TABS = [ @@ -35,6 +36,7 @@ const PROCESS_TABS = [ { key: "internal", index: "6", label: "内部审核", role: "内部审核人" }, { key: "technical", index: "7", label: "技术审核", role: "技术负责人" }, { key: "control", index: "8", label: "过程控制", role: "过程控制负责人" }, + { key: "archive", index: "9", label: "项目归档", role: "档案管理员" }, ]; const { router } = tools; @@ -58,7 +60,7 @@ const parseProjectNode = (data) => { } }; -const buildProcessTabs = (projectNode) => { +const buildProcessTabs = (projectNode, archiveFlag) => { const nodes = parseProjectNode(projectNode); const nodeMap = {}; nodes.forEach((n) => { @@ -95,6 +97,18 @@ const buildProcessTabs = (projectNode) => { }; } + if (idx === 8) { + // 项目归档:前8个全部完成才解锁 + const unlocked = allPrevDone(8); + const archived = archiveFlag === 1; + return { + ...item, + state: unlocked ? (archived ? "已完成" : "待办理") : "前置未完成", + stateType: unlocked ? (archived ? "success" : "default") : "warning", + locked: !unlocked, + }; + } + if (node) { return { ...item, @@ -114,7 +128,10 @@ const ProjectFlow = (props) => { const projectId = router.query?.id; const readOnly = router.query?.readOnly === '1'; - const processTabs = buildProcessTabs(evalProjectDetailData?.projectNode); + const processTabs = buildProcessTabs( + evalProjectDetailData?.projectNode, + evalProjectDetailData?.archiveFlag, + ); const handleTabChange = (key) => { if (key === "report") { @@ -167,6 +184,9 @@ const ProjectFlow = (props) => { case "control": children = ; break; + case "archive": + children = ; + break; default: children = null; }