diff --git a/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.js b/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.js
index 8cb9797..be106b2 100644
--- a/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.js
+++ b/src/pages/Container/SafetyEvalBusiness/EvalReportLibrary/index.js
@@ -10,19 +10,17 @@ import {
Row,
Col,
message,
- Descriptions,
Upload,
DatePicker,
Space,
ConfigProvider,
- Tag,
- Tooltip,
} from "antd";
import { PlusOutlined, UploadOutlined, SendOutlined } from "@ant-design/icons";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "~/components/SearchForm";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import PreviewUrlButton from "~/components/PreviewUrlButton";
+import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
@@ -41,7 +39,25 @@ const { TextArea } = Input;
const UPLOAD_ACTION = `${window.process?.env?.app?.API_HOST || ""}/safetyEval/file/upload`;
+/** 报告文件类型配置 */
+const FILE_TYPE_CONFIG = {
+ main: {
+ label: "报告正文",
+ accept: ".pdf,.doc,.docx,.zip,.rar,.7z",
+ acceptSet: new Set(["pdf", "doc", "docx", "zip", "rar", "7z"]),
+ rule: "正式签字盖章版正文",
+ format: "PDF、Word、ZIP、RAR、7Z,单个文件不超过100MB",
+ },
+ attachment: {
+ label: "报告附件",
+ accept: ".pdf,.doc,.docx,.xls,.xlsx,.jpg,.jpeg,.png,.zip,.rar,.7z",
+ acceptSet: new Set(["pdf", "doc", "docx", "xls", "xlsx", "jpg", "jpeg", "png", "zip", "rar", "7z"]),
+ rule: "图纸、检测报告、签字页及支撑资料",
+ format: "PDF、Office、图片、ZIP、RAR、7Z",
+ },
+};
+let fileInputCounter = 0;
/** 保密属性(OpenAPI 仅示例 NORMAL) */
const SECRET_LEVEL_OPTIONS = [
@@ -85,30 +101,6 @@ const getYearOptions = () => {
});
};
-const getFileExt = (name = "") => {
- const parts = String(name).split(".");
- return parts.length > 1 ? parts.pop().toUpperCase() : "PDF";
-};
-
-/** 解析 JSON 文件列表,使用 PreviewUrlButton 渲染预览链接 */
-const renderFileLinks = (jsonStr) => {
- if (!jsonStr) return "-";
- try {
- const files = JSON.parse(jsonStr);
- if (!Array.isArray(files) || files.length === 0) return "-";
- return files.map((f, i) => (
-
- ));
- } catch {
- return jsonStr;
- }
-};
-
const getOptionLabel = (options, value) =>
options.find((item) => item.value === value)?.label || value || "-";
@@ -131,19 +123,16 @@ const EvalReportLibrary = (props) => {
const [total, setTotal] = useState(0);
const [statData, setStatData] = useState({});
const [addVisible, setAddVisible] = useState(false);
- const [detailVisible, setDetailVisible] = useState(false);
const [detailData, setDetailData] = useState(null);
const [spotcheckVisible, setSpotcheckVisible] = useState(false);
const [spotcheckData, setSpotcheckData] = useState([]);
const [currentReport, setCurrentReport] = useState(null);
const [rectifyVisible, setRectifyVisible] = useState(false);
const [rectifyRecord, setRectifyRecord] = useState(null);
- const [uploadedFile, setUploadedFile] = useState(null);
- const [contractFiles, setContractFiles] = useState([]);
- const [processDocFiles, setProcessDocFiles] = useState([]);
// 变更时间:2026-08-11 变更原因:批量报送需记录勾选的报告主键
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [batchSubmitting, setBatchSubmitting] = useState(false);
+ const [reportFiles, setReportFiles] = useState([]);
const {
evalReportPageLoading,
@@ -153,6 +142,9 @@ const EvalReportLibrary = (props) => {
evalReportSpotcheckRectifyLoading,
} = props.safetyEvalBusiness;
+ // 查看模式:复用上传弹窗展示详情,表单整体禁用
+ const viewMode = detailData !== null;
+
const fetchSummary = async () => {
const res = await props.evalReportSummary();
if (res?.success !== false) {
@@ -200,122 +192,116 @@ const EvalReportLibrary = (props) => {
};
const handleOpenAdd = () => {
- setUploadedFile(null);
- setContractFiles([]);
- setProcessDocFiles([]);
+ setDetailData(null);
uploadForm.resetFields();
uploadForm.setFieldsValue({
reportYear: dayjs().year(),
evalTypeCode: "PRE",
-
secretLevelCode: "NORMAL",
issueDate: dayjs(),
submitToRegulator: 0,
});
+ fileInputCounter = 0;
+ setReportFiles([
+ { id: ++fileInputCounter, type: "main", url: "", fileName: "" },
+ { id: ++fileInputCounter, type: "attachment", url: "", fileName: "" },
+ ]);
setAddVisible(true);
};
- const handleFileUploadChange = (info) => {
- if (info.file.status === "uploading") return;
+ const addReportFileRow = () => {
+ setReportFiles((prev) => [
+ ...prev,
+ { id: ++fileInputCounter, type: "attachment", url: "", fileName: "" },
+ ]);
+ message.success("已添加一条文件记录");
+ };
+
+ const removeReportFileRow = (id) => {
+ setReportFiles((prev) => {
+ if (prev.length <= 1) {
+ message.warning("至少保留一条文件记录");
+ return prev;
+ }
+ return prev.filter((f) => f.id !== id);
+ });
+ };
+
+ const changeReportFileType = (id, type) => {
+ setReportFiles((prev) =>
+ prev.map((f) => (f.id === id ? { ...f, type, url: "", fileName: "", uploading: false } : f)),
+ );
+ };
+
+ const handleReportFileUpload = (id, info) => {
+ if (info.file.status === "uploading") {
+ setReportFiles((prev) =>
+ prev.map((f) => (f.id === id ? { ...f, fileName: info.file.name, uploading: true } : f)),
+ );
+ return;
+ }
if (info.file.status === "done") {
- const responseData = info.file.response?.data;
- const fileUrl = responseData?.url || responseData?.fileUrl || "";
- const fileName =
- responseData?.name || responseData?.fileName || info.file.name;
- if (!fileUrl) {
+ const data = info.file.response?.data;
+ const url = data?.url || data?.fileUrl || "";
+ const name = data?.name || data?.fileName || info.file.name;
+ if (!url) {
message.error("上传响应中未获取到文件地址");
return;
}
- setUploadedFile({ url: fileUrl, name: fileName });
- uploadForm.setFieldsValue({
- fileUrl,
- fileName,
- reportName:
- uploadForm.getFieldValue("reportName") ||
- fileName.replace(/\.[^.]+$/, ""),
- });
- message.success(`${fileName} 上传成功`);
+ const { createdTime, size } = data || {};
+ setReportFiles((prev) =>
+ prev.map((f) =>
+ f.id === id
+ ? { ...f, url, fileName: name, createdTime, size, uploading: false }
+ : f,
+ ),
+ );
+ message.success(`${name} 上传成功`);
} else if (info.file.status === "error") {
+ setReportFiles((prev) =>
+ prev.map((f) => (f.id === id ? { ...f, fileName: "", uploading: false } : f)),
+ );
message.error(`${info.file.name} 上传失败`);
}
};
- // 合同文件上传
- const handleContractUploadChange = (info) => {
- if (info.file.status === "error") {
- message.error(`${info.file.name} 上传失败`);
- return;
- }
- const doneFiles = info.fileList
- .filter((f) => f.status === "done" && f.response?.data)
- .map((f) => ({
- uid: f.uid,
- url: f.response.data.url || f.response.data.fileUrl || "",
- name: f.response.data.name || f.response.data.fileName || f.name,
- }))
- .filter((f) => f.url);
- setContractFiles(doneFiles);
- };
-
- // 其他过程文档上传
- const handleProcessDocUploadChange = (info) => {
- if (info.file.status === "error") {
- message.error(`${info.file.name} 上传失败`);
- return;
- }
- const doneFiles = info.fileList
- .filter((f) => f.status === "done" && f.response?.data)
- .map((f) => ({
- uid: f.uid,
- url: f.response.data.url || f.response.data.fileUrl || "",
- name: f.response.data.name || f.response.data.fileName || f.name,
- }))
- .filter((f) => f.url);
- setProcessDocFiles(doneFiles);
- };
-
const handleAddOk = async () => {
const values = await uploadForm.validateFields().catch(() => null);
if (!values) return;
- if (!values.fileUrl) {
- message.warning("请选择需要上传的正式报告文件");
+ const uploading = reportFiles.some((r) => r.uploading);
+ if (uploading) {
+ message.warning("文件正在上传中,请稍候");
return;
}
+ const doneFiles = reportFiles.filter((r) => r.url);
+ const mainFiles = doneFiles.filter((r) => r.type === "main");
+ if (mainFiles.length === 0) {
+ message.warning("请至少上传1个报告正文");
+ return;
+ }
+ const fileUrlArr = doneFiles.map((r) => ({
+ type: r.type,
+ url: r.url,
+ name: r.fileName,
+ createdTime: r.createdTime,
+ size: r.size,
+ }));
const payload = {
+ ...values,
reportNo: values.reportNo?.trim(),
reportName: values.reportName?.trim(),
- reportYear: values.reportYear,
- evalTypeCode: values.evalTypeCode,
- evalTypeName: getOptionLabel(
- EVAL_TYPE_OPTIONS,
- values.evalTypeCode,
- ),
- industryCode: values.industryCode,
- industryName: getOptionLabel(
- QUALIFICATION_INDUSTRY_OPTIONS,
- values.industryCode,
- ),
+ evalTypeName: getOptionLabel(EVAL_TYPE_OPTIONS, values.evalTypeCode),
+ industryName: getOptionLabel(QUALIFICATION_INDUSTRY_OPTIONS, values.industryCode),
evaluatedUnitName: values.evaluatedUnitName?.trim(),
issueDate: values.issueDate
? dayjs(values.issueDate).format("YYYY-MM-DD")
: undefined,
- pageCount: values.pageCount,
- fileUrl: values.fileUrl,
- fileName: values.fileName,
- secretLevelCode: values.secretLevelCode,
- secretLevelName: getOptionLabel(
- SECRET_LEVEL_OPTIONS,
- values.secretLevelCode,
- ),
- contractFileUrls:
- contractFiles.length > 0 ? JSON.stringify(contractFiles) : undefined,
- processDocUrls:
- processDocFiles.length > 0
- ? JSON.stringify(processDocFiles)
- : undefined,
- remarks: values.remarks,
+ fileUrl: JSON.stringify(fileUrlArr),
+ secretLevelName: getOptionLabel(SECRET_LEVEL_OPTIONS, values.secretLevelCode),
submitToRegulator: values.submitToRegulator === 1,
};
+ delete payload.reportMainFile;
+ delete payload.reportAttachment;
const res = await props.evalReportAdd(payload);
if (res?.success !== false) {
message.success(
@@ -330,22 +316,75 @@ const EvalReportLibrary = (props) => {
};
const handleViewDetail = async (record) => {
- setDetailData(null);
- setDetailVisible(true);
+ // 查看复用上传弹窗:清空表单并置为只读,接口返回后回填数据
+ setDetailData({});
+ uploadForm.resetFields();
+ fileInputCounter = 0;
+ setReportFiles([]);
+ setAddVisible(true);
const res = await props.evalReportDetail({ id: record.id });
- if (res?.success !== false) {
- setDetailData(res?.data || {});
+ if (res?.success === false) {
+ setAddVisible(false);
+ return;
}
+ const data = res?.data || {};
+ setDetailData(data);
+ uploadForm.setFieldsValue({
+ ...data,
+ issueDate: data.issueDate ? dayjs(data.issueDate) : undefined,
+ });
+ let files = [];
+ try {
+ const parsed = JSON.parse(data.fileUrl);
+ if (Array.isArray(parsed)) files = parsed;
+ } catch {
+ if (data.fileUrl) {
+ files = [{ type: "main", url: data.fileUrl, name: data.fileName || data.reportName }];
+ }
+ }
+ setReportFiles(
+ files.map((f) => ({
+ id: ++fileInputCounter,
+ type: f.type,
+ url: f.url,
+ fileName: f.name,
+ })),
+ );
};
const handleDownload = async (record) => {
let fileUrl = record.fileUrl;
let fileName = record.fileName || record.reportName || "报告文件";
+ // 解析 fileUrl:兼容新格式(JSON 数组)和旧格式(字符串)
+ if (fileUrl) {
+ try {
+ const arr = JSON.parse(fileUrl);
+ if (Array.isArray(arr) && arr.length > 0) {
+ const mainFile = arr.find((f) => f.type === "main") || arr[0];
+ fileUrl = mainFile.url;
+ fileName = mainFile.name || fileName;
+ }
+ } catch {
+ // 旧格式:直接使用字符串
+ }
+ }
if (!fileUrl) {
const res = await props.evalReportDetail({ id: record.id });
if (res?.success === false) return;
- fileUrl = res?.data?.fileUrl;
- fileName = res?.data?.fileName || fileName;
+ const detailFileUrl = res?.data?.fileUrl;
+ if (detailFileUrl) {
+ try {
+ const arr = JSON.parse(detailFileUrl);
+ if (Array.isArray(arr) && arr.length > 0) {
+ const mainFile = arr.find((f) => f.type === "main") || arr[0];
+ fileUrl = mainFile.url;
+ fileName = mainFile.name || fileName;
+ }
+ } catch {
+ fileUrl = detailFileUrl;
+ fileName = res?.data?.fileName || fileName;
+ }
+ }
}
if (!fileUrl) {
message.warning("文件地址不存在");
@@ -864,15 +903,19 @@ const EvalReportLibrary = (props) => {
/>
- {/* 上传历史报告 */}
+ {/* 上传历史报告 / 报告详情(查看复用同一弹窗,disabled 只读) */}
- 上传历史报告
-
- 上传过去三年已完成并正式签发的安全评价报告
+ viewMode ? (
+ "报告详情"
+ ) : (
+
+
上传历史报告
+
+ 上传过去三年已完成并正式签发的安全评价报告
+
-
+ )
}
open={addVisible}
onCancel={() => setAddVisible(false)}
@@ -880,6 +923,23 @@ const EvalReportLibrary = (props) => {
confirmLoading={evalReportAddLoading}
okText="确认上传"
cancelText="取消"
+ footer={
+ viewMode ? (
+
+ {detailData && isSpotcheckRelated(detailData) && (
+
+ )}
+
+
+ ) : undefined
+ }
width={820}
destroyOnHidden
className="erl-upload-modal"
@@ -889,69 +949,8 @@ const EvalReportLibrary = (props) => {
layout="vertical"
preserve={false}
scrollToFirstError
+ disabled={viewMode}
>
-
-
-
-
-
-
-
- {
- if (!uploadForm.getFieldValue("fileUrl")) {
- throw new Error("请选择正式报告文件");
- }
- },
- },
- ]}
- >
-
-
{
- const isLt100M = file.size / 1024 / 1024 <= 100;
- if (!isLt100M) {
- message.error("单个文件不超过 100MB");
- return Upload.LIST_IGNORE;
- }
- return true;
- }}
- className="erl-upload-trigger"
- >
-
-
PDF
-
-
选择正式报告文件
-
- 支持 PDF、Word,建议上传签字盖章后的正式 PDF
- 文件,单个文件不超过100MB。
-
-
- {uploadedFile ? `${uploadedFile.name}` : "尚未选择文件"}
-
-
-
}
- >
- 选择文件
-
-
-
-
-
-
{
+ {/* 报告文件列表(对齐原型 .report-upload-files Table 布局) */}
-
- {
- const isLt100M = file.size / 1024 / 1024 <= 100;
- if (!isLt100M) {
- message.error("单个文件不超过 100MB");
- return Upload.LIST_IGNORE;
- }
- return true;
- }}
- >
- }>选择合同文件
-
-
-
-
-
- {
- const isLt100M = file.size / 1024 / 1024 <= 100;
- if (!isLt100M) {
- message.error("单个文件不超过 100MB");
- return Upload.LIST_IGNORE;
- }
- return true;
- }}
- >
- }>选择过程文档
-
-
+
+
+
报告文件
+ {!viewMode && (
+ } onClick={addReportFileRow}>
+ 添加文件
+
+ )}
+
+
(
+