企业预案管理

master
LiuJiaNan 2026-04-10 11:47:04 +08:00
parent 58da2bbb60
commit 3af4efd132
21 changed files with 573 additions and 199 deletions

View File

@ -66,3 +66,5 @@
`/emergencyRescue/container/enterprise/planAndDrill/enterprisePlan/list` `/emergencyRescue/container/enterprise/planAndDrill/enterprisePlan/list`
- 预案与演练/应急预案管理 - 预案与演练/应急预案管理
`/emergencyRescue/container/enterprise/planAndDrill/emergencyPlan/list` `/emergencyRescue/container/enterprise/planAndDrill/emergencyPlan/list`
- 预案与演练/应急演练管理
`/emergencyRescue/container/enterprise/planAndDrill/emergencyExercise/list`

View File

@ -0,0 +1,26 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const emergencyExerciseList = declareRequest(
"emergencyExerciseLoading",
"Post > @/emergencyRescue/emergencyDrill/list",
);
export const emergencyExerciseInfo = declareRequest(
"emergencyExerciseLoading",
"Get > /emergencyRescue/emergencyDrill/{id}",
);
export const emergencyExerciseAdd = declareRequest(
"emergencyExerciseLoading",
"Post > @/emergencyRescue/emergencyDrill/save",
);
export const emergencyExerciseUpdate = declareRequest(
"emergencyExerciseLoading",
"Put > @/emergencyRescue/emergencyDrill/edit",
);
export const emergencyExerciseDelete = declareRequest(
"emergencyExerciseLoading",
"Delete > @/emergencyRescue/emergencyDrill/{id}",
);
export const emergencyExerciseDeleteBatch = declareRequest(
"emergencyExerciseLoading",
"Delete > @/emergencyRescue/emergencyDrill/ids?ids={ids}",
);

View File

@ -25,3 +25,4 @@ export const NS_ENTERPRISE_EMERGENCY_PERSONNEL = defineNamespace("enterpriseEmer
export const NS_EMERGENCY_RESCUE_TEAM = defineNamespace("emergencyRescueTeam"); export const NS_EMERGENCY_RESCUE_TEAM = defineNamespace("emergencyRescueTeam");
export const NS_ENTERPRISE_PLAN = defineNamespace("enterprisePlan"); export const NS_ENTERPRISE_PLAN = defineNamespace("enterprisePlan");
export const NS_EMERGENCY_PLAN = defineNamespace("emergencyPlan"); export const NS_EMERGENCY_PLAN = defineNamespace("emergencyPlan");
export const NS_EMERGENCY_EXERCISE = defineNamespace("emergencyExercise");

View File

@ -0,0 +1,143 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, message, Modal, Space } from "antd";
import { useState } from "react";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import DeleteIcon from "zy-react-library/components/Icon/DeleteIcon";
import Page from "zy-react-library/components/Page";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import { FORM_ITEM_RENDER_ENUM } from "zy-react-library/enum/formItemRender";
import useTable from "zy-react-library/hooks/useTable";
import { NS_EMERGENCY_EXERCISE } from "~/enumerate/namespace";
function List(props) {
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [form] = Search.useForm();
const { tableProps, getData } = useTable(props["emergencyExerciseList"], {
form,
transform: formData => ({
drillStartTime: formData.drillTime?.[0],
drillEndTime: formData.drillTime?.[1],
}),
});
const onDelete = (record) => {
Modal.confirm({
title: "删除确认",
content: `确定要删除【${record.planName}】吗`,
onOk: async () => {
const { success } = await props["emergencyExerciseDelete"]({ id: record.id });
if (success) {
message.success("删除成功");
getData();
}
},
});
};
const onDeleteBatch = () => {
if (selectedRowKeys.length === 0) {
message.warning("请选择要删除的记录");
return;
}
Modal.confirm({
title: "删除确认",
content: "确认要删除吗?",
onOk: async () => {
const { success } = await props["emergencyExerciseDeleteBatch"]({ ids: selectedRowKeys });
if (success) {
message.success("删除成功");
getData();
}
},
});
};
return (
<Page isShowAllAction={false}>
<Search
options={[
{ name: "likeDrillName", label: "应急演练方案名称" },
{ name: "drillTime", label: "演练时间", render: FORM_ITEM_RENDER_ENUM.DATE_RANGE },
]}
form={form}
onFinish={getData}
/>
<Table
rowSelection={{
preserveSelectedRowKeys: true,
selectedRowKeys,
onChange: (selectedRowKeys) => {
setSelectedRowKeys(selectedRowKeys);
},
}}
toolBarRender={() => (
<Space>
<Button
type="primary"
icon={(<AddIcon />)}
onClick={() => {
props.history.push("./add");
}}
>
新增
</Button>
<Button
type="primary"
danger
ghost
icon={(<DeleteIcon />)}
onClick={onDeleteBatch}
>
批量删除
</Button>
</Space>
)}
columns={[
{ title: "应急演练方案名称", dataIndex: "drillName" },
{ title: "演练单位", dataIndex: "drillUnit" },
{ title: "演练时间", dataIndex: "drillTime" },
{
title: "操作",
width: 150,
fixed: "right",
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => {
props.history.push(`./view?id=${record.id}`);
}}
>
查看
</Button>
<Button
type="link"
onClick={() => {
props.history.push(`./add?id=${record.id}`);
}}
>
编辑
</Button>
<Button
type="link"
danger
onClick={() => {
onDelete(record);
}}
>
删除
</Button>
</Space>
),
},
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_EMERGENCY_EXERCISE], true)(List);

