feat
parent
634e818ed9
commit
d1ca41a530
|
|
@ -63,3 +63,18 @@ export const evalProjectDetail = declareRequest(
|
||||||
"evalProjectDetailLoading",
|
"evalProjectDetailLoading",
|
||||||
"Get > /safetyEval/institution/eval-project/get",
|
"Get > /safetyEval/institution/eval-project/get",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const evalProjectDelete = declareRequest(
|
||||||
|
"evalProjectDeleteLoading",
|
||||||
|
"Post > @/safetyEval/institution/eval-project/delete",
|
||||||
|
);
|
||||||
|
|
||||||
|
export const evalProjectDocPage = declareRequest(
|
||||||
|
"evalProjectDocLoading",
|
||||||
|
"Get > /safetyEval/institution/eval-project-doc/page",
|
||||||
|
);
|
||||||
|
|
||||||
|
export const evalProjectDocSave = declareRequest(
|
||||||
|
"evalProjectDocSaveLoading",
|
||||||
|
"Post > @/safetyEval/institution/eval-project-doc/save",
|
||||||
|
);
|
||||||
|
|
@ -4,7 +4,7 @@ import { PlusOutlined } from "@ant-design/icons";
|
||||||
const isImage = (url) =>
|
const isImage = (url) =>
|
||||||
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
|
/\.(png|jpe?g|gif|bmp|webp|svg)(\?.*)?$/i.test(url || "");
|
||||||
|
|
||||||
export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept, extra }) {
|
export default function AttachmentUpload({ name, label, disabled = false, maxCount, accept, extra ,rules }) {
|
||||||
const [previewImage, setPreviewImage] = useState("");
|
const [previewImage, setPreviewImage] = useState("");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -14,6 +14,7 @@ export default function AttachmentUpload({ name, label, disabled = false, maxCou
|
||||||
label={label}
|
label={label}
|
||||||
extra={extra}
|
extra={extra}
|
||||||
valuePropName="fileList"
|
valuePropName="fileList"
|
||||||
|
rules={rules}
|
||||||
getValueProps={(value) => {
|
getValueProps={(value) => {
|
||||||
if (Array.isArray(value)) {
|
if (Array.isArray(value)) {
|
||||||
return { fileList: value };
|
return { fileList: value };
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,11 @@ export const EVAL_TYPE_OPTIONS = [
|
||||||
{ label: "安全现状评价", value: "STATUS" },
|
{ label: "安全现状评价", value: "STATUS" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const EVAL_TYPE_MAP = EVAL_TYPE_OPTIONS.reduce((acc, cur) => {
|
||||||
|
acc[cur.value] = cur.label;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
/** 登记半径选项 */
|
/** 登记半径选项 */
|
||||||
export const CHECKIN_RADIUS_OPTIONS = [
|
export const CHECKIN_RADIUS_OPTIONS = [
|
||||||
{ label: "100 米", value: 100 },
|
{ label: "100 米", value: 100 },
|
||||||
|
|
@ -314,3 +319,16 @@ export const CHECKIN_RADIUS_OPTIONS = [
|
||||||
{ label: "300 米", value: 300 },
|
{ label: "300 米", value: 300 },
|
||||||
{ label: "500 米", value: 500 },
|
{ label: "500 米", value: 500 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** 项目规则 — 资料类别枚举 */
|
||||||
|
export const MATERIAL_TYPE_OPTIONS = [
|
||||||
|
{ label: "现场踏勘资料", value: "SITE_SURVEY" },
|
||||||
|
{ label: "报告资料", value: "REPORT" },
|
||||||
|
{ label: "整改资料", value: "RECTIFICATION" },
|
||||||
|
{ label: "其他项目资料", value: "OTHER" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const MATERIAL_TYPE_MAP = MATERIAL_TYPE_OPTIONS.reduce((acc, cur) => {
|
||||||
|
acc[cur.value] = cur.label;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
@ -211,6 +211,11 @@ const menuItems = [
|
||||||
label: "安评项目管理",
|
label: "安评项目管理",
|
||||||
icon: <FileTextOutlined />,
|
icon: <FileTextOutlined />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "/safetyEval/container/SafetyEvalBusiness/EvalProject/ProjectDocLibrary",
|
||||||
|
label: "项目资料管理",
|
||||||
|
icon: <FileTextOutlined />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "/safetyEval/container/SafetyEvalBusiness/CustomerManage",
|
key: "/safetyEval/container/SafetyEvalBusiness/CustomerManage",
|
||||||
label: "安评客户管理",
|
label: "安评客户管理",
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,7 @@ import {
|
||||||
Flex,
|
Flex,
|
||||||
Space,
|
Space,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import {
|
import { PlusOutlined } from "@ant-design/icons";
|
||||||
PlusOutlined,
|
|
||||||
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
import ControlWrapper from "@cqsjjb/jjb-react-admin-component/ControlWrapper";
|
||||||
|
|
@ -29,6 +26,7 @@ import {
|
||||||
district,
|
district,
|
||||||
ENTERPRISE_STATUS_OPTIONS,
|
ENTERPRISE_STATUS_OPTIONS,
|
||||||
ENTERPRISE_SCALE_OPTIONS,
|
ENTERPRISE_SCALE_OPTIONS,
|
||||||
|
EVAL_TYPE_MAP,
|
||||||
} from "~/enumerate/constant";
|
} from "~/enumerate/constant";
|
||||||
import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
|
import { QUALIFICATION_INDUSTRY_OPTIONS } from "~/enumerate/enterpriseOptions";
|
||||||
import { phoneRule, creditCodeRule } from "~/utils/validators";
|
import { phoneRule, creditCodeRule } from "~/utils/validators";
|
||||||
|
|
@ -45,25 +43,30 @@ const CustomerManage = (props) => {
|
||||||
const [detailOpen, setDetailOpen] = useState(false);
|
const [detailOpen, setDetailOpen] = useState(false);
|
||||||
const [currentDetail, setCurrentDetail] = useState(null);
|
const [currentDetail, setCurrentDetail] = useState(null);
|
||||||
const [editingId, setEditingId] = useState(null);
|
const [editingId, setEditingId] = useState(null);
|
||||||
const {customerLoading, customerModifyLoading, customerSaveLoading}= props.safetyEvalBusiness;
|
const { customerLoading, customerModifyLoading, customerSaveLoading } =
|
||||||
|
props.safetyEvalBusiness;
|
||||||
|
|
||||||
const getData = async (pagination) => {
|
const getData = async (pagination) => {
|
||||||
const params = {
|
const params = {
|
||||||
...router.query,
|
...router.query,
|
||||||
current: pagination?.current || router.query.current || 1,
|
current: pagination?.current || router.query.current || 1,
|
||||||
size: pagination?.size || router.query.size || 10,
|
size: pagination?.size || router.query.size || 10,
|
||||||
};
|
};
|
||||||
const res = await props.customerPage(params);
|
const res = await props.customerPage(params);
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
setDataSource(res?.data || []);
|
setDataSource(res?.data || []);
|
||||||
setTotal(res?.total || 0);
|
setTotal(res?.total || 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMapConfirm = (location) => {
|
const handleMapConfirm = (location) => {
|
||||||
const { lng, lat, address } = location;
|
const { lng, lat, address } = location;
|
||||||
|
|
||||||
modalForm.setFieldsValue({ longitude: lng, latitude: lat, businessAddress: address });
|
modalForm.setFieldsValue({
|
||||||
|
longitude: lng,
|
||||||
|
latitude: lat,
|
||||||
|
businessAddress: address,
|
||||||
|
});
|
||||||
setMapPickerVisible(false);
|
setMapPickerVisible(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -100,21 +103,21 @@ const CustomerManage = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenEdit = async (record) => {
|
const handleOpenEdit = async (record) => {
|
||||||
const res = await props.customerDetail({ id: record.id });
|
const res = await props.customerDetail({ id: record.id });
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
setEditingId(record.id);
|
setEditingId(record.id);
|
||||||
setCurrentDetail(res?.data || {});
|
setCurrentDetail(res?.data || {});
|
||||||
modalForm.setFieldsValue(res?.data || {});
|
modalForm.setFieldsValue(res?.data || {});
|
||||||
setModalOpen(true);
|
setModalOpen(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewDetail = async (record) => {
|
const handleViewDetail = async (record) => {
|
||||||
const res = await props.customerDetail({ id: record.id });
|
const res = await props.customerDetail({ id: record.id });
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
setCurrentDetail(res?.data || {});
|
setCurrentDetail(res?.data || {});
|
||||||
setDetailOpen(true);
|
setDetailOpen(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (record) => {
|
const handleDelete = async (record) => {
|
||||||
|
|
@ -122,29 +125,34 @@ const CustomerManage = (props) => {
|
||||||
title: "确认删除",
|
title: "确认删除",
|
||||||
content: "确定要删除这个客户吗?",
|
content: "确定要删除这个客户吗?",
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
await props.customerDelete({ data: record.id });
|
await props.customerDelete({ data: record.id });
|
||||||
message.success("删除成功");
|
message.success("删除成功");
|
||||||
getData();
|
getData();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCreateSubmit = () => {
|
const handleCreateSubmit = () => {
|
||||||
modalForm.validateFields().then(async (values) => {
|
modalForm.validateFields().then(async (values) => {
|
||||||
if (editingId) {
|
if (editingId) {
|
||||||
await props.customerModify({ ...values, id: editingId });
|
await props.customerModify({ ...values, id: editingId });
|
||||||
} else {
|
} else {
|
||||||
await props.customerSave(values);
|
await props.customerSave(values);
|
||||||
}
|
}
|
||||||
message.success(editingId ? "修改成功" : "创建成功");
|
message.success(editingId ? "修改成功" : "创建成功");
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
getData();
|
getData();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
|
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
|
||||||
{ title: "客户名称", dataIndex: "customerName", ellipsis: true, width: 220 },
|
{
|
||||||
|
title: "客户名称",
|
||||||
|
dataIndex: "customerName",
|
||||||
|
ellipsis: true,
|
||||||
|
width: 220,
|
||||||
|
},
|
||||||
{ title: "统一社会信用代码", dataIndex: "creditCode", width: 220 },
|
{ title: "统一社会信用代码", dataIndex: "creditCode", width: 220 },
|
||||||
{ title: "客户联系人", dataIndex: "principalName", width: 150 },
|
{ title: "客户联系人", dataIndex: "principalName", width: 150 },
|
||||||
{ title: "联系人电话", dataIndex: "principalPhone", width: 160 },
|
{ title: "联系人电话", dataIndex: "principalPhone", width: 160 },
|
||||||
|
|
@ -304,7 +312,11 @@ const CustomerManage = (props) => {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="creditCode" label="统一社会信用代码" rules={[creditCodeRule(false)]}>
|
<Form.Item
|
||||||
|
name="creditCode"
|
||||||
|
label="统一社会信用代码"
|
||||||
|
rules={[creditCodeRule(false)]}
|
||||||
|
>
|
||||||
<Input placeholder="请输入" maxLength={18} />
|
<Input placeholder="请输入" maxLength={18} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -376,7 +388,11 @@ const CustomerManage = (props) => {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="principalPhone" label="负责人电话" rules={[phoneRule("负责人电话", false)]}>
|
<Form.Item
|
||||||
|
name="principalPhone"
|
||||||
|
label="负责人电话"
|
||||||
|
rules={[phoneRule("负责人电话", false)]}
|
||||||
|
>
|
||||||
<Input placeholder="请输入" maxLength={20} />
|
<Input placeholder="请输入" maxLength={20} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -389,7 +405,11 @@ const CustomerManage = (props) => {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="legalRepresentativePhone" label="法人电话" rules={[phoneRule("法人电话", false)]}>
|
<Form.Item
|
||||||
|
name="legalRepresentativePhone"
|
||||||
|
label="法人电话"
|
||||||
|
rules={[phoneRule("法人电话", false)]}
|
||||||
|
>
|
||||||
<Input placeholder="请输入" maxLength={20} />
|
<Input placeholder="请输入" maxLength={20} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
@ -487,39 +507,30 @@ const CustomerManage = (props) => {
|
||||||
<h4 style={{ margin: "12px 0 8px" }}>安全评价项目列表</h4>
|
<h4 style={{ margin: "12px 0 8px" }}>安全评价项目列表</h4>
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
dataSource={[
|
dataSource={currentDetail.projects || []}
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: `${currentDetail.customerName}安全现状评价`,
|
|
||||||
type: "安全现状评价",
|
|
||||||
date: "2026-06-15",
|
|
||||||
status: "已完成",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: `${currentDetail.customerName}安全验收评价`,
|
|
||||||
type: "安全验收评价",
|
|
||||||
date: "2023-07-20",
|
|
||||||
status: "已归档",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: `${currentDetail.customerName}安全预评价`,
|
|
||||||
type: "安全预评价",
|
|
||||||
date: "2020-08-12",
|
|
||||||
status: "已归档",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
columns={[
|
columns={[
|
||||||
{ title: "项目名称", dataIndex: "name" },
|
{ title: "项目名称", dataIndex: "projectName" },
|
||||||
{ title: "评价类别", dataIndex: "type", width: 120 },
|
{
|
||||||
{ title: "报告完成日期", dataIndex: "date", width: 130 },
|
title: "评价类型",
|
||||||
|
dataIndex: "evalTypeCode",
|
||||||
|
width: 120,
|
||||||
|
render: (s) => EVAL_TYPE_MAP[s] || s || "",
|
||||||
|
},
|
||||||
|
{ title: "报告完成日期", dataIndex: "planEndDate", width: 130 },
|
||||||
{
|
{
|
||||||
title: "项目状态",
|
title: "项目状态",
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
width: 100,
|
width: 100,
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
render: (s) => <span style={{ color: "#52c41a" }}>{s}</span>,
|
render: (phase) => {
|
||||||
|
const color =
|
||||||
|
phase === "延期"
|
||||||
|
? "error"
|
||||||
|
: phase === "正常"
|
||||||
|
? "success"
|
||||||
|
: undefined;
|
||||||
|
return <Tag color={color}>{phase || "-"}</Tag>;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,12 @@ import {
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Tag,
|
Tag,
|
||||||
|
Modal,
|
||||||
|
message,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||||
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
import SearchForm from "@cqsjjb/jjb-react-admin-component/SearchForm";
|
||||||
|
|
@ -66,6 +69,18 @@ const EvalProject = (props) => {
|
||||||
getData(pagination);
|
getData(pagination);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleDelete = (record) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "确认删除",
|
||||||
|
content: "确定要删除这个项目吗?",
|
||||||
|
onOk: async () => {
|
||||||
|
await props.evalProjectDelete({ data: [record.id] });
|
||||||
|
message.success("删除成功");
|
||||||
|
getData();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
|
{ title: "序号", width: 60, render: (_, __, index) => index + 1 },
|
||||||
{
|
{
|
||||||
|
|
@ -97,7 +112,7 @@ const EvalProject = (props) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 100,
|
width: 160,
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<Space>
|
||||||
|
|
@ -108,6 +123,15 @@ const EvalProject = (props) => {
|
||||||
>
|
>
|
||||||
查看
|
查看
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
danger
|
||||||
|
|
||||||
|
onClick={() => handleDelete(record)}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,347 @@
|
||||||
|
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";
|
||||||
|
|
||||||
|
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();
|
||||||
|
await props.evalProjectDocSave({
|
||||||
|
projectId: values.projectId,
|
||||||
|
projectDocUrl: values.file.map((item) => ({
|
||||||
|
project_doc_url: item.url,
|
||||||
|
materialName: item.name,
|
||||||
|
materialType: values.materialType,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
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) => (
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
onClick={() => window.open(record.project_doc_url)}
|
||||||
|
>
|
||||||
|
预览
|
||||||
|
</Button>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</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));
|
||||||
Loading…
Reference in New Issue