diff --git a/jjb.config.js b/jjb.config.js index 494cd85..ccd777e 100644 --- a/jjb.config.js +++ b/jjb.config.js @@ -13,8 +13,8 @@ module.exports = { //API_HOST: "http://localhost:80", // API_HOST: "http://192.168.0.134", - API_HOST: "http://192.168.0.150", //太浅 - // API_HOST: "https://gbs-gateway.qhdsafety.com", + //API_HOST: "http://192.168.0.150", //太浅 + API_HOST: "https://gbs-gateway.qhdsafety.com", // API_HOST: "http://192.168.0.103", //huwei }, production: { diff --git a/src/api/safetyEvalBusiness/index.js b/src/api/safetyEvalBusiness/index.js index dcff078..be8c2e2 100644 --- a/src/api/safetyEvalBusiness/index.js +++ b/src/api/safetyEvalBusiness/index.js @@ -301,6 +301,18 @@ export const evalProcessControlFiles = declareRequest( "processControlFiles: [] | res.data || []", ); +/** 项目档案文件列表-机构端 */ +export const evalProjectArchiveFileOrgPage = declareRequest( + "evalProjectArchiveFileOrgLoading", + "Get > /safetyEval/eval-project-archive-file/org/list", +); + +/** 新增项目档案文件(机构补充上传) */ +export const evalProjectArchiveFileAdd = declareRequest( + "evalProjectArchiveFileAddLoading", + "Post > @/safetyEval/eval-project-archive-file", +); + // ===== 监管端-安评项目实时监控 ===== export const evalProjectMonitorStat = declareRequest( @@ -346,6 +358,12 @@ export const evalProjectArchivePage = declareRequest( "Get > /safetyEval/institution/eval-project/archive-supervision-page", ); +/** 项目档案文件列表-监管端 */ +export const evalProjectArchiveFilePage = declareRequest( + "evalProjectArchiveFileLoading", + "Get > /safetyEval/eval-project-archive-file/monitor/page", +); + // ===== 机构端-安评报告库 ===== export const evalReportSummary = declareRequest( diff --git a/src/enumerate/constant/index.js b/src/enumerate/constant/index.js index 1dd61db..c542f25 100644 --- a/src/enumerate/constant/index.js +++ b/src/enumerate/constant/index.js @@ -444,11 +444,11 @@ export const WARING_STATUS_OPTIONS = [ export const NODE_LABELS = { RISK_ANALYSIS: "风险分析", - BUSINESS_CONTRACT: "商务合同", + BUSINESS_CONTRACT: "业务合同", PROJECT_GROUP: "项目组", - SITE_SURVEY: "现场勘查", + SITE_SURVEY: "现场踏勘", REPORT_PREPARATION: "报告编制", - INTERNAL_REVIEW: "内审", + INTERNAL_REVIEW: "内部审核", TECHNICAL_REVIEW: "技术审核", PROCESS_CONTROL: "过程控制", ARCHIVE: "项目归档", @@ -784,3 +784,47 @@ export const QUESTION_TYPE_MAP = QUESTION_TYPE_OPTIONS.reduce( (acc, cur) => ({ ...acc, [cur.value]: cur.label }), {}, ); + +/** 项目档案类型(eval_project_archive_file.archive_type) */ +export const ARCHIVE_TYPE_OPTIONS = [ + { label: "项目风险分析", value: "RISK_ANALYSIS" }, + { label: "技术服务合同", value: "TECH_SERVICE_CONTRACT" }, + { label: "项目组织文件", value: "PROJECT_ORG_FILE" }, + { label: "实施计划", value: "IMPLEMENT_PLAN" }, + { label: "从业告知", value: "PRACTICE_NOTICE" }, + { label: "现场记录", value: "SITE_RECORD" }, + { label: "现场勘察资料", value: "SITE_SURVEY" }, + { label: "现场检查资料", value: "SITE_CHECK" }, + { label: "整改复查资料", value: "RECTIFICATION_REVIEW" }, + { label: "评价报告", value: "EVALUATION_REPORT" }, + { label: "审核记录", value: "REVIEW_RECORD" }, + { label: "过程控制文件", value: "PROCESS_CONTROL" }, + { label: "签发文件", value: "SIGN_ISSUE" }, + { label: "专家评审材料", value: "EXPERT_REVIEW" }, + { label: "项目附图", value: "PROJECT_ATTACHMENT" }, + { label: "报告修改材料", value: "REPORT_MODIFICATION" }, + { label: "往来文件", value: "CORRESPONDENCE" }, + { label: "其他", value: "OTHER" }, +]; + +export const ARCHIVE_TYPE_MAP = ARCHIVE_TYPE_OPTIONS.reduce( + (acc, cur) => ({ ...acc, [cur.value]: cur.label }), + {}, +); + +/** 档案文件来源(record_source:auto_process_record流程自动记录 / upload_additional_files补充上传) */ +export const ARCHIVE_RECORD_SOURCE_MAP = { + auto_process_record: "系统形成", + upload_additional_files: "机构补充上传", +}; + +/** 档案文件来源选项与徽标文案(机构端档案内容列表) */ +export const ARCHIVE_RECORD_SOURCE_OPTIONS = [ + { label: "流程自动记录", value: "auto_process_record" }, + { label: "补充上传", value: "upload_additional_files" }, +]; + +export const ARCHIVE_RECORD_SOURCE_BADGE_MAP = ARCHIVE_RECORD_SOURCE_OPTIONS.reduce( + (acc, cur) => ({ ...acc, [cur.value]: cur.label }), + {}, +); diff --git a/src/pages/Container/EduTraining/ClassManage/Add/index.js b/src/pages/Container/EduTraining/ClassManage/Add/index.js index b856c03..17a9813 100644 --- a/src/pages/Container/EduTraining/ClassManage/Add/index.js +++ b/src/pages/Container/EduTraining/ClassManage/Add/index.js @@ -94,12 +94,14 @@ function ClassAddPage(props) { const handleSubmit = async () => { try { const values = await form.validateFields(); - // 培训日期范围拆为后端 startDate/endDate(yyyy-MM-dd) + // 培训日期范围拆为后端 startDate/endDate;结束时间为当天最大值(与延期一致) const { trainingDateRange, ...rest } = values; const payload = { ...rest, startDate: trainingDateRange?.[0]?.format("YYYY-MM-DD"), - endDate: trainingDateRange?.[1]?.format("YYYY-MM-DD"), + endDate: trainingDateRange?.[1] + ?.endOf("day") + .format("YYYY-MM-DD HH:mm:ss"), ...(isEdit ? { id: router.query.classId } : {}), }; const res = await (isEdit diff --git a/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js index b25ad76..aa4f16d 100644 --- a/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js +++ b/src/pages/Container/MonitorManage/EvalProjectMonitor/Detail/index.js @@ -523,6 +523,7 @@ const EvalProjectMonitorDetail = (props) => { setWpsVisible(false)} fileId={wpsDoc?.fileId} + fileName={wpsDoc?.name} readOnly /> )} diff --git a/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.less b/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.less index 09cd6cc..bf0fa71 100644 --- a/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.less +++ b/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.less @@ -249,7 +249,7 @@ border-radius: 4px; background: #eff6ff; color: #475569; - font-size: 13px; + font-size: 14px; line-height: 1.65; strong { diff --git a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js index 091d65e..3b296a6 100644 --- a/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js +++ b/src/pages/Container/SafetyEvalBusiness/ProjectFlow/ArchiveContent.js @@ -1,48 +1,54 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import { - Tag, Button, - Card, Table, Input, Flex, - Popover, - Image, + Select, Row, Col, DatePicker, Form, + Modal, message, } from "antd"; import dayjs from "dayjs"; import { Connect } from "@cqsjjb/jjb-dva-runtime"; +import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction"; import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace"; import { tools } from "@cqsjjb/jjb-common-lib"; +import { useDebounce } from "~/utils"; import LayoutFooterPortal from "~/components/LayoutFooterPortal"; +import PreviewUrlButton from "~/components/PreviewUrlButton"; +import ArchiveSupplementModal from "./ArchiveSupplementModal"; +import { + NODE_LABELS, + ARCHIVE_RECORD_SOURCE_OPTIONS, + ARCHIVE_RECORD_SOURCE_BADGE_MAP, +} from "~/enumerate/constant"; const { router } = tools; const ArchiveContent = ({ readOnly, ...props }) => { const projectId = router.query?.id; - const { processControlDetail, processControlFiles, evalProjectDetailData } = + const { processControlDetail, evalProjectDetailData } = props.safetyEvalBusiness || {}; const { processControl, archiveFlag } = processControlDetail || {}; - const [files, setFiles] = useState([]); const [archiveForm] = Form.useForm(); + const [archiveFiles, setArchiveFiles] = useState([]); + const [sourceFilter, setSourceFilter] = useState(""); + const [keyword, setKeyword] = useState(""); + const [supplementOpen, setSupplementOpen] = useState(false); + const [viewRecord, setViewRecord] = useState(null); const fetchDetail = () => { props.evalProcessControlDetail({ projectId }); }; - const fetchFiles = () => { - props.evalProcessControlFiles({ projectId }); - }; - useEffect(() => { if (projectId) { fetchDetail(); - fetchFiles(); } }, [projectId]); @@ -59,11 +65,34 @@ const ArchiveContent = ({ readOnly, ...props }) => { } }, [processControlDetail]); + const fetchArchiveFiles = () => { + props.evalProjectArchiveFileOrgPage({ projectId }).then((res) => { + if (res?.success !== false) { + setArchiveFiles(res?.data || []); + } + }); + }; + useEffect(() => { - if (processControlFiles) { - setFiles(processControlFiles); - } - }, [processControlFiles]); + if (!projectId) return; + fetchArchiveFiles(); + }, [projectId]); + + /** 本地筛选:来源 + 文件名称/档案类型关键字(防抖 400ms) */ + const debouncedKeyword = useDebounce(keyword, 400); + const filteredArchiveFiles = useMemo(() => { + const kw = debouncedKeyword.trim().toLowerCase(); + return archiveFiles.filter((item) => { + const matchSource = !sourceFilter || item.recordSource === sourceFilter; + const matchKeyword = + !kw || + (item.files || []).some((f) => + (f.name || "").toLowerCase().includes(kw), + ) || + (item.archiveType || "").toLowerCase().includes(kw); + return matchSource && matchKeyword; + }); + }, [archiveFiles, sourceFilter, debouncedKeyword]); const isSignatureCompleted = processControl?.reviewerSignatureTaskStatus === "completed"; @@ -80,91 +109,149 @@ const ArchiveContent = ({ readOnly, ...props }) => { } }; - const isImage = (name) => - /\.(jpg|jpeg|png|gif|webp|bmp|svg)$/i.test(name || ""); + /** 解析文件地址:相对路径拼接 window.fileUrl */ + const resolveFileUrl = (raw) => { + if (!raw) return ""; + const u = String(raw); + return /^https?:\/\//i.test(u) ? u : `${window.fileUrl || ""}${u}`; + }; - const fileColumns = [ - { title: "流程名称", dataIndex: "processName", ellipsis: true }, - { title: "条目名称", dataIndex: "itemName", ellipsis: true }, + /** 单文件下载:跨域地址下 a.download 无效,fetch 转 blob 后再触发下载 */ + const downloadArchiveFile = async (f) => { + const url = resolveFileUrl(f.url); + if (!url) return; + const res = await fetch(url); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const blobUrl = window.URL.createObjectURL(await res.blob()); + const link = document.createElement("a"); + link.href = blobUrl; + link.download = f.name || ""; + link.style.display = "none"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(blobUrl); + }; + + /** 批量下载 files 数组(逐个串行下载,失败不影响其余文件) */ + const handleBatchDownload = async (files) => { + for (const f of files || []) { + try { + await downloadArchiveFile(f); + } catch { + message.error(`文件下载失败:${f.name || f.url}`); + } + } + }; + + const archiveColumns = [ { - 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; + title: "序号", + width: 60, + render: (_, __, index) => index + 1, + }, + { + title: "文件名称", + dataIndex: "files", + ellipsis: true, + render: (v) => { + if(Array.isArray(v)){ + return v.map(v=>v.name).join(',') } - 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}个附件 - - - - ); }, }, + { + title: "档案类型", + dataIndex: "archiveType", + width: 180, + + }, + { + title: "来源/业务节点", + dataIndex: "recordSource", + width: 150, + render: (v, r) => ( + + + {ARCHIVE_RECORD_SOURCE_BADGE_MAP[v] || v || "-"} + + {NODE_LABELS[r.businessNode] || "-"} + + ), + }, + { + title: "形成/上传人员", + dataIndex: "createName", + width: 120, + render: (v) => v || "-", + }, + { + title: "形成时间", + dataIndex: "createTime", + width: 200, + render: (v) => v || "-", + }, + { + title: "操作", + width: 110, + render: (_, r) => ( + + + + + ), + }, ]; return ( - -
- - 项目归档 - - - 项目过程文件清单,确认签字完成后归档入库 - -
+ + + + setKeyword(e.target.value)} + onSearch={setKeyword} + className="pf-archive__keyword-input" + /> + + + + + + +