safety-eval-service-frontend/src/pages/Container/Supervision/EvalProjectDatabase/ArchiveModal.js

228 lines
7.4 KiB
JavaScript
Raw Normal View History

2026-08-25 17:55:37 +08:00
import React, { useEffect, useState } from "react";
2026-08-26 10:43:20 +08:00
import { Button, Flex, Input, Modal, Table, Tag, message } from "antd";
2026-08-25 17:55:37 +08:00
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
import PreviewUrlButton from "~/components/PreviewUrlButton";
import {
2026-08-26 10:43:20 +08:00
NODE_LABELS,
2026-08-25 17:55:37 +08:00
ARCHIVE_TYPE_MAP,
ARCHIVE_RECORD_SOURCE_MAP,
} from "~/enumerate/constant";
/** 解析文件地址:相对路径拼接 window.fileUrl与 PreviewUrlButton 保持一致) */
const resolveFileUrl = (raw) => {
if (!raw) return "";
const u = String(raw);
return /^https?:\/\//i.test(u) ? u : `${window.fileUrl || ""}${u}`;
};
/** 项目档案查看弹窗(原型 modal-reg-project-archive */
function ArchiveModal({ record, loading, fetchFiles, onClose }) {
const [query, setQuery] = useState({ pageIndex: 1, pageSize: 10, fileName: "" });
const [list, setList] = useState([]);
const [total, setTotal] = useState(0);
2026-08-26 10:43:20 +08:00
const [viewRecord, setViewRecord] = useState(null);
2026-08-25 17:55:37 +08:00
useEffect(() => {
if (!record?.id) return;
fetchFiles({ projectId: record.id, ...query }).then((res) => {
if (res?.success !== false) {
setList(res?.data || []);
setTotal(res?.totalCount || 0);
}
});
}, [record?.id, query]);
2026-08-26 10:43:20 +08:00
/** 单文件下载:跨域地址下 a.download 无效fetch 转 blob 后再触发下载 */
const downloadArchiveFile = async (f) => {
const url = resolveFileUrl(f.url);
2026-08-25 17:55:37 +08:00
if (!url) return;
2026-08-26 10:43:20 +08:00
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}`);
}
}
2026-08-25 17:55:37 +08:00
};
const columns = [
{
title: "序号",
width: 60,
render: (_, __, index) =>
(query.pageIndex - 1) * query.pageSize + index + 1,
},
{
title: "文件名称",
2026-08-26 10:43:20 +08:00
dataIndex: "files",
2026-08-25 17:55:37 +08:00
ellipsis: true,
render: (v, r) => (
2026-08-26 10:43:20 +08:00
2026-08-25 17:55:37 +08:00
<div>
2026-08-26 10:43:20 +08:00
{Array.isArray(v) && v.length
? v.map((f) => f.name).join(",")
: "-"}
2026-08-25 17:55:37 +08:00
</div>
2026-08-26 10:43:20 +08:00
2026-08-25 17:55:37 +08:00
),
},
{
title: "档案类型",
dataIndex: "archiveType",
width: 130,
},
{
title: "来源",
dataIndex: "recordSource",
width: 120,
render: (v) =>
v ? (
<Tag color={v === "upload_additional_files" ? "warning" : "processing"}>
{ARCHIVE_RECORD_SOURCE_MAP[v]}
</Tag>
) : (
"-"
),
},
{
title: "业务节点",
dataIndex: "businessNode",
width: 130,
2026-08-26 10:43:20 +08:00
render: (v) => NODE_LABELS[v] || v || "-",
2026-08-25 17:55:37 +08:00
},
{
title: "操作",
width: 120,
render: (_, r) => (
<TableAction>
<Button
type="link"
size="small"
2026-08-26 10:43:20 +08:00
disabled={!r.files?.length}
onClick={() => setViewRecord(r)}
>
查看
</Button>
<Button
type="link"
size="small"
disabled={!r.files?.length}
onClick={() => handleBatchDownload(r.files)}
2026-08-25 17:55:37 +08:00
>
下载
</Button>
</TableAction>
),
},
];
const processCount = record?.processFileCount ?? 0;
const additionalCount = record?.additionalFileCount ?? 0;
return (
<Modal
open={!!record}
title={
<div>
<div>项目档案查看</div>
<div className="epd-archive__subtitle">
查看项目执行过程中由系统形成及机构补充上传的档案资料
</div>
</div>
}
width={1000}
footer={<Button onClick={onClose}>关闭</Button>}
destroyOnHidden
onCancel={onClose}
className="epd-archive"
>
<div className="epd-archive__meta">
<div>
<span>项目名称</span>
<strong>{record?.projectName || "-"}</strong>
<small>{record?.projectNo}</small>
</div>
<div>
<span>评价机构</span>
<strong>{record?.orgName || "-"}</strong>
</div>
<div>
<span>评价类型</span>
<strong>{record?.evalTypeName || "-"}</strong>
</div>
<div>
<span>档案数量</span>
<strong>{processCount + additionalCount} </strong>
<small>
流程文档 {processCount} / 补充文件 {additionalCount}
</small>
</div>
</div>
<Input.Search
placeholder="输入文件名称"
allowClear
onSearch={(v) => setQuery((q) => ({ ...q, fileName: v, pageIndex: 1 }))}
className="epd-archive__search"
/>
<Table
rowKey="id"
size="small"
columns={columns}
dataSource={list}
loading={loading}
scroll={{ x: 800, y: 400 }}
pagination={{
total,
current: query.pageIndex,
pageSize: query.pageSize,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (t) => `${t}`,
onChange: (page, pageSize) =>
setQuery((q) => ({ ...q, pageIndex: page, pageSize })),
}}
/>
2026-08-26 10:43:20 +08:00
<Modal
open={!!viewRecord}
title="查看档案文件"
width={640}
footer={null}
destroyOnHidden
onCancel={() => setViewRecord(null)}
>
<Flex gap={8}>
{(viewRecord?.files || []).map((f, i) => (
<PreviewUrlButton key={f.url || i} url={f.url}>
{f.name || "查看文件"}
</PreviewUrlButton>
))}
</Flex>
</Modal>
2026-08-25 17:55:37 +08:00
</Modal>
);
}
export default ArchiveModal;