feat
parent
81209bb537
commit
c05a311bd6
|
|
@ -28,6 +28,8 @@
|
|||
"@eigenpal/docx-editor-react": "^1.9.0",
|
||||
"@rc-component/motion": "^1.0.0",
|
||||
"@rc-component/util": "^1.11.1",
|
||||
"pizzip": "latest",
|
||||
"docxtemplater": "latest",
|
||||
"antd": "^5.29.2",
|
||||
"china-division": "^2.7.0",
|
||||
"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>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item name="businessAddress" label="企事业单位经营地址">
|
||||
<Form.Item name="businessAddress" label="经营地址">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
|
|
|||
|
|
@ -13,12 +13,8 @@ import {
|
|||
DatePicker,
|
||||
Space,
|
||||
ConfigProvider,
|
||||
Card,
|
||||
} from "antd";
|
||||
import {
|
||||
DownloadOutlined,
|
||||
ExportOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { DownloadOutlined, ExportOutlined } from "@ant-design/icons";
|
||||
import TableAction from "@cqsjjb/jjb-react-admin-component/TableAction";
|
||||
import PageLayout from "@cqsjjb/jjb-react-admin-component/PageLayout";
|
||||
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 { Connect } from "@cqsjjb/jjb-dva-runtime";
|
||||
import { tools } from "@cqsjjb/jjb-common-lib";
|
||||
import { renderDocxBlob, fillDocxTemplate } from "~/utils/fillDocxTemplate";
|
||||
import dayjs from "dayjs";
|
||||
import "./index.less";
|
||||
|
||||
|
|
@ -36,6 +33,10 @@ const { RangePicker } = DatePicker;
|
|||
|
||||
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 */
|
||||
const PROTO_THEME = {
|
||||
token: { borderRadius: 8, borderRadiusLG: 12, borderRadiusSM: 6 },
|
||||
|
|
@ -264,6 +265,7 @@ const EvalReportDatabase = (props) => {
|
|||
const [spotCheckData, setSpotCheckData] = useState([]);
|
||||
const [orgOptions, setOrgOptions] = useState([]);
|
||||
const [spotCheckForm] = Form.useForm();
|
||||
const [viewChecklistLoading, setViewChecklistLoading] = useState(false);
|
||||
|
||||
const {
|
||||
regulatorEvalReportPageLoading,
|
||||
|
|
@ -274,6 +276,9 @@ const EvalReportDatabase = (props) => {
|
|||
regulatorSpotCheckReportLoading,
|
||||
} = props.regulatorEvalReport || {};
|
||||
|
||||
const { registeredOrgListData } = props.orgInfo || {};
|
||||
|
||||
|
||||
const fetchSummary = async () => {
|
||||
const res = await props.regulatorEvalReportSummary();
|
||||
if (res?.success !== false) {
|
||||
|
|
@ -289,8 +294,12 @@ const EvalReportDatabase = (props) => {
|
|||
};
|
||||
// RangePicker 拆成接口字段
|
||||
if (params.archiveTimeRange?.length === 2) {
|
||||
params.archiveTimeStart = dayjs(params.archiveTimeRange[0]).format("YYYY-MM-DD");
|
||||
params.archiveTimeEnd = dayjs(params.archiveTimeRange[1]).format("YYYY-MM-DD");
|
||||
params.archiveTimeStart = dayjs(params.archiveTimeRange[0]).format(
|
||||
"YYYY-MM-DD",
|
||||
);
|
||||
params.archiveTimeEnd = dayjs(params.archiveTimeRange[1]).format(
|
||||
"YYYY-MM-DD",
|
||||
);
|
||||
}
|
||||
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 next = { ...values, current: 1, size: router.query.size || 20 };
|
||||
if (next.archiveTimeRange?.length === 2) {
|
||||
next.archiveTimeStart = dayjs(next.archiveTimeRange[0]).format("YYYY-MM-DD");
|
||||
next.archiveTimeEnd = dayjs(next.archiveTimeRange[1]).format("YYYY-MM-DD");
|
||||
next.archiveTimeStart = dayjs(next.archiveTimeRange[0]).format(
|
||||
"YYYY-MM-DD",
|
||||
);
|
||||
next.archiveTimeEnd = dayjs(next.archiveTimeRange[1]).format(
|
||||
"YYYY-MM-DD",
|
||||
);
|
||||
} else {
|
||||
next.archiveTimeStart = 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) => {
|
||||
setCurrentReport(record);
|
||||
|
|
@ -592,7 +677,6 @@ const EvalReportDatabase = (props) => {
|
|||
title: "报告编号",
|
||||
dataIndex: "reportNo",
|
||||
width: 150,
|
||||
|
||||
},
|
||||
{
|
||||
title: "报告名称",
|
||||
|
|
@ -640,12 +724,8 @@ const EvalReportDatabase = (props) => {
|
|||
fixed: "right",
|
||||
render: (_, record) => (
|
||||
<TableAction size={4} wrap>
|
||||
<a onClick={() => handleViewDetail(record)}>
|
||||
查看报告
|
||||
</a>
|
||||
<a onClick={() => handleOpenAi(record)}>
|
||||
AI分析
|
||||
</a>
|
||||
<a onClick={() => handleViewDetail(record)}>查看报告</a>
|
||||
<a onClick={() => handleOpenAi(record)}>AI分析</a>
|
||||
</TableAction>
|
||||
),
|
||||
},
|
||||
|
|
@ -669,8 +749,10 @@ const EvalReportDatabase = (props) => {
|
|||
dataIndex: "checkResultName",
|
||||
width: 90,
|
||||
render: (val, record) => {
|
||||
if (record.checkResultCode === 1) return <span className="redb-tag redb-tag-success">合格</span>;
|
||||
if (record.checkResultCode === 2) return <span className="redb-tag redb-tag-danger">不合格</span>;
|
||||
if (record.checkResultCode === 1)
|
||||
return <span className="redb-tag redb-tag-success">合格</span>;
|
||||
if (record.checkResultCode === 2)
|
||||
return <span className="redb-tag redb-tag-danger">不合格</span>;
|
||||
return val || "-";
|
||||
},
|
||||
},
|
||||
|
|
@ -697,8 +779,10 @@ const EvalReportDatabase = (props) => {
|
|||
dataIndex: "reviewResultName",
|
||||
width: 100,
|
||||
render: (val, record) => {
|
||||
if (record.reviewResultCode === 1) return <span className="redb-tag redb-tag-success">通过</span>;
|
||||
if (record.reviewResultCode === 2) return <span className="redb-tag redb-tag-danger">不通过</span>;
|
||||
if (record.reviewResultCode === 1)
|
||||
return <span className="redb-tag redb-tag-success">通过</span>;
|
||||
if (record.reviewResultCode === 2)
|
||||
return <span className="redb-tag redb-tag-danger">不通过</span>;
|
||||
return val || "-";
|
||||
},
|
||||
},
|
||||
|
|
@ -709,11 +793,7 @@ const EvalReportDatabase = (props) => {
|
|||
render: (_, record) => {
|
||||
// 有整改反馈且尚未复核时可复核
|
||||
if (record.rectifyFeedback && !record.reviewResultCode) {
|
||||
return (
|
||||
<a onClick={() => handleOpenReview(record)}>
|
||||
复核
|
||||
</a>
|
||||
);
|
||||
return <a onClick={() => handleOpenReview(record)}>复核</a>;
|
||||
}
|
||||
return "-";
|
||||
},
|
||||
|
|
@ -796,7 +876,10 @@ const EvalReportDatabase = (props) => {
|
|||
/>
|
||||
</Form.Item>,
|
||||
<Form.Item key="archiveTimeRange" name="archiveTimeRange">
|
||||
<RangePicker style={{ width: "100%" }} placeholder={["报送时间起", "报送时间止"]} />
|
||||
<RangePicker
|
||||
style={{ width: "100%" }}
|
||||
placeholder={["报送时间起", "报送时间止"]}
|
||||
/>
|
||||
</Form.Item>,
|
||||
]}
|
||||
onFinish={handleSearch}
|
||||
|
|
@ -811,10 +894,18 @@ const EvalReportDatabase = (props) => {
|
|||
<Button type="primary" size="small" onClick={handleOpenSpotCheck}>
|
||||
抽检
|
||||
</Button>
|
||||
<Button size="small" icon={<DownloadOutlined />} onClick={handleBatchDownload}>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<DownloadOutlined />}
|
||||
onClick={handleBatchDownload}
|
||||
>
|
||||
批量下载
|
||||
</Button>
|
||||
<Button size="small" icon={<ExportOutlined />} onClick={handleOpenExport}>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<ExportOutlined />}
|
||||
onClick={handleOpenExport}
|
||||
>
|
||||
导出目录
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -888,12 +979,18 @@ const EvalReportDatabase = (props) => {
|
|||
[
|
||||
"评价类型",
|
||||
detailData.evalTypeName ||
|
||||
getOptionLabel(EVAL_TYPE_OPTIONS, detailData.evalTypeCode),
|
||||
getOptionLabel(
|
||||
EVAL_TYPE_OPTIONS,
|
||||
detailData.evalTypeCode,
|
||||
),
|
||||
],
|
||||
[
|
||||
"所属行业",
|
||||
detailData.industryName ||
|
||||
getOptionLabel(INDUSTRY_OPTIONS, detailData.industryCode),
|
||||
getOptionLabel(
|
||||
INDUSTRY_OPTIONS,
|
||||
detailData.industryCode,
|
||||
),
|
||||
],
|
||||
[
|
||||
"签发日期",
|
||||
|
|
@ -904,7 +1001,9 @@ const EvalReportDatabase = (props) => {
|
|||
[
|
||||
"报送时间",
|
||||
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={() =>
|
||||
handleFileDownload(
|
||||
detailData.fileUrl,
|
||||
detailData.fileName || detailData.reportName || "正式安全评价报告.pdf"
|
||||
detailData.fileName ||
|
||||
detailData.reportName ||
|
||||
"正式安全评价报告.pdf",
|
||||
)
|
||||
}
|
||||
>
|
||||
{detailData.fileName || detailData.reportName || "正式安全评价报告.pdf"}
|
||||
{detailData.fileName ||
|
||||
detailData.reportName ||
|
||||
"正式安全评价报告.pdf"}
|
||||
</span>,
|
||||
getFileExtLabel(detailData.fileName),
|
||||
"V1.0",
|
||||
|
|
@ -935,17 +1038,16 @@ const EvalReportDatabase = (props) => {
|
|||
]}
|
||||
/>
|
||||
</ModalSection>
|
||||
<ModalSection
|
||||
title="合同文件"
|
||||
desc="查看上传的合同文件。"
|
||||
>
|
||||
<ModalSection title="合同文件" desc="查看上传的合同文件。">
|
||||
<ProtoTable
|
||||
headers={["文件名称", "文件类型"]}
|
||||
rows={parseFileList(detailData.contractFileUrls).map((f) => [
|
||||
<span
|
||||
key={f.url}
|
||||
style={{ color: "#1677ff", cursor: "pointer" }}
|
||||
onClick={() => handleFileDownload(f.url, f.name || "未命名文件")}
|
||||
onClick={() =>
|
||||
handleFileDownload(f.url, f.name || "未命名文件")
|
||||
}
|
||||
>
|
||||
{f.name || "未命名文件"}
|
||||
</span>,
|
||||
|
|
@ -953,17 +1055,16 @@ const EvalReportDatabase = (props) => {
|
|||
])}
|
||||
/>
|
||||
</ModalSection>
|
||||
<ModalSection
|
||||
title="其他过程文档"
|
||||
desc="查看上传的过程文档。"
|
||||
>
|
||||
<ModalSection title="其他过程文档" desc="查看上传的过程文档。">
|
||||
<ProtoTable
|
||||
headers={["文件名称", "文件类型"]}
|
||||
rows={parseFileList(detailData.processDocUrls).map((f) => [
|
||||
<span
|
||||
key={f.url}
|
||||
style={{ color: "#1677ff", cursor: "pointer" }}
|
||||
onClick={() => handleFileDownload(f.url, f.name || "未命名文件")}
|
||||
onClick={() =>
|
||||
handleFileDownload(f.url, f.name || "未命名文件")
|
||||
}
|
||||
>
|
||||
{f.name || "未命名文件"}
|
||||
</span>,
|
||||
|
|
@ -1009,7 +1110,10 @@ const EvalReportDatabase = (props) => {
|
|||
{aiData && (
|
||||
<>
|
||||
<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) => (
|
||||
<span key={m.label}>
|
||||
{m.label}:<strong>{m.value}</strong>
|
||||
|
|
@ -1033,7 +1137,9 @@ const EvalReportDatabase = (props) => {
|
|||
<td>{row.dim}</td>
|
||||
<td>{row.result}</td>
|
||||
<td>
|
||||
<span className={`redb-tag ${levelClass[row.level] || ""}`}>
|
||||
<span
|
||||
className={`redb-tag ${levelClass[row.level] || ""}`}
|
||||
>
|
||||
{row.level}
|
||||
</span>
|
||||
</td>
|
||||
|
|
@ -1067,7 +1173,13 @@ const EvalReportDatabase = (props) => {
|
|||
title="导出设置"
|
||||
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
|
||||
name="scope"
|
||||
label="导出范围"
|
||||
|
|
@ -1082,7 +1194,12 @@ const EvalReportDatabase = (props) => {
|
|||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="fileFormat" label="文件格式" initialValue="CSV" className="redb-field">
|
||||
<Form.Item
|
||||
name="fileFormat"
|
||||
label="文件格式"
|
||||
initialValue="CSV"
|
||||
className="redb-field"
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "CSV", value: "CSV" },
|
||||
|
|
@ -1091,7 +1208,11 @@ const EvalReportDatabase = (props) => {
|
|||
]}
|
||||
/>
|
||||
</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="选填" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
|
@ -1114,14 +1235,37 @@ const EvalReportDatabase = (props) => {
|
|||
title="批量操作确认"
|
||||
desc="批量分类将按年度、行业和评价类型重新校验目录;批量下载将生成所选报告的压缩文件。"
|
||||
>
|
||||
<Form form={batchForm} layout="vertical" preserve={false} className="redb-form-grid" scrollToFirstError>
|
||||
<Form.Item name="scopeLabel" label="操作范围" className="redb-field">
|
||||
<Form
|
||||
form={batchForm}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
className="redb-form-grid"
|
||||
scrollToFirstError
|
||||
>
|
||||
<Form.Item
|
||||
name="scopeLabel"
|
||||
label="操作范围"
|
||||
className="redb-field"
|
||||
>
|
||||
<Input disabled />
|
||||
</Form.Item>
|
||||
<Form.Item name="keepRecord" label="保留原分类记录" className="redb-field">
|
||||
<Select options={[{ label: "是", value: "是" }, { label: "否", value: "否" }]} />
|
||||
<Form.Item
|
||||
name="keepRecord"
|
||||
label="保留原分类记录"
|
||||
className="redb-field"
|
||||
>
|
||||
<Select
|
||||
options={[
|
||||
{ label: "是", value: "是" },
|
||||
{ label: "否", value: "否" },
|
||||
]}
|
||||
/>
|
||||
</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="选填" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
|
@ -1182,7 +1326,12 @@ const EvalReportDatabase = (props) => {
|
|||
destroyOnClose
|
||||
className="redb-modal"
|
||||
>
|
||||
<Form form={spotcheckForm} layout="vertical" preserve={false} scrollToFirstError>
|
||||
<Form
|
||||
form={spotcheckForm}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
scrollToFirstError
|
||||
>
|
||||
<Form.Item
|
||||
name="checkResultCode"
|
||||
label="抽查结果"
|
||||
|
|
@ -1196,20 +1345,34 @@ const EvalReportDatabase = (props) => {
|
|||
/>
|
||||
</Form.Item>
|
||||
<Form.Item name="checkOpinion" label="抽查意见">
|
||||
<TextArea rows={3} placeholder="填写抽查意见" maxLength={500} showCount />
|
||||
<TextArea
|
||||
rows={3}
|
||||
placeholder="填写抽查意见"
|
||||
maxLength={500}
|
||||
showCount
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
noStyle
|
||||
shouldUpdate={(prev, cur) => prev.checkResultCode !== cur.checkResultCode}
|
||||
shouldUpdate={(prev, cur) =>
|
||||
prev.checkResultCode !== cur.checkResultCode
|
||||
}
|
||||
>
|
||||
{({ getFieldValue }) =>
|
||||
getFieldValue("checkResultCode") === 2 ? (
|
||||
<Form.Item
|
||||
name="rectifyRequire"
|
||||
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>
|
||||
) : null
|
||||
}
|
||||
|
|
@ -1234,7 +1397,12 @@ const EvalReportDatabase = (props) => {
|
|||
<div>整改反馈:{reviewRecord.rectifyFeedback || "-"}</div>
|
||||
</div>
|
||||
)}
|
||||
<Form form={reviewForm} layout="vertical" preserve={false} scrollToFirstError>
|
||||
<Form
|
||||
form={reviewForm}
|
||||
layout="vertical"
|
||||
preserve={false}
|
||||
scrollToFirstError
|
||||
>
|
||||
<Form.Item
|
||||
name="reviewResultCode"
|
||||
label="复核结果"
|
||||
|
|
@ -1273,7 +1441,9 @@ const EvalReportDatabase = (props) => {
|
|||
>
|
||||
<div className="redb-modal-section">
|
||||
<h4>抽检设置</h4>
|
||||
<p>从选定评价机构已报送的安全评价报告中随机抽取,生成监督检查单。</p>
|
||||
<p>
|
||||
从选定评价机构已报送的安全评价报告中随机抽取,生成监督检查单。
|
||||
</p>
|
||||
</div>
|
||||
<Form form={spotCheckForm} layout="vertical" preserve={false}>
|
||||
<Row gutter={16}>
|
||||
|
|
@ -1307,7 +1477,10 @@ const EvalReportDatabase = (props) => {
|
|||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="planCheckDate" label="计划检查日期">
|
||||
<DatePicker style={{ width: "100%" }} placeholder="选择日期" />
|
||||
<DatePicker
|
||||
style={{ width: "100%" }}
|
||||
placeholder="选择日期"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
|
@ -1327,7 +1500,10 @@ const EvalReportDatabase = (props) => {
|
|||
</Col>
|
||||
<Col span={24}>
|
||||
<Form.Item name="checkBasis" label="补充检查依据">
|
||||
<TextArea rows={2} placeholder="可填写本次专项检查、年度计划或属地监管文件依据" />
|
||||
<TextArea
|
||||
rows={2}
|
||||
placeholder="可填写本次专项检查、年度计划或属地监管文件依据"
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
|
@ -1352,6 +1528,20 @@ const EvalReportDatabase = (props) => {
|
|||
<h4 style={{ margin: 0 }}>随机抽取报告</h4>
|
||||
</div>
|
||||
<Space>
|
||||
<Button
|
||||
size="small"
|
||||
type="primary"
|
||||
loading={viewChecklistLoading}
|
||||
onClick={handleViewChecklist}
|
||||
>
|
||||
查看检查单
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
onClick={handleDownloadChecklist}
|
||||
>
|
||||
下载检查单
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
loading={regulatorSpotCheckReportLoading}
|
||||
|
|
@ -1362,16 +1552,29 @@ const EvalReportDatabase = (props) => {
|
|||
</Space>
|
||||
</div>
|
||||
<Table
|
||||
rowKey="id"
|
||||
rowKey="archiveTime"
|
||||
size="small"
|
||||
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: "操作",
|
||||
width: 90,
|
||||
render: (_, record) => (
|
||||
<Button type="primary" size="small" onClick={() => handleViewDetail(record)}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
onClick={() => handleViewDetail(record)}
|
||||
>
|
||||
查看
|
||||
</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(
|
||||
"orgInfoLoading",
|
||||
"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(
|
||||
|
|
|
|||
Loading…
Reference in New Issue