feat
parent
81209bb537
commit
c05a311bd6
|
|
@ -28,6 +28,8 @@
|
||||||
"@eigenpal/docx-editor-react": "^1.9.0",
|
"@eigenpal/docx-editor-react": "^1.9.0",
|
||||||
"@rc-component/motion": "^1.0.0",
|
"@rc-component/motion": "^1.0.0",
|
||||||
"@rc-component/util": "^1.11.1",
|
"@rc-component/util": "^1.11.1",
|
||||||
|
"pizzip": "latest",
|
||||||
|
"docxtemplater": "latest",
|
||||||
"antd": "^5.29.2",
|
"antd": "^5.29.2",
|
||||||
"china-division": "^2.7.0",
|
"china-division": "^2.7.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
|
|
|
||||||
1934
pnpm-lock.yaml
1934
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
|
@ -456,7 +456,7 @@ function OrgAccountPage(props) {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Form.Item name="businessAddress" label="企事业单位经营地址">
|
<Form.Item name="businessAddress" label="经营地址">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,8 @@ import {
|
||||||
DatePicker,
|
DatePicker,
|
||||||
Space,
|
Space,
|
||||||
ConfigProvider,
|
ConfigProvider,
|
||||||
Card,
|
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import {
|
import { DownloadOutlined, ExportOutlined } from "@ant-design/icons";
|
||||||
DownloadOutlined,
|
|
||||||
ExportOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||||
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";
|
||||||
|
|
@ -27,6 +23,7 @@ import { AntdTableFuncControl } from "@cqsjjb/jjb-common-decorator/antd";
|
||||||
import { NS_REGULATOR_EVAL_REPORT, NS_ORG_INFO } from "~/enumerate/namespace";
|
import { NS_REGULATOR_EVAL_REPORT, NS_ORG_INFO } from "~/enumerate/namespace";
|
||||||
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
import { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||||
import { tools } from "@cqsjjb/jjb-common-lib";
|
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||||
|
import { renderDocxBlob, fillDocxTemplate } from "~/utils/fillDocxTemplate";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
|
|
@ -36,6 +33,10 @@ const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
const API_HOST = window.process?.env?.app?.API_HOST || "";
|
const API_HOST = window.process?.env?.app?.API_HOST || "";
|
||||||
|
|
||||||
|
/** 监督检查单 docx 模板(线上) */
|
||||||
|
const CHECKLIST_TEMPLATE_URL =
|
||||||
|
"https://test-dragon-yf-pub.oss-cn-hangzhou.aliyuncs.com/jjb/6a7c36c2e4b0435c62b76a34.docx";
|
||||||
|
|
||||||
/** 原型 CSS:--radius 8 / --radius-lg 12 */
|
/** 原型 CSS:--radius 8 / --radius-lg 12 */
|
||||||
const PROTO_THEME = {
|
const PROTO_THEME = {
|
||||||
token: { borderRadius: 8, borderRadiusLG: 12, borderRadiusSM: 6 },
|
token: { borderRadius: 8, borderRadiusLG: 12, borderRadiusSM: 6 },
|
||||||
|
|
@ -264,6 +265,7 @@ const EvalReportDatabase = (props) => {
|
||||||
const [spotCheckData, setSpotCheckData] = useState([]);
|
const [spotCheckData, setSpotCheckData] = useState([]);
|
||||||
const [orgOptions, setOrgOptions] = useState([]);
|
const [orgOptions, setOrgOptions] = useState([]);
|
||||||
const [spotCheckForm] = Form.useForm();
|
const [spotCheckForm] = Form.useForm();
|
||||||
|
const [viewChecklistLoading, setViewChecklistLoading] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
regulatorEvalReportPageLoading,
|
regulatorEvalReportPageLoading,
|
||||||
|
|
@ -274,6 +276,9 @@ const EvalReportDatabase = (props) => {
|
||||||
regulatorSpotCheckReportLoading,
|
regulatorSpotCheckReportLoading,
|
||||||
} = props.regulatorEvalReport || {};
|
} = props.regulatorEvalReport || {};
|
||||||
|
|
||||||
|
const { registeredOrgListData } = props.orgInfo || {};
|
||||||
|
|
||||||
|
|
||||||
const fetchSummary = async () => {
|
const fetchSummary = async () => {
|
||||||
const res = await props.regulatorEvalReportSummary();
|
const res = await props.regulatorEvalReportSummary();
|
||||||
if (res?.success !== false) {
|
if (res?.success !== false) {
|
||||||
|
|
@ -289,8 +294,12 @@ const EvalReportDatabase = (props) => {
|
||||||
};
|
};
|
||||||
// RangePicker 拆成接口字段
|
// RangePicker 拆成接口字段
|
||||||
if (params.archiveTimeRange?.length === 2) {
|
if (params.archiveTimeRange?.length === 2) {
|
||||||
params.archiveTimeStart = dayjs(params.archiveTimeRange[0]).format("YYYY-MM-DD");
|
params.archiveTimeStart = dayjs(params.archiveTimeRange[0]).format(
|
||||||
params.archiveTimeEnd = dayjs(params.archiveTimeRange[1]).format("YYYY-MM-DD");
|
"YYYY-MM-DD",
|
||||||
|
);
|
||||||
|
params.archiveTimeEnd = dayjs(params.archiveTimeRange[1]).format(
|
||||||
|
"YYYY-MM-DD",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
delete params.archiveTimeRange;
|
delete params.archiveTimeRange;
|
||||||
|
|
||||||
|
|
@ -361,11 +370,96 @@ const EvalReportDatabase = (props) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getChecklistData = () => {
|
||||||
|
const data = registeredOrgListData.find(
|
||||||
|
(o) => o.id === spotCheckForm.getFieldValue("orgId"),
|
||||||
|
);
|
||||||
|
const unitName = data.unitName;
|
||||||
|
return {
|
||||||
|
unitName,
|
||||||
|
fileName: `安全评价报告监督检查单_${unitName}.docx`,
|
||||||
|
data: {
|
||||||
|
unitName,
|
||||||
|
creditCode: data.creditCode || "",
|
||||||
|
qualificationCertNo: data.qualificationCertNo || "",
|
||||||
|
legalRepresentative: data.legalRepresentative || "",
|
||||||
|
businessAddress: data.businessAddress || "",
|
||||||
|
principalName: data.principalName || "",
|
||||||
|
principalPhone: data.principalPhone || "",
|
||||||
|
checkTypeName: getOptionLabel(
|
||||||
|
CHECK_TYPE_OPTIONS,
|
||||||
|
spotCheckForm.getFieldValue("checkType"),
|
||||||
|
),
|
||||||
|
planCheckDate: spotCheckForm.getFieldValue("planCheckDate")
|
||||||
|
? dayjs(spotCheckForm.getFieldValue("planCheckDate")).format(
|
||||||
|
"YYYY年M月D日",
|
||||||
|
)
|
||||||
|
: "",
|
||||||
|
checkLeader: spotCheckForm.getFieldValue("checkLeader") || "",
|
||||||
|
checkLocation: spotCheckForm.getFieldValue("checkLocation") || "",
|
||||||
|
checkMembers: spotCheckForm.getFieldValue("checkMembers") || "",
|
||||||
|
reports: spotCheckData.map((item,index) => ({
|
||||||
|
index: index + 1,
|
||||||
|
reportNo: item.reportNo || "",
|
||||||
|
reportName: item.reportName || "",
|
||||||
|
evalTypeName: item.evalTypeName || "",
|
||||||
|
industryName: item.industryName || "",
|
||||||
|
issueDate: item.issueDate || "",
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadDocx = async (blob, fileName) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("file", new File([blob], fileName), fileName);
|
||||||
|
const res = await fetch(
|
||||||
|
`${window.process.env.app.API_HOST}/safetyEval/file/upload`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { token: sessionStorage.getItem("token") },
|
||||||
|
body: formData,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const json = await res.json();
|
||||||
|
const url = json?.data?.url;
|
||||||
|
if (!url) throw new Error("上传失败");
|
||||||
|
return url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleViewChecklist = async () => {
|
||||||
|
setViewChecklistLoading(true);
|
||||||
|
try {
|
||||||
|
const { fileName, data } = getChecklistData();
|
||||||
|
const blob = await renderDocxBlob(CHECKLIST_TEMPLATE_URL, data);
|
||||||
|
const url = await uploadDocx(blob, fileName);
|
||||||
|
window.open(url, "_blank");
|
||||||
|
message.success("检查单已生成");
|
||||||
|
} finally {
|
||||||
|
setViewChecklistLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDownloadChecklist = async () => {
|
||||||
|
const { fileName, data } = getChecklistData();
|
||||||
|
await fillDocxTemplate(
|
||||||
|
CHECKLIST_TEMPLATE_URL,
|
||||||
|
data,
|
||||||
|
fileName,
|
||||||
|
{ download: true },
|
||||||
|
);
|
||||||
|
message.success("检查单已下载");
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = (values) => {
|
const handleSearch = (values) => {
|
||||||
const next = { ...values, current: 1, size: router.query.size || 20 };
|
const next = { ...values, current: 1, size: router.query.size || 20 };
|
||||||
if (next.archiveTimeRange?.length === 2) {
|
if (next.archiveTimeRange?.length === 2) {
|
||||||
next.archiveTimeStart = dayjs(next.archiveTimeRange[0]).format("YYYY-MM-DD");
|
next.archiveTimeStart = dayjs(next.archiveTimeRange[0]).format(
|
||||||
next.archiveTimeEnd = dayjs(next.archiveTimeRange[1]).format("YYYY-MM-DD");
|
"YYYY-MM-DD",
|
||||||
|
);
|
||||||
|
next.archiveTimeEnd = dayjs(next.archiveTimeRange[1]).format(
|
||||||
|
"YYYY-MM-DD",
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
next.archiveTimeStart = undefined;
|
next.archiveTimeStart = undefined;
|
||||||
next.archiveTimeEnd = undefined;
|
next.archiveTimeEnd = undefined;
|
||||||
|
|
@ -515,15 +609,6 @@ const EvalReportDatabase = (props) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenSpotcheckList = async (record) => {
|
|
||||||
setCurrentReport(record);
|
|
||||||
setSpotcheckVisible(true);
|
|
||||||
setSpotcheckList([]);
|
|
||||||
const res = await props.regulatorSpotcheckList({ reportId: record.id });
|
|
||||||
if (res?.success !== false) {
|
|
||||||
setSpotcheckList(res?.data || []);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenSpotcheckStart = (record) => {
|
const handleOpenSpotcheckStart = (record) => {
|
||||||
setCurrentReport(record);
|
setCurrentReport(record);
|
||||||
|
|
@ -592,7 +677,6 @@ const EvalReportDatabase = (props) => {
|
||||||
title: "报告编号",
|
title: "报告编号",
|
||||||
dataIndex: "reportNo",
|
dataIndex: "reportNo",
|
||||||
width: 150,
|
width: 150,
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "报告名称",
|
title: "报告名称",
|
||||||
|
|
@ -640,12 +724,8 @@ const EvalReportDatabase = (props) => {
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<TableAction size={4} wrap>
|
<TableAction size={4} wrap>
|
||||||
<a onClick={() => handleViewDetail(record)}>
|
<a onClick={() => handleViewDetail(record)}>查看报告</a>
|
||||||
查看报告
|
<a onClick={() => handleOpenAi(record)}>AI分析</a>
|
||||||
</a>
|
|
||||||
<a onClick={() => handleOpenAi(record)}>
|
|
||||||
AI分析
|
|
||||||
</a>
|
|
||||||
</TableAction>
|
</TableAction>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
@ -669,8 +749,10 @@ const EvalReportDatabase = (props) => {
|
||||||
dataIndex: "checkResultName",
|
dataIndex: "checkResultName",
|
||||||
width: 90,
|
width: 90,
|
||||||
render: (val, record) => {
|
render: (val, record) => {
|
||||||
if (record.checkResultCode === 1) return <span className="redb-tag redb-tag-success">合格</span>;
|
if (record.checkResultCode === 1)
|
||||||
if (record.checkResultCode === 2) return <span className="redb-tag redb-tag-danger">不合格</span>;
|
return <span className="redb-tag redb-tag-success">合格</span>;
|
||||||
|
if (record.checkResultCode === 2)
|
||||||
|
return <span className="redb-tag redb-tag-danger">不合格</span>;
|
||||||
return val || "-";
|
return val || "-";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -697,8 +779,10 @@ const EvalReportDatabase = (props) => {
|
||||||
dataIndex: "reviewResultName",
|
dataIndex: "reviewResultName",
|
||||||
width: 100,
|
width: 100,
|
||||||
render: (val, record) => {
|
render: (val, record) => {
|
||||||
if (record.reviewResultCode === 1) return <span className="redb-tag redb-tag-success">通过</span>;
|
if (record.reviewResultCode === 1)
|
||||||
if (record.reviewResultCode === 2) return <span className="redb-tag redb-tag-danger">不通过</span>;
|
return <span className="redb-tag redb-tag-success">通过</span>;
|
||||||
|
if (record.reviewResultCode === 2)
|
||||||
|
return <span className="redb-tag redb-tag-danger">不通过</span>;
|
||||||
return val || "-";
|
return val || "-";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -709,11 +793,7 @@ const EvalReportDatabase = (props) => {
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
// 有整改反馈且尚未复核时可复核
|
// 有整改反馈且尚未复核时可复核
|
||||||
if (record.rectifyFeedback && !record.reviewResultCode) {
|
if (record.rectifyFeedback && !record.reviewResultCode) {
|
||||||
return (
|
return <a onClick={() => handleOpenReview(record)}>复核</a>;
|
||||||
<a onClick={() => handleOpenReview(record)}>
|
|
||||||
复核
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return "-";
|
return "-";
|
||||||
},
|
},
|
||||||
|
|
@ -796,7 +876,10 @@ const EvalReportDatabase = (props) => {
|
||||||
/>
|
/>
|
||||||
</Form.Item>,
|
</Form.Item>,
|
||||||
<Form.Item key="archiveTimeRange" name="archiveTimeRange">
|
<Form.Item key="archiveTimeRange" name="archiveTimeRange">
|
||||||
<RangePicker style={{ width: "100%" }} placeholder={["报送时间起", "报送时间止"]} />
|
<RangePicker
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
placeholder={["报送时间起", "报送时间止"]}
|
||||||
|
/>
|
||||||
</Form.Item>,
|
</Form.Item>,
|
||||||
]}
|
]}
|
||||||
onFinish={handleSearch}
|
onFinish={handleSearch}
|
||||||
|
|
@ -811,10 +894,18 @@ const EvalReportDatabase = (props) => {
|
||||||
<Button type="primary" size="small" onClick={handleOpenSpotCheck}>
|
<Button type="primary" size="small" onClick={handleOpenSpotCheck}>
|
||||||
抽检
|
抽检
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="small" icon={<DownloadOutlined />} onClick={handleBatchDownload}>
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<DownloadOutlined />}
|
||||||
|
onClick={handleBatchDownload}
|
||||||
|
>
|
||||||
批量下载
|
批量下载
|
||||||
</Button>
|
</Button>
|
||||||
<Button size="small" icon={<ExportOutlined />} onClick={handleOpenExport}>
|
<Button
|
||||||
|
size="small"
|
||||||
|
icon={<ExportOutlined />}
|
||||||
|
onClick={handleOpenExport}
|
||||||
|
>
|
||||||
导出目录
|
导出目录
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -888,12 +979,18 @@ const EvalReportDatabase = (props) => {
|
||||||
[
|
[
|
||||||
"评价类型",
|
"评价类型",
|
||||||
detailData.evalTypeName ||
|
detailData.evalTypeName ||
|
||||||
getOptionLabel(EVAL_TYPE_OPTIONS, detailData.evalTypeCode),
|
getOptionLabel(
|
||||||
|
EVAL_TYPE_OPTIONS,
|
||||||
|
detailData.evalTypeCode,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"所属行业",
|
"所属行业",
|
||||||
detailData.industryName ||
|
detailData.industryName ||
|
||||||
getOptionLabel(INDUSTRY_OPTIONS, detailData.industryCode),
|
getOptionLabel(
|
||||||
|
INDUSTRY_OPTIONS,
|
||||||
|
detailData.industryCode,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"签发日期",
|
"签发日期",
|
||||||
|
|
@ -904,7 +1001,9 @@ const EvalReportDatabase = (props) => {
|
||||||
[
|
[
|
||||||
"报送时间",
|
"报送时间",
|
||||||
detailData.archiveTime
|
detailData.archiveTime
|
||||||
? dayjs(detailData.archiveTime).format("YYYY-MM-DD HH:mm")
|
? dayjs(detailData.archiveTime).format(
|
||||||
|
"YYYY-MM-DD HH:mm",
|
||||||
|
)
|
||||||
: "-",
|
: "-",
|
||||||
],
|
],
|
||||||
]}
|
]}
|
||||||
|
|
@ -923,11 +1022,15 @@ const EvalReportDatabase = (props) => {
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
handleFileDownload(
|
handleFileDownload(
|
||||||
detailData.fileUrl,
|
detailData.fileUrl,
|
||||||
detailData.fileName || detailData.reportName || "正式安全评价报告.pdf"
|
detailData.fileName ||
|
||||||
|
detailData.reportName ||
|
||||||
|
"正式安全评价报告.pdf",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{detailData.fileName || detailData.reportName || "正式安全评价报告.pdf"}
|
{detailData.fileName ||
|
||||||
|
detailData.reportName ||
|
||||||
|
"正式安全评价报告.pdf"}
|
||||||
</span>,
|
</span>,
|
||||||
getFileExtLabel(detailData.fileName),
|
getFileExtLabel(detailData.fileName),
|
||||||
"V1.0",
|
"V1.0",
|
||||||
|
|
@ -935,17 +1038,16 @@ const EvalReportDatabase = (props) => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</ModalSection>
|
</ModalSection>
|
||||||
<ModalSection
|
<ModalSection title="合同文件" desc="查看上传的合同文件。">
|
||||||
title="合同文件"
|
|
||||||
desc="查看上传的合同文件。"
|
|
||||||
>
|
|
||||||
<ProtoTable
|
<ProtoTable
|
||||||
headers={["文件名称", "文件类型"]}
|
headers={["文件名称", "文件类型"]}
|
||||||
rows={parseFileList(detailData.contractFileUrls).map((f) => [
|
rows={parseFileList(detailData.contractFileUrls).map((f) => [
|
||||||
<span
|
<span
|
||||||
key={f.url}
|
key={f.url}
|
||||||
style={{ color: "#1677ff", cursor: "pointer" }}
|
style={{ color: "#1677ff", cursor: "pointer" }}
|
||||||
onClick={() => handleFileDownload(f.url, f.name || "未命名文件")}
|
onClick={() =>
|
||||||
|
handleFileDownload(f.url, f.name || "未命名文件")
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{f.name || "未命名文件"}
|
{f.name || "未命名文件"}
|
||||||
</span>,
|
</span>,
|
||||||
|
|
@ -953,17 +1055,16 @@ const EvalReportDatabase = (props) => {
|
||||||
])}
|
])}
|
||||||
/>
|
/>
|
||||||
</ModalSection>
|
</ModalSection>
|
||||||
<ModalSection
|
<ModalSection title="其他过程文档" desc="查看上传的过程文档。">
|
||||||
title="其他过程文档"
|
|
||||||
desc="查看上传的过程文档。"
|
|
||||||
>
|
|
||||||
<ProtoTable
|
<ProtoTable
|
||||||
headers={["文件名称", "文件类型"]}
|
headers={["文件名称", "文件类型"]}
|
||||||
rows={parseFileList(detailData.processDocUrls).map((f) => [
|
rows={parseFileList(detailData.processDocUrls).map((f) => [
|
||||||
<span
|
<span
|
||||||
key={f.url}
|
key={f.url}
|
||||||
style={{ color: "#1677ff", cursor: "pointer" }}
|
style={{ color: "#1677ff", cursor: "pointer" }}
|
||||||
onClick={() => handleFileDownload(f.url, f.name || "未命名文件")}
|
onClick={() =>
|
||||||
|
handleFileDownload(f.url, f.name || "未命名文件")
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{f.name || "未命名文件"}
|
{f.name || "未命名文件"}
|
||||||
</span>,
|
</span>,
|
||||||
|
|
@ -1009,7 +1110,10 @@ const EvalReportDatabase = (props) => {
|
||||||
{aiData && (
|
{aiData && (
|
||||||
<>
|
<>
|
||||||
<ModalSection title="分析概览" desc={aiData.analysis.overview}>
|
<ModalSection title="分析概览" desc={aiData.analysis.overview}>
|
||||||
<div className="redb-summary-strip" style={{ marginTop: "0.8rem", marginBottom: 0 }}>
|
<div
|
||||||
|
className="redb-summary-strip"
|
||||||
|
style={{ marginTop: "0.8rem", marginBottom: 0 }}
|
||||||
|
>
|
||||||
{aiData.analysis.metrics.map((m) => (
|
{aiData.analysis.metrics.map((m) => (
|
||||||
<span key={m.label}>
|
<span key={m.label}>
|
||||||
{m.label}:<strong>{m.value}</strong>
|
{m.label}:<strong>{m.value}</strong>
|
||||||
|
|
@ -1033,7 +1137,9 @@ const EvalReportDatabase = (props) => {
|
||||||
<td>{row.dim}</td>
|
<td>{row.dim}</td>
|
||||||
<td>{row.result}</td>
|
<td>{row.result}</td>
|
||||||
<td>
|
<td>
|
||||||
<span className={`redb-tag ${levelClass[row.level] || ""}`}>
|
<span
|
||||||
|
className={`redb-tag ${levelClass[row.level] || ""}`}
|
||||||
|
>
|
||||||
{row.level}
|
{row.level}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -1067,7 +1173,13 @@ const EvalReportDatabase = (props) => {
|
||||||
title="导出设置"
|
title="导出设置"
|
||||||
desc="导出当前查询范围内的数据,系统将记录导出人和导出时间。"
|
desc="导出当前查询范围内的数据,系统将记录导出人和导出时间。"
|
||||||
>
|
>
|
||||||
<Form form={exportForm} layout="vertical" preserve={false} className="redb-form-grid" scrollToFirstError>
|
<Form
|
||||||
|
form={exportForm}
|
||||||
|
layout="vertical"
|
||||||
|
preserve={false}
|
||||||
|
className="redb-form-grid"
|
||||||
|
scrollToFirstError
|
||||||
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="scope"
|
name="scope"
|
||||||
label="导出范围"
|
label="导出范围"
|
||||||
|
|
@ -1082,7 +1194,12 @@ const EvalReportDatabase = (props) => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="fileFormat" label="文件格式" initialValue="CSV" className="redb-field">
|
<Form.Item
|
||||||
|
name="fileFormat"
|
||||||
|
label="文件格式"
|
||||||
|
initialValue="CSV"
|
||||||
|
className="redb-field"
|
||||||
|
>
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{ label: "CSV", value: "CSV" },
|
{ label: "CSV", value: "CSV" },
|
||||||
|
|
@ -1091,7 +1208,11 @@ const EvalReportDatabase = (props) => {
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="exportRemark" label="导出说明" className="redb-field redb-field-full">
|
<Form.Item
|
||||||
|
name="exportRemark"
|
||||||
|
label="导出说明"
|
||||||
|
className="redb-field redb-field-full"
|
||||||
|
>
|
||||||
<TextArea rows={3} placeholder="选填" />
|
<TextArea rows={3} placeholder="选填" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
@ -1114,14 +1235,37 @@ const EvalReportDatabase = (props) => {
|
||||||
title="批量操作确认"
|
title="批量操作确认"
|
||||||
desc="批量分类将按年度、行业和评价类型重新校验目录;批量下载将生成所选报告的压缩文件。"
|
desc="批量分类将按年度、行业和评价类型重新校验目录;批量下载将生成所选报告的压缩文件。"
|
||||||
>
|
>
|
||||||
<Form form={batchForm} layout="vertical" preserve={false} className="redb-form-grid" scrollToFirstError>
|
<Form
|
||||||
<Form.Item name="scopeLabel" label="操作范围" className="redb-field">
|
form={batchForm}
|
||||||
|
layout="vertical"
|
||||||
|
preserve={false}
|
||||||
|
className="redb-form-grid"
|
||||||
|
scrollToFirstError
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="scopeLabel"
|
||||||
|
label="操作范围"
|
||||||
|
className="redb-field"
|
||||||
|
>
|
||||||
<Input disabled />
|
<Input disabled />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="keepRecord" label="保留原分类记录" className="redb-field">
|
<Form.Item
|
||||||
<Select options={[{ label: "是", value: "是" }, { label: "否", value: "否" }]} />
|
name="keepRecord"
|
||||||
|
label="保留原分类记录"
|
||||||
|
className="redb-field"
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
options={[
|
||||||
|
{ label: "是", value: "是" },
|
||||||
|
{ label: "否", value: "否" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="remark" label="操作说明" className="redb-field redb-field-full">
|
<Form.Item
|
||||||
|
name="remark"
|
||||||
|
label="操作说明"
|
||||||
|
className="redb-field redb-field-full"
|
||||||
|
>
|
||||||
<TextArea rows={3} placeholder="选填" />
|
<TextArea rows={3} placeholder="选填" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
|
|
@ -1182,7 +1326,12 @@ const EvalReportDatabase = (props) => {
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
className="redb-modal"
|
className="redb-modal"
|
||||||
>
|
>
|
||||||
<Form form={spotcheckForm} layout="vertical" preserve={false} scrollToFirstError>
|
<Form
|
||||||
|
form={spotcheckForm}
|
||||||
|
layout="vertical"
|
||||||
|
preserve={false}
|
||||||
|
scrollToFirstError
|
||||||
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="checkResultCode"
|
name="checkResultCode"
|
||||||
label="抽查结果"
|
label="抽查结果"
|
||||||
|
|
@ -1196,20 +1345,34 @@ const EvalReportDatabase = (props) => {
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item name="checkOpinion" label="抽查意见">
|
<Form.Item name="checkOpinion" label="抽查意见">
|
||||||
<TextArea rows={3} placeholder="填写抽查意见" maxLength={500} showCount />
|
<TextArea
|
||||||
|
rows={3}
|
||||||
|
placeholder="填写抽查意见"
|
||||||
|
maxLength={500}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
noStyle
|
noStyle
|
||||||
shouldUpdate={(prev, cur) => prev.checkResultCode !== cur.checkResultCode}
|
shouldUpdate={(prev, cur) =>
|
||||||
|
prev.checkResultCode !== cur.checkResultCode
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{({ getFieldValue }) =>
|
{({ getFieldValue }) =>
|
||||||
getFieldValue("checkResultCode") === 2 ? (
|
getFieldValue("checkResultCode") === 2 ? (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="rectifyRequire"
|
name="rectifyRequire"
|
||||||
label="整改要求"
|
label="整改要求"
|
||||||
rules={[{ required: true, message: "不合格须填写整改要求" }]}
|
rules={[
|
||||||
|
{ required: true, message: "不合格须填写整改要求" },
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<TextArea rows={3} placeholder="请填写整改要求" maxLength={500} showCount />
|
<TextArea
|
||||||
|
rows={3}
|
||||||
|
placeholder="请填写整改要求"
|
||||||
|
maxLength={500}
|
||||||
|
showCount
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|
@ -1234,7 +1397,12 @@ const EvalReportDatabase = (props) => {
|
||||||
<div>整改反馈:{reviewRecord.rectifyFeedback || "-"}</div>
|
<div>整改反馈:{reviewRecord.rectifyFeedback || "-"}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<Form form={reviewForm} layout="vertical" preserve={false} scrollToFirstError>
|
<Form
|
||||||
|
form={reviewForm}
|
||||||
|
layout="vertical"
|
||||||
|
preserve={false}
|
||||||
|
scrollToFirstError
|
||||||
|
>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="reviewResultCode"
|
name="reviewResultCode"
|
||||||
label="复核结果"
|
label="复核结果"
|
||||||
|
|
@ -1273,7 +1441,9 @@ const EvalReportDatabase = (props) => {
|
||||||
>
|
>
|
||||||
<div className="redb-modal-section">
|
<div className="redb-modal-section">
|
||||||
<h4>抽检设置</h4>
|
<h4>抽检设置</h4>
|
||||||
<p>从选定评价机构已报送的安全评价报告中随机抽取,生成监督检查单。</p>
|
<p>
|
||||||
|
从选定评价机构已报送的安全评价报告中随机抽取,生成监督检查单。
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Form form={spotCheckForm} layout="vertical" preserve={false}>
|
<Form form={spotCheckForm} layout="vertical" preserve={false}>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
|
|
@ -1307,7 +1477,10 @@ const EvalReportDatabase = (props) => {
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Form.Item name="planCheckDate" label="计划检查日期">
|
<Form.Item name="planCheckDate" label="计划检查日期">
|
||||||
<DatePicker style={{ width: "100%" }} placeholder="选择日期" />
|
<DatePicker
|
||||||
|
style={{ width: "100%" }}
|
||||||
|
placeholder="选择日期"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
|
|
@ -1327,7 +1500,10 @@ const EvalReportDatabase = (props) => {
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Form.Item name="checkBasis" label="补充检查依据">
|
<Form.Item name="checkBasis" label="补充检查依据">
|
||||||
<TextArea rows={2} placeholder="可填写本次专项检查、年度计划或属地监管文件依据" />
|
<TextArea
|
||||||
|
rows={2}
|
||||||
|
placeholder="可填写本次专项检查、年度计划或属地监管文件依据"
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
@ -1352,6 +1528,20 @@ const EvalReportDatabase = (props) => {
|
||||||
<h4 style={{ margin: 0 }}>随机抽取报告</h4>
|
<h4 style={{ margin: 0 }}>随机抽取报告</h4>
|
||||||
</div>
|
</div>
|
||||||
<Space>
|
<Space>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
loading={viewChecklistLoading}
|
||||||
|
onClick={handleViewChecklist}
|
||||||
|
>
|
||||||
|
查看检查单
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={handleDownloadChecklist}
|
||||||
|
>
|
||||||
|
下载检查单
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
loading={regulatorSpotCheckReportLoading}
|
loading={regulatorSpotCheckReportLoading}
|
||||||
|
|
@ -1362,16 +1552,29 @@ const EvalReportDatabase = (props) => {
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Table
|
||||||
rowKey="id"
|
rowKey="archiveTime"
|
||||||
size="small"
|
size="small"
|
||||||
columns={[
|
columns={[
|
||||||
{ title: "报告名称", dataIndex: "reportName", ellipsis: true },
|
{
|
||||||
{ title: "被检查机构", dataIndex: "orgName", width: 200, ellipsis: true },
|
title: "报告名称",
|
||||||
|
dataIndex: "reportName",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "被检查机构",
|
||||||
|
dataIndex: "orgName",
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 90,
|
width: 90,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Button type="primary" size="small" onClick={() => handleViewDetail(record)}>
|
<Button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleViewDetail(record)}
|
||||||
|
>
|
||||||
查看
|
查看
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
import PizZip from "pizzip";
|
||||||
|
import Docxtemplater from "docxtemplater";
|
||||||
|
|
||||||
|
/** 模板文件缓存,避免重复下载 */
|
||||||
|
const templateCache = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载 docx 模板文件内容(带缓存,相同 URL 只下载一次)
|
||||||
|
* @param {string} templateUrl - 模板文件 URL
|
||||||
|
* @returns {Promise<ArrayBuffer>}
|
||||||
|
*/
|
||||||
|
async function loadTemplate(templateUrl) {
|
||||||
|
if (templateCache.has(templateUrl)) {
|
||||||
|
return templateCache.get(templateUrl);
|
||||||
|
}
|
||||||
|
const response = await fetch(templateUrl);
|
||||||
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
templateCache.set(templateUrl, arrayBuffer);
|
||||||
|
return arrayBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载 docx 模板并填充数据,返回生成的 Blob
|
||||||
|
* @param {string} templateUrl - 模板文件 URL(如 /xxx.docx)
|
||||||
|
* @param {object} data - 占位符数据,如 { orgName: "xxx" }
|
||||||
|
* @returns {Promise<Blob>} 填充后的 docx Blob
|
||||||
|
*/
|
||||||
|
export async function renderDocxBlob(templateUrl, data) {
|
||||||
|
const arrayBuffer = await loadTemplate(templateUrl);
|
||||||
|
const zip = new PizZip(arrayBuffer);
|
||||||
|
const doc = new Docxtemplater(zip, {
|
||||||
|
paragraphLoop: true,
|
||||||
|
linebreaks: true,
|
||||||
|
});
|
||||||
|
doc.render(data);
|
||||||
|
return doc.getZip().generate({
|
||||||
|
type: "blob",
|
||||||
|
mimeType:
|
||||||
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载 docx 模板,填充数据后打开预览或触发下载
|
||||||
|
* @param {string} templateUrl - 模板文件 URL(如 /xxx.docx)
|
||||||
|
* @param {object} data - 占位符数据,如 { orgName: "xxx" }
|
||||||
|
* @param {string} fileName - 文件名称
|
||||||
|
* @param {object} [options] - 可选配置
|
||||||
|
* @param {boolean} [options.download=false] - 为 true 时触发下载,默认在新窗口预览
|
||||||
|
*/
|
||||||
|
export async function fillDocxTemplate(
|
||||||
|
templateUrl,
|
||||||
|
data,
|
||||||
|
fileName,
|
||||||
|
{ download = false } = {},
|
||||||
|
) {
|
||||||
|
const blob = await renderDocxBlob(templateUrl, data);
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
if (download) {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.download = fileName || "document.docx";
|
||||||
|
link.style.display = "none";
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认:新窗口打开预览
|
||||||
|
window.open(url, "_blank");
|
||||||
|
// 延迟释放,避免新窗口加载前 URL 失效
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 60000);
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,7 @@ export const orgAccountResetPassword = declareRequest(
|
||||||
export const registeredOrgList = declareRequest(
|
export const registeredOrgList = declareRequest(
|
||||||
"orgInfoLoading",
|
"orgInfoLoading",
|
||||||
"Get > /safetyEval/org-info/page",
|
"Get > /safetyEval/org-info/page",
|
||||||
'data: [] | res.data || [] & totalCount: 0 | res.totalCount || 0'
|
'registeredOrgListData: [] | res.data || [] & totalCount: 0 | res.totalCount || 0'
|
||||||
);
|
);
|
||||||
|
|
||||||
export const registeredOrgGet = declareRequest(
|
export const registeredOrgGet = declareRequest(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue