safety-eval-service-frontend/src/pages/Container/SafetyEvalBusiness/ProjectDocLibrary/index.js

344 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useState, useEffect } from "react";
import {
Form,
Table,
Button,
Input,
Select,
Space,
Modal,
Upload,
message,
Flex,
} from "antd";
import { PlusOutlined, UploadOutlined } from "@ant-design/icons";
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
import { NS_SAFETY_EVAL_BUSINESS } from "~/enumerate/namespace";
import { MATERIAL_TYPE_OPTIONS, MATERIAL_TYPE_MAP } from "~/enumerate/constant";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { tools } from "@cqsjjb/jjb-common-lib";
import AttachmentUpload from "~/components/AttachmentUpload";
import PreviewUrlButton from "~/components/PreviewUrlButton";
const { router } = tools;
const ProjectDocLibrary = (props) => {
const [searchForm] = Form.useForm();
const [uploadForm] = Form.useForm();
const [dataSource, setDataSource] = useState([]);
const [total, setTotal] = useState(0);
const [uploadModalOpen, setUploadModalOpen] = useState(false);
const [projectList, setProjectList] = useState([]);
const [docViewerVisible, setDocViewerVisible] = useState(false);
const [currentDocs, setCurrentDocs] = useState([]);
const { evalProjectDocLoading, evalProjectDocSaveLoading } = props.safetyEvalBusiness;
const getData = async (pagination) => {
const params = {
...router.query,
current: pagination?.current || router.query.current || 1,
size: pagination?.size || router.query.size || 10,
};
const res = await props.evalProjectDocPage(params);
if (res?.success !== false) {
setDataSource(res?.data || []);
setTotal(res?.total || 0);
}
};
const getProjectList = async () => {
const res = await props.evalProjectPage({ current: 1, size: 999 });
if (res?.success !== false) {
setProjectList(res?.data || []);
}
};
useEffect(() => {
searchForm.setFieldsValue(router.query);
getData();
}, []);
const handleSearch = (values) => {
router.query = { ...router.query, ...values, current: 1, size: 10 };
getData();
};
const handleReset = (values) => {
searchForm.resetFields();
router.query = { ...values, current: 1, size: 10 };
getData();
};
const handlePageChange = (pagination) => {
router.query = {
...router.query,
current: pagination.current,
size: pagination.pageSize,
};
getData(pagination);
};
const handleOpenUpload = () => {
uploadForm.resetFields();
getProjectList();
setUploadModalOpen(true);
};
const handleUploadSubmit = async () => {
const values = await uploadForm.validateFields();
const res = await props.evalProjectDocSave({
projectId: values.projectId,
projectDocUrl: values.file.map((item) => ({
project_doc_url: item.url,
materialName: item.name,
materialType: values.materialType,
})),
});
if (res?.success !== false) {
message.success("上传成功");
setUploadModalOpen(false);
getData();
}
};
const columns = [
{ title: "项目编号", dataIndex: "projectNo", width: 130 },
{
title: "项目名称",
dataIndex: "projectName",
ellipsis: true,
width: 200,
},
{ title: "企业名称", dataIndex: "customerName", ellipsis: true, width: 180 },
{ title: "评价类型", dataIndex: "evalTypeName", width: 120 },
{
title: "上传文档数量",
width: 110,
render: (_, record) => {
const map = record.projectDocUrlMap || {};
const count = Object.values(map).reduce((sum, arr) => sum + (arr?.length || 0), 0);
return <span>{count}</span>;
},
},
{
title: "操作",
width: 100,
fixed: "right",
render: (_, record) => (
<Button
type="link"
size="small"
onClick={() => {
const map = record.projectDocUrlMap || {};
setCurrentDocs(Object.entries(map));
setDocViewerVisible(true);
}}
>
查看文档
</Button>
),
},
];
return (
<PageLayout
title="项目资料管理"
extra={
<Button type="primary" icon={<PlusOutlined />} onClick={handleOpenUpload}>
上传资料
</Button>
}
>
<div style={{ fontSize: 13, color: "#666", marginBottom: 16 }}>
按项目归集企业和机构上传的资料先选择项目再查看项目文档
</div>
<SearchForm
form={searchForm}
loading={false}
formLine={[
<Form.Item key="projectName" name="projectName">
<ControlWrapper.Input
label="项目名称"
placeholder="输入项目名称"
allowClear
/>
</Form.Item>,
<Form.Item key="customerName" name="customerName">
<ControlWrapper.Input
label="企业名称"
placeholder="输入企业名称"
allowClear
/>
</Form.Item>,
]}
onFinish={handleSearch}
onReset={handleReset}
style={{ marginBottom: 16 }}
/>
<Table
rowKey="id"
columns={columns}
dataSource={dataSource}
loading={evalProjectDocLoading}
scroll={{ y: props.scrollY, x: 1000 }}
pagination={{
total,
current: router.query.current || 1,
pageSize: router.query.size || 10,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total}`,
}}
onChange={handlePageChange}
/>
<Modal
title="上传资料"
open={uploadModalOpen}
onCancel={() => setUploadModalOpen(false)}
onOk={handleUploadSubmit}
confirmLoading={evalProjectDocSaveLoading}
width={700}
destroyOnHide={true}
>
<Form form={uploadForm} layout="vertical" preserve={false}>
<Form.Item
label="项目"
name="projectId"
rules={[{ required: true, message: "请选择项目" }]}
>
<Select
placeholder="请选择项目"
showSearch
optionFilterProp="label"
options={projectList.map((p) => ({
label: `${p.projectNo} - ${p.projectName}`,
value: p.id,
}))}
/>
</Form.Item>
<Form.Item label='资料类别' name='materialType' rules={[{ required: true, message: "请选择资料类别" }]}>
<Select
placeholder="请选择资料类别"
showSearch
optionFilterProp="label"
options={MATERIAL_TYPE_OPTIONS}
/>
</Form.Item>
<AttachmentUpload
label="选择文件"
name="file"
maxCount={10}
accept=".pdf,.doc,.docx,.png,.jpg,.jpeg,.bmp,.webp"
extra="最多上传10个文件"
rules={[{ required: true, message: "请上传文件" }]}
/>
</Form>
</Modal>
<Modal
title="项目文档"
open={docViewerVisible}
onCancel={() => setDocViewerVisible(false)}
footer={null}
width={700}
destroyOnHide={true}
>
{currentDocs.map(([type, docs]) => (
<div key={type} style={{ marginBottom: 24 }}>
<h4 style={{ marginBottom: 8, color: "#333" }}>{MATERIAL_TYPE_MAP[type] || type}</h4>
<Table
rowKey="project_doc_url"
dataSource={docs}
pagination={false}
size="small"
columns={[
{ title: "文件名称", dataIndex: "materialName", ellipsis: true },
{
title: "操作",
width: 100,
render: (_, record) => (
<PreviewUrlButton url={record.project_doc_url} />
),
},
]}
/>
</div>
))}
{currentDocs.length === 0 && (
<div style={{ textAlign: "center", color: "#999", padding: 40 }}>暂无文档</div>
)}
</Modal>
</PageLayout>
);
};
/** 上传按钮字段组件 - 上传后回填 url */
const UploadButtonField = ({ value, onChange }) => {
const [uploading, setUploading] = useState(false);
return (
<UploadButtonInner
value={value}
onChange={onChange}
uploading={uploading}
setUploading={setUploading}
/>
);
};
const UploadButtonInner = ({ value, onChange, uploading, setUploading }) => {
return (
<Flex gap={8} align="center">
{value && (
<span style={{ fontSize: 12, color: "#999", maxWidth: 120, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
{value.split("/").pop()}
</span>
)}
<Upload
showUploadList={false}
action={`${window.process.env.app.API_HOST}/safetyEval/file/upload`}
headers={{ token: sessionStorage.getItem("token") }}
beforeUpload={(file) => {
const ext = "." + file.name.split(".").pop().toLowerCase();
if (ext === ".gif") {
message.error("不支持 GIF 格式图片上传");
return Upload.LIST_IGNORE;
}
return true;
}}
onChange={(info) => {
if (info.file.status === "uploading") {
setUploading(true);
} else if (info.file.status === "done") {
setUploading(false);
const url = info.file.response?.data;
if (url) {
onChange?.(url);
}
} else if (info.file.status === "error") {
setUploading(false);
message.error("上传失败");
}
}}
>
<Button icon={<UploadOutlined />} loading={uploading} size="small">
选择文件
</Button>
</Upload>
</Flex>
);
};
export default Connect(
[NS_SAFETY_EVAL_BUSINESS],
true,
)(AntdTableFuncControl(ProjectDocLibrary));