View File

@ -0,0 +1,5 @@
function EmergencyExercise(props) {
return props.children;
}
export default EmergencyExercise;

View File

@ -18,7 +18,7 @@ function GradeResponse(props) {
}, },
useStorageQueryCriteria: false, useStorageQueryCriteria: false,
onSuccess: () => { onSuccess: () => {
props.setResponseLevelRefresh(true); props.mode !== "view" && props.setResponseLevelRefresh(true);
}, },
}); });
@ -40,15 +40,19 @@ function GradeResponse(props) {
<div> <div>
<Table <Table
toolBarRender={() => ( toolBarRender={() => (
<Button <Space>
type="primary" {props.mode !== "view" && (
icon={(<AddIcon />)} <Button
onClick={() => { type="primary"
setAddModalVisible(true); icon={(<AddIcon />)}
}} onClick={() => {
> setAddModalVisible(true);
新增 }}
</Button> >
新增
</Button>
)}
</Space>
)} )}
columns={[ columns={[
{ title: "分级名称", dataIndex: "levelName" }, { title: "分级名称", dataIndex: "levelName" },
@ -58,6 +62,7 @@ function GradeResponse(props) {
title: "操作", title: "操作",
width: 150, width: 150,
fixed: "right", fixed: "right",
hidden: props.mode === "view",
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
<Button <Button

View File

@ -42,7 +42,7 @@ function OrganizationStructure(props) {
const getTreeList = async () => { const getTreeList = async () => {
const { data } = await props["enterprisePlanLevelOrganizationListTree"]({ planId: props.planId }); const { data } = await props["enterprisePlanLevelOrganizationListTree"]({ planId: props.planId });
setTreeList(data); setTreeList(data);
props.setExecutingAgencyRefresh(true); props.mode !== "view" && props.setExecutingAgencyRefresh(true);
}; };
useEffect(() => { useEffect(() => {
@ -82,54 +82,56 @@ function OrganizationStructure(props) {
<div> <div>
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<div style={{ width: 500 }}> <div style={{ width: 500 }}>
<Space style={{ marginBottom: 16 }}> {props.mode !== "view" && (
<Button <Space style={{ marginBottom: 16 }}>
type="primary" <Button
icon={(<AddIcon />)} type="primary"
onClick={() => { icon={(<AddIcon />)}
setAddOrganizationModalVisible(true); onClick={() => {
setAddOrganizationModalType("add"); setAddOrganizationModalVisible(true);
}} setAddOrganizationModalType("add");
> }}
新增一级 >
</Button> 新增一级
<Button </Button>
type="primary" <Button
icon={(<AddIcon />)} type="primary"
disabled={Object.keys(currentOrganization).length === 0} icon={(<AddIcon />)}
onClick={() => { disabled={Object.keys(currentOrganization).length === 0}
setAddOrganizationModalVisible(true); onClick={() => {
setAddOrganizationModalType("addChild"); setAddOrganizationModalVisible(true);
setParentId(currentOrganization.id); setAddOrganizationModalType("addChild");
setParentName(currentOrganization.orgName); setParentId(currentOrganization.id);
}} setParentName(currentOrganization.orgName);
> }}
新增下级 >
</Button> 新增下级
<Button </Button>
type="primary" <Button
icon={(<EditIcon />)} type="primary"
disabled={Object.keys(currentOrganization).length === 0} icon={(<EditIcon />)}
onClick={() => { disabled={Object.keys(currentOrganization).length === 0}
setAddOrganizationModalVisible(true); onClick={() => {
setAddOrganizationModalType("update"); setAddOrganizationModalVisible(true);
setParentId(currentOrganization.parentId); setAddOrganizationModalType("update");
setParentName(currentOrganization.parentName); setParentId(currentOrganization.parentId);
setCurrentOrganizationId(currentOrganization.id); setParentName(currentOrganization.parentName);
}} setCurrentOrganizationId(currentOrganization.id);
> }}
编辑 >
</Button> 编辑
<Button </Button>
type="primary" <Button
danger type="primary"
icon={(<DeleteIcon />)} danger
disabled={Object.keys(currentOrganization).length === 0 || currentOrganization.children || tableProps.dataSource.length > 0} icon={(<DeleteIcon />)}
onClick={onDeleteOrganization} disabled={Object.keys(currentOrganization).length === 0 || currentOrganization.children || tableProps.dataSource.length > 0}
> onClick={onDeleteOrganization}
删除 >
</Button> 删除
</Space> </Button>
</Space>
)}
<BasicLeftTree <BasicLeftTree
treeData={treeList} treeData={treeList}
nameKey="orgName" nameKey="orgName"
@ -143,16 +145,20 @@ function OrganizationStructure(props) {
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<Table <Table
toolBarRender={() => ( toolBarRender={() => (
<Button <Space>
type="primary" {props.mode !== "view" && (
icon={(<AddIcon />)} <Button
disabled={Object.keys(currentOrganization).length === 0} type="primary"
onClick={() => { icon={(<AddIcon />)}
setAddOrganizationPersonnelModalVisible(true); disabled={Object.keys(currentOrganization).length === 0}
}} onClick={() => {
> setAddOrganizationPersonnelModalVisible(true);
新增人员 }}
</Button> >
新增人员
</Button>
)}
</Space>
)} )}
options={false} options={false}
columns={[ columns={[
@ -165,6 +171,7 @@ function OrganizationStructure(props) {
title: "操作", title: "操作",
width: 150, width: 150,
fixed: "right", fixed: "right",
hidden: props.mode === "view",
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
<Button <Button

View File

@ -47,15 +47,19 @@ function PlanAttachments(props) {
<div> <div>
<Table <Table
toolBarRender={() => ( toolBarRender={() => (
<Button <Space>
type="primary" {props.mode !== "view" && (
icon={(<AddIcon />)} <Button
onClick={() => { type="primary"
setAddModalVisible(true); icon={(<AddIcon />)}
}} onClick={() => {
> setAddModalVisible(true);
新增 }}
</Button> >
新增
</Button>
)}
</Space>
)} )}
options={false} options={false}
columns={[ columns={[
@ -82,24 +86,28 @@ function PlanAttachments(props) {
> >
下载 下载
</Button> </Button>
<Button {props.mode !== "view" && (
type="link" <Button
onClick={() => { type="link"
setCurrentId(record.id); onClick={() => {
setAddModalVisible(true); setCurrentId(record.id);
}} setAddModalVisible(true);
> }}
编辑 >
</Button> 编辑
<Button </Button>
type="link" )}
danger {props.mode !== "view" && (
onClick={() => { <Button
onDelete(record.id); type="link"
}} danger
> onClick={() => {
删除 onDelete(record.id);
</Button> }}
>
删除
</Button>
)}
</Space> </Space>
), ),
}, },

View File

@ -85,75 +85,79 @@ function PlanInstructions(props) {
return ( return (
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<div style={{ width: "50%" }}> {
<FormBuilder props.mode !== "view" && (
showActionButtons={false} <div style={{ width: "50%" }}>
span={24} <FormBuilder
labelCol={{ span: 6 }} showActionButtons={false}
loading={props.enterprisePlan.enterprisePlanCommandSettingLoading} span={24}
options={[ labelCol={{ span: 6 }}
{ key: "divider", label: "指令设置", render: FORM_ITEM_RENDER_ENUM.DIVIDER }, loading={props.enterprisePlan.enterprisePlanCommandSettingLoading}
{ options={[
name: "responseLevel", { key: "divider", label: "指令设置", render: FORM_ITEM_RENDER_ENUM.DIVIDER },
label: "响应级别", {
render: FORM_ITEM_RENDER_ENUM.CHECKBOX, name: "responseLevel",
items: responseLevelList, label: "响应级别",
itemsField: { labelKey: "levelName", valueKey: "id" }, render: FORM_ITEM_RENDER_ENUM.CHECKBOX,
componentProps: { items: responseLevelList,
onChange: (values) => { itemsField: { labelKey: "levelName", valueKey: "id" },
const responseLevelName = []; componentProps: {
for (let i = 0; i < values.length; i++) { onChange: (values) => {
responseLevelName.push(getLabelName({ const responseLevelName = [];
list: responseLevelList, for (let i = 0; i < values.length; i++) {
status: values[i], responseLevelName.push(getLabelName({
idKey: "id", list: responseLevelList,
nameKey: "levelName", status: values[i],
})); idKey: "id",
} nameKey: "levelName",
form.setFieldValue("responseLevelName", responseLevelName); }));
}
form.setFieldValue("responseLevelName", responseLevelName);
},
},
}, },
}, { name: "responseLevelName", label: "响应级别名称", onlyForLabel: true },
}, { name: "commandContent", label: "指令内容", render: FORM_ITEM_RENDER_ENUM.TEXTAREA },
{ name: "responseLevelName", label: "响应级别名称", onlyForLabel: true }, {
{ name: "commandContent", label: "指令内容", render: FORM_ITEM_RENDER_ENUM.TEXTAREA }, name: "executingAgencyId",
{ label: "执行机构",
name: "executingAgencyId", render: (
label: "执行机构", <BasicSelectTree
render: ( treeData={executingAgencyListTree}
<BasicSelectTree nameKey="orgName"
treeData={executingAgencyListTree} multiple
nameKey="orgName" onGetLabel={(label) => {
multiple form.setFieldValue("executingAgencyName", label);
onGetLabel={(label) => { }}
form.setFieldValue("executingAgencyName", label); />
}} ),
/> },
), { name: "executingAgencyName", label: "执行机构名称", onlyForLabel: true },
}, ]}
{ name: "executingAgencyName", label: "执行机构名称", onlyForLabel: true }, form={form}
]} onFinish={onSubmit}
form={form} />
onFinish={onSubmit} <Space style={{ justifyContent: "center", width: "100%" }}>
/> <Button
<Space style={{ justifyContent: "center", width: "100%" }}> loading={props.enterprisePlan.enterprisePlanCommandSettingLoading}
<Button type="primary"
loading={props.enterprisePlan.enterprisePlanCommandSettingLoading} onClick={() => {
type="primary" form.submit();
onClick={() => { }}
form.submit(); >
}} 保存
> </Button>
保存 <Button onClick={() => {
</Button> form.resetFields();
<Button onClick={() => { }}
form.resetFields(); >
}} 重置
> </Button>
重置 </Space>
</Button> </div>
</Space> )
</div> }
<div style={{ width: "50%" }}> <div style={{ flex: 1 }}>
<Divider orientation="left">指令列表</Divider> <Divider orientation="left">指令列表</Divider>
<Table <Table
options={false} options={false}
@ -165,6 +169,7 @@ function PlanInstructions(props) {
title: "操作", title: "操作",
width: 150, width: 150,
fixed: "right", fixed: "right",
hidden: props.mode === "view",
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
<Button <Button

View File

@ -41,15 +41,19 @@ function GradeResponse(props) {
<div> <div>
<Table <Table
toolBarRender={() => ( toolBarRender={() => (
<Button <Space>
type="primary" {props.mode !== "view" && (
icon={(<AddIcon />)} <Button
onClick={() => { type="primary"
setAddModalVisible(true); icon={(<AddIcon />)}
}} onClick={() => {
> setAddModalVisible(true);
新增 }}
</Button> >
新增
</Button>
)}
</Space>
)} )}
columns={[ columns={[
{ title: "资源类型", dataIndex: "resourceType" }, { title: "资源类型", dataIndex: "resourceType" },
@ -73,24 +77,28 @@ function GradeResponse(props) {
> >
查看 查看
</Button> </Button>
<Button {props.mode !== "view" && (
type="link" <Button
onClick={() => { type="link"
setCurrentId(record.id); onClick={() => {
setAddModalVisible(true); setCurrentId(record.id);
}} setAddModalVisible(true);
> }}
编辑 >
</Button> 编辑
<Button </Button>
type="link" )}
danger {props.mode !== "view" && (
onClick={() => { <Button
onDelete(record.id); type="link"
}} danger
> onClick={() => {
删除 onDelete(record.id);
</Button> }}
>
删除
</Button>
)}
</Space> </Space>
), ),
}, },

View File

@ -24,10 +24,11 @@ function PlanText(props) {
values={{ values={{
planName: props.planName, planName: props.planName,
}} }}
span={24}
submitButtonText="保存并下一步" submitButtonText="保存并下一步"
options={[ options={[
{ name: "planName", label: "预案名称", span: 24, componentProps: { disabled: true } }, { name: "planName", label: "预案名称", componentProps: { disabled: true } },
{ name: "planText", label: "预案文本", span: 24, render: FORM_ITEM_RENDER_ENUM.TEXTAREA, required: false }, { name: "planText", label: "预案文本", render: FORM_ITEM_RENDER_ENUM.TEXTAREA, required: false },
]} ]}
form={form} form={form}
onFinish={onSubmit} onFinish={onSubmit}

View File

@ -57,10 +57,6 @@ function Add(props) {
planInstructions: "planText", planInstructions: "planText",
}; };
if (urlState.currentStep === "planText") {
return;
}
if (urlState.currentStep === "planAttachments") { if (urlState.currentStep === "planAttachments") {
setReviewPersonModalVisible(true); setReviewPersonModalVisible(true);
return; return;

View File

@ -61,7 +61,7 @@ function List(props) {
{ {
title: "审核状态", title: "审核状态",
dataIndex: "auditFlag", dataIndex: "auditFlag",
render: (_, record) => getLabelName({ list: ENTERPRISE_PLAN_AUDIT_STATUS_ENUM, status: record.auditFlag }) || "暂存", render: (_, record) => getLabelName({ list: ENTERPRISE_PLAN_AUDIT_STATUS_ENUM, status: record.auditFlag }) || "待完善",
}, },
{ {
title: "操作", title: "操作",
@ -72,7 +72,7 @@ function List(props) {
<Button <Button
type="link" type="link"
onClick={() => { onClick={() => {
props.history.push(`./view?id=${record.id}`); props.history.push(`./view?id=${record.id}&planName=${record.planName}`);
}} }}
> >
查看 查看

View File

@ -0,0 +1,40 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Descriptions } from "antd";
import { useEffect, useState } from "react";
import { NS_ENTERPRISE_PLAN } from "~/enumerate/namespace";
function BasicInfo(props) {
const [info, setInfo] = useState({});
const getData = async () => {
const { data } = await props["enterprisePlanBasicInfoInfo"]({ id: props.planId });
setInfo(data);
};
useEffect(() => {
getData();
}, [props.planId]);
return (
<div>
<Descriptions
bordered
column={2}
styles={{ label: { width: 200 } }}
items={[
{ label: "预案名称", children: info.planName },
{ label: "预案类型", children: info.planTypeName },
{ label: "预案编码", children: info.planCode },
{ label: "预案等级", children: info.planLevelName },
{ label: "事件类型", children: info.eventTypeName },
{ label: "经度", children: info.longitude },
{ label: "纬度", children: info.latitude },
{ label: "预案概述", children: info.planOverview },
{ label: "备注", children: info.remarks },
]}
/>
</div>
);
}
export default Connect([NS_ENTERPRISE_PLAN], true)(BasicInfo);

View File

@ -0,0 +1,7 @@
import GradeResponsePage from "../../../Add/components/GradeResponse";
function GradeResponse(props) {
return <GradeResponsePage mode="view" {...props} />;
}
export default GradeResponse;

View File

@ -0,0 +1,7 @@
import OrganizationStructurePage from "../../../Add/components/OrganizationStructure";
function OrganizationStructure(props) {
return <OrganizationStructurePage mode="view" {...props} />;
}
export default OrganizationStructure;

View File

@ -0,0 +1,7 @@
import PlanAttachmentsPage from "../../../Add/components/PlanAttachments";
function PlanAttachments(props) {
return <PlanAttachmentsPage mode="view" {...props} />;
}
export default PlanAttachments;

View File

@ -0,0 +1,7 @@
import PlanInstructionsPage from "../../../Add/components/PlanInstructions";
function PlanInstructions(props) {
return <PlanInstructionsPage mode="view" {...props} />;
}
export default PlanInstructions;

View File

@ -0,0 +1,7 @@
import PlanResourcesPage from "../../../Add/components/PlanResources";
function PlanResources(props) {
return <PlanResourcesPage mode="view" {...props} />;
}
export default PlanResources;

View File

@ -0,0 +1,21 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Descriptions } from "antd";
import { NS_ENTERPRISE_PLAN } from "~/enumerate/namespace";
function BasicInfo(props) {
return (
<div>
<Descriptions
bordered
column={1}
styles={{ label: { width: 200 } }}
items={[
{ label: "预案名称", children: props.planName },
{ label: "预案文本", children: props.planText },
]}
/>
</div>
);
}
export default Connect([NS_ENTERPRISE_PLAN], true)(BasicInfo);

View File

@ -0,0 +1,71 @@
import useUrlState from "@ahooksjs/use-url-state";
import { Tabs } from "antd";
import Page from "zy-react-library/components/Page";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import BasicInfo from "./components/BasicInfo";
import GradeResponse from "./components/GradeResponse";
import OrganizationStructure from "./components/OrganizationStructure";
import PlanAttachments from "./components/PlanAttachments";
import PlanInstructions from "./components/PlanInstructions";
import PlanResources from "./components/PlanResources";
import PlanText from "./components/PlanText";
function View() {
const query = useGetUrlQuery();
const [urlState, setUrlState] = useUrlState(
{
currentStep: "basicInfo",
planId: query.id,
planName: query.planName,
},
{ navigateMode: "replace" },
);
return (
<Page headerTitle="查看">
<Tabs
activeKey={urlState.currentStep}
onChange={key => setUrlState({ currentStep: key })}
items={[
{
key: "basicInfo",
label: "基本信息",
children: (<BasicInfo planId={query.id} />),
},
{
key: "gradeResponse",
label: "分级响应",
children: (<GradeResponse planId={query.id} />),
},
{
key: "organizationStructure",
label: "组织结构",
children: (<OrganizationStructure planId={query.id} />),
},
{
key: "planResources",
label: "预案资源",
children: (<PlanResources planId={query.id} />),
},
{
key: "planInstructions",
label: "预案指令",
children: (<PlanInstructions planId={query.id} />),
},
{
key: "planText",
label: "预案文案",
children: (<PlanText planId={query.id} planName={urlState.planName} />),
},
{
key: "planAttachments",
label: "预案附件",
children: (<PlanAttachments planId={query.id} />),
},
]}
/>
</Page>
);
}
export default View;