421 lines
15 KiB
JavaScript
421 lines
15 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
||
import dayjs from "dayjs";
|
||
import { Form, Table, Button, Modal, Input, Select, DatePicker, Card, Row, Col, Statistic, message, Tag } from "antd";
|
||
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 TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||
import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||
import { NS_QUAL_REVIEW } from "~/enumerate/namespace";
|
||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||
import { ON_SITE_REVIEW_STATUS_MAP } from "~/enumerate/constant";
|
||
import AttachmentUpload from "~/components/AttachmentUpload";
|
||
|
||
const { router } = tools;
|
||
|
||
const SiteReviewNotice = (props) => {
|
||
const [searchForm] = Form.useForm();
|
||
const [noticeForm] = Form.useForm();
|
||
const [resultForm] = Form.useForm();
|
||
const [noticeModalOpen, setNoticeModalOpen] = useState(false);
|
||
const [resultModalOpen, setResultModalOpen] = useState(false);
|
||
const [currentEditId, setCurrentEditId] = useState(null);
|
||
const [currentResultRow, setCurrentResultRow] = useState(null);
|
||
|
||
const {
|
||
qualReview,
|
||
queryQualOnSiteReviewPage,
|
||
queryQualOnSiteReviewStatistics,
|
||
addQualOnSiteReview,
|
||
updateQualOnSiteReview,
|
||
uploadQualOnSiteReviewResult,
|
||
fetchQualOnSiteReviewDetail,
|
||
fetchQualOnSiteReviewResultDetail,
|
||
queryOrgSelectList,
|
||
resetModelState,
|
||
} = props;
|
||
|
||
const {
|
||
qualOnSiteReviewList = [],
|
||
qualOnSiteReviewTotal = 0,
|
||
qualOnSiteReviewStats = {},
|
||
orgSelectList = [],
|
||
siteReviewNoticeLoading,
|
||
siteReviewStatisticsLoading,
|
||
siteReviewSaveLoading,
|
||
siteReviewResultLoading,
|
||
} = qualReview || {};
|
||
|
||
useEffect(() => {
|
||
queryQualOnSiteReviewStatistics();
|
||
queryOrgSelectList();
|
||
searchForm.setFieldsValue(router.query);
|
||
handleSearch();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (!noticeModalOpen) {
|
||
resetModelState(NS_QUAL_REVIEW, { siteReviewSaveLoading: false });
|
||
}
|
||
}, [noticeModalOpen]);
|
||
|
||
useEffect(() => {
|
||
if (!resultModalOpen) {
|
||
resetModelState(NS_QUAL_REVIEW, { siteReviewResultLoading: false });
|
||
}
|
||
}, [resultModalOpen]);
|
||
|
||
const handleSearch = () => {
|
||
|
||
queryQualOnSiteReviewPage({
|
||
...router.query,
|
||
});
|
||
};
|
||
|
||
const handleSearchSubmit = (values) => {
|
||
const { dateRange, ...rest } = values;
|
||
router.query = {
|
||
...router.query,
|
||
...rest,
|
||
...(dateRange && dateRange[0] && { reviewDateStartBegin: dateRange[0].format("YYYY-MM-DD") }),
|
||
...(dateRange && dateRange[1] && { reviewDateStartEnd: dateRange[1].format("YYYY-MM-DD") }),
|
||
current: 1,
|
||
size: 10,
|
||
};
|
||
handleSearch();
|
||
};
|
||
|
||
const handleReset = (values) => {
|
||
router.query = { ...values, current: 1, size: 10 , reviewDateStartBegin: undefined, reviewDateStartEnd: undefined };
|
||
handleSearch();
|
||
};
|
||
|
||
const handleOpenNoticeModal = () => {
|
||
setCurrentEditId(null);
|
||
noticeForm.resetFields();
|
||
setNoticeModalOpen(true);
|
||
};
|
||
|
||
const handleEditNotice = (record) => {
|
||
setCurrentEditId(record.id);
|
||
fetchQualOnSiteReviewDetail({ id: record.id }).then((res) => {
|
||
if (res?.success !== false) {
|
||
const detail = res.data;
|
||
noticeForm.setFieldsValue({
|
||
orgId: detail.orgId,
|
||
reviewDateStart: detail.reviewDateStart ? dayjs(detail.reviewDateStart) : null,
|
||
reviewLocation: detail.reviewLocation,
|
||
reviewMembers: detail.reviewMembers,
|
||
reviewLeader: detail.reviewLeader,
|
||
materialRequirements: detail.materialRequirements,
|
||
supervisorContact: detail.supervisorContact,
|
||
contactPhone: detail.contactPhone,
|
||
});
|
||
}
|
||
});
|
||
setNoticeModalOpen(true);
|
||
};
|
||
|
||
const handleSubmitNotice = () => {
|
||
noticeForm.validateFields().then((values) => {
|
||
const payload = {
|
||
...values,
|
||
reviewDateStart: values.reviewDateStart.format("YYYY-MM-DD HH:mm:ss"),
|
||
status: "AGENCY_PENDING_CONFIRMATION",
|
||
};
|
||
|
||
const action = currentEditId
|
||
? updateQualOnSiteReview({ ...payload, id: currentEditId })
|
||
: addQualOnSiteReview(payload);
|
||
|
||
action.then((res) => {
|
||
if (res?.success !== false) {
|
||
setNoticeModalOpen(false);
|
||
message.success(currentEditId ? "现场审查通知已修改" : "现场审查通知已下发机构端");
|
||
queryQualOnSiteReviewStatistics();
|
||
handleSearch();
|
||
}
|
||
});
|
||
}).catch(() => {});
|
||
};
|
||
|
||
const handleOpenResultModal = (record) => {
|
||
setCurrentResultRow(record);
|
||
resultForm.resetFields();
|
||
resultForm.setFieldsValue({
|
||
institution: record.unitName,
|
||
reviewDateStart: record.reviewDateStart,
|
||
});
|
||
fetchQualOnSiteReviewResultDetail({ id: record.id }).then((res) => {
|
||
if (res?.success !== false && res?.data) {
|
||
const detail = res.data;
|
||
resultForm.setFieldsValue({
|
||
reviewLeader: detail.reviewLeader || "",
|
||
reviewResult: detail.reviewResult,
|
||
reviewOpinion: detail.reviewOpinion || "",
|
||
fileList: (detail.fileList || []).map((item) => ({
|
||
url: item.url,
|
||
uid: item.id,
|
||
status: "done",
|
||
name: item.name,
|
||
})),
|
||
});
|
||
}
|
||
});
|
||
setResultModalOpen(true);
|
||
};
|
||
|
||
const handleSubmitResult = () => {
|
||
resultForm.validateFields().then((values) => {
|
||
|
||
|
||
const payload = {
|
||
id: currentResultRow.id,
|
||
...values,
|
||
};
|
||
|
||
uploadQualOnSiteReviewResult(payload).then((res) => {
|
||
if (res?.success !== false) {
|
||
setResultModalOpen(false);
|
||
message.success("审查结果已保存并上传");
|
||
queryQualOnSiteReviewStatistics();
|
||
handleSearch();
|
||
}
|
||
});
|
||
}).catch(() => {});
|
||
};
|
||
|
||
const columns = [
|
||
{ title: "机构名称", dataIndex: "unitName", ellipsis: true },
|
||
{
|
||
title: "现场审查时间", dataIndex: "reviewDateStart",
|
||
|
||
},
|
||
{ title: "审查地点", dataIndex: "reviewLocation", ellipsis: true },
|
||
{ title: "审查组成员", dataIndex: "reviewMembers", ellipsis: true },
|
||
{
|
||
title: "状态", dataIndex: "status",
|
||
render: (status) => {
|
||
const cfg = ON_SITE_REVIEW_STATUS_MAP[status];
|
||
return cfg ? <Tag color={cfg.color}>{cfg.label}</Tag> : (status || "-");
|
||
},
|
||
},
|
||
{
|
||
title: "操作", width: 220, fixed: "right",
|
||
render: (_, record) => (
|
||
<TableAction>
|
||
<Button type="link" size="small" onClick={() => handleEditNotice(record)}>
|
||
修改
|
||
</Button>
|
||
<Button type="link" size="small" onClick={() => handleOpenResultModal(record)}>
|
||
审查结果上传
|
||
</Button>
|
||
</TableAction>
|
||
),
|
||
},
|
||
];
|
||
|
||
return (
|
||
<PageLayout
|
||
title="现场审查通知"
|
||
extra={
|
||
<Button type="primary" onClick={handleOpenNoticeModal}>
|
||
下发现场审查通知
|
||
</Button>
|
||
}
|
||
>
|
||
|
||
|
||
<Row gutter={16} style={{ marginBottom: 16 }}>
|
||
<Col span={8}>
|
||
<Card size="small" loading={siteReviewStatisticsLoading}>
|
||
<Statistic title="通知总数" value={qualOnSiteReviewStats.totalCount ?? "-"} suffix="条" />
|
||
<div style={{ fontSize: 12, color: "#999", marginTop: 4 }}>{qualOnSiteReviewStats.annualCumulativeCount ?? 0} 条本年度累计</div>
|
||
</Card>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Card size="small" loading={siteReviewStatisticsLoading}>
|
||
<Statistic title="待开展审查" value={qualOnSiteReviewStats.pendingReviewNextThirtyDaysCount ?? ""} valueStyle={{ color: "#2563eb" }} suffix="条" />
|
||
<div style={{ fontSize: 12, color: "#999", marginTop: 4 }}>未来30日内</div>
|
||
</Card>
|
||
</Col>
|
||
<Col span={8}>
|
||
<Card size="small" loading={siteReviewStatisticsLoading}>
|
||
<Statistic title="已完成" value={qualOnSiteReviewStats.completedArchivedCount ?? "-"} valueStyle={{ color: "#059669" }} suffix="条" />
|
||
<div style={{ fontSize: 12, color: "#999", marginTop: 4 }}>审查结果已归档</div>
|
||
</Card>
|
||
</Col>
|
||
</Row>
|
||
|
||
<SearchForm
|
||
form={searchForm}
|
||
loading={siteReviewNoticeLoading}
|
||
formLine={[
|
||
<Form.Item key="unitName" name="unitName">
|
||
<ControlWrapper.Input label="机构名称" placeholder="请输入机构名称" allowClear />
|
||
</Form.Item>,
|
||
<Form.Item key="status" name="status">
|
||
<ControlWrapper.Select label="审查状态" placeholder="全部" allowClear style={{ width: "100%" }}>
|
||
{Object.entries(ON_SITE_REVIEW_STATUS_MAP).map(([value, cfg]) => (
|
||
<Select.Option key={value} value={value}>{cfg.label}</Select.Option>
|
||
))}
|
||
</ControlWrapper.Select>
|
||
</Form.Item>,
|
||
<Form.Item key="dateRange" name="dateRange">
|
||
<ControlWrapper.DatePicker.RangePicker label="审查日期" placeholder={["开始日期", "结束日期"]} />
|
||
</Form.Item>,
|
||
]}
|
||
onFinish={handleSearchSubmit}
|
||
onReset={handleReset}
|
||
style={{ marginBottom: 16 }}
|
||
/>
|
||
|
||
<Table
|
||
rowKey="id"
|
||
columns={columns}
|
||
dataSource={qualOnSiteReviewList}
|
||
loading={siteReviewNoticeLoading}
|
||
scroll={{ y: props.scrollY, x: 1200 }}
|
||
pagination={{
|
||
total: qualOnSiteReviewTotal,
|
||
showSizeChanger: true,
|
||
showQuickJumper: true,
|
||
showTotal: (total) => `共 ${total} 条`,
|
||
current: Number(router.query.current) || 1,
|
||
pageSize: Number(router.query.size) || 10,
|
||
onChange: (page, pageSize) => {
|
||
router.query = { ...router.query, current: page, size: pageSize };
|
||
handleSearch();
|
||
},
|
||
}}
|
||
/>
|
||
|
||
{/* 下发现场审查通知 弹窗 */}
|
||
<Modal
|
||
title={currentEditId ? "修改现场审查通知" : "下发现场审查通知"}
|
||
open={noticeModalOpen}
|
||
onCancel={() => setNoticeModalOpen(false)}
|
||
onOk={handleSubmitNotice}
|
||
okText={currentEditId ? "保存修改" : "确认下发"}
|
||
confirmLoading={siteReviewSaveLoading}
|
||
width={760}
|
||
destroyOnHidden
|
||
>
|
||
<Form form={noticeForm} layout="vertical">
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<Form.Item name="orgId" label="受审查机构" rules={[{ required: true, message: "请选择" }]}>
|
||
<Select placeholder="请选择" showSearch optionFilterProp="label">
|
||
{(orgSelectList || []).map((item) => (
|
||
<Select.Option key={item.id} value={item.id} label={item.unitName}>
|
||
{item.unitName}
|
||
</Select.Option>
|
||
))}
|
||
</Select>
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={12}>
|
||
<Form.Item name="reviewDateStart" label="现场审查开始时间" rules={[{ required: true, message: "请选择" }]}>
|
||
<DatePicker style={{ width: "100%" }} showTime />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<Form.Item name="reviewLocation" label="审查地点" rules={[{ required: true, message: "请输入" }]}>
|
||
<Input placeholder="机构办公场所、档案室及设备存放场所" maxLength={200} />
|
||
</Form.Item>
|
||
</Col>
|
||
{/* <Col span={12}>
|
||
<Form.Item name="reviewLeader" label="审查组长">
|
||
<Input placeholder="请输入" maxLength={50} />
|
||
</Form.Item>
|
||
</Col> */}
|
||
</Row>
|
||
<Form.Item name="reviewMembers" label="审查组成员名单" rules={[{ required: true, message: "请输入" }]}>
|
||
<Input placeholder="请输入审查组成员名单" maxLength={100} />
|
||
</Form.Item>
|
||
<Form.Item name="materialRequirements" label="材料准备要求">
|
||
<Input.TextArea
|
||
rows={4}
|
||
placeholder="请准备机构资质申请材料原件、人员档案、社保材料、办公场所和档案室证明、设备清单、过程控制制度及项目档案等资料。"
|
||
maxLength={500}
|
||
/>
|
||
</Form.Item>
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<Form.Item name="supervisorContact" label="监管联系人">
|
||
<Input placeholder="请输入监管联系人" maxLength={50} />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={12}>
|
||
<Form.Item name="contactPhone" label="联系电话" rules={[{ pattern: /^(\d{3,4}-?\d{7,8}|\d{11})$/, message: "请输入正确的联系电话" }]}>
|
||
<Input placeholder="请输入联系电话" maxLength={20} />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
</Form>
|
||
</Modal>
|
||
|
||
{/* 审查结果上传弹窗 */}
|
||
<Modal
|
||
title="上传审查结果"
|
||
open={resultModalOpen}
|
||
onCancel={() => setResultModalOpen(false)}
|
||
onOk={handleSubmitResult}
|
||
okText="保存审查结果"
|
||
confirmLoading={siteReviewResultLoading}
|
||
width={760}
|
||
destroyOnHidden
|
||
>
|
||
<div style={{ fontSize: 13, color: "#666", marginBottom: 16 }}>
|
||
上传现场审查结果文件,并保存审查结论和意见。
|
||
</div>
|
||
<Form form={resultForm} layout="vertical">
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<Form.Item name="institution" label="受审查机构">
|
||
<Input disabled />
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={12}>
|
||
<Form.Item name="reviewDateStart" label="现场审查日期">
|
||
<Input disabled />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
<Row gutter={16}>
|
||
<Col span={12}>
|
||
<Form.Item name="reviewResult" label="审查结论" rules={[{ required: true, message: "请选择" }]}>
|
||
<Select placeholder="请选择">
|
||
<Select.Option value={true}>审查通过</Select.Option>
|
||
|
||
<Select.Option value={false}>不通过</Select.Option>
|
||
</Select>
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={12}>
|
||
<Form.Item name="reviewLeader" label="审查组长">
|
||
<Input placeholder="请输入" />
|
||
</Form.Item>
|
||
</Col>
|
||
</Row>
|
||
<AttachmentUpload
|
||
name="fileList"
|
||
label="审查结果文件"
|
||
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
|
||
maxCount={10}
|
||
extra="支持上传审查记录、审查意见或现场审查结果文件,限定pdf和图片,最多10个。"
|
||
/>
|
||
<Form.Item name="reviewOpinion" label="审查意见">
|
||
<Input.TextArea rows={4} placeholder="填写现场审查发现、整改要求和结论依据" />
|
||
</Form.Item>
|
||
</Form>
|
||
</Modal>
|
||
</PageLayout>
|
||
);
|
||
};
|
||
|
||
export default Connect([NS_QUAL_REVIEW], true)(AntdTableFuncControl(SiteReviewNotice));
|