344 lines
10 KiB
JavaScript
344 lines
10 KiB
JavaScript
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)); |