feat(enclosedArea): 新增封闭区域管理功能

- 实现封闭区域的列表展示、新增、编辑、删除功能
- 支持封闭区域的父子层级结构展示与跳转
- 集成地图组件用于显示和选择封闭区域位置
- 提供查看详情弹窗,支持地理位置查看
- 封装通用表格与搜索组件提升复用性
- 添加权限控制字段便于后续权限配置接入
master
fangjiakai 2025-12-19 17:56:11 +08:00
parent 3045b714db
commit e0dac3d2de
6 changed files with 622 additions and 0 deletions

View File

@ -0,0 +1,24 @@
import {declareRequest} from '@cqsjjb/jjb-dva-runtime';
export const enclosedAreaList = declareRequest(
'enclosedAreaLoading',
'Post > @/risk/enclosedArea/list',
);
export const enclosedAreaAdd = declareRequest(
'enclosedAreaLoading',
'Post > @/risk/enclosedArea/save'
);
export const enclosedAreaEdit = declareRequest(
'enclosedAreaLoading',
'Put > @/risk/enclosedArea/edit'
);
export const enclosedAreaDelete = declareRequest(
'enclosedAreaLoading',
'Delete > @/risk/enclosedArea/{id}'
);
export const enclosedAreaBatchDelete = declareRequest(
'enclosedAreaLoading',
'Delete > @/risk/enclosedArea/ids/{ids}'
);
export const enclosedAreaInfo = declareRequest('enclosedAreaLoading', 'Get > /risk/enclosedArea/{id}');

View File

@ -0,0 +1,248 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space, Descriptions } from "antd";
import { useEffect, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
import Search from "zy-react-library/components/Search";
import Table from "zy-react-library/components/Table";
import useTable from "zy-react-library/hooks/useTable";
import { NS_ENCLOSEDAREA } from "~/enumerate/namespace";
import DictionarySelect from "zy-react-library/components/Select/Dictionary";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import useGetUrlQuery from "zy-react-library/hooks/useGetUrlQuery";
import Map from "zy-react-library/components/Map";
import LocationIcon from "zy-react-library/components/Icon/LocationIcon";
import MapSelector from "zy-react-library/components/Map/MapSelector";
function EnclosedArea(props) {
const { parentId, parentName } = useGetUrlQuery();
const [addModalVisible, setAddModalVisible] = useState(false);
const [infoModalVisible, setInfoModalVisible] = useState(false);
const [currentId, setCurrentId] = useState("");
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["enclosedAreaList"], {
form,
params: {
eqParentId: parentId || 0,
}
});
const onDelete = async (record) => {
if (record.childCount > 0 || record.useCount > 0)
return message.warning("正在使用中的封闭区域不能删除");
await props["enclosedAreaDelete"]({ id: record.id });
message.success("删除成功");
getData();
};
return (
<div style={{ padding: 10 }}>
{parentId && <HeaderBack title={`${parentName}`} />}
<Search
form={form}
onFinish={getData}
options={[
{ name: "likeName", label: "封闭区域名称" },
{ name: "eqType", label: "封闭区域类型", render: <DictionarySelect dictValue="enclosedarea_type" /> },
]}
/>
<Table
rowSelection={{
selectedRowKeys,
onChange: selectedRowKeys => setSelectedRowKeys(selectedRowKeys),
}}
toolBarRender={() => (
<Space>
<Button type="primary" icon={<AddIcon />} onClick={() => setAddModalVisible(true)}>新增</Button>
</Space>
)}
columns={[
{
dataIndex: "name", title: "封闭区域名称", render: (_, record) => (
<Button
block
type="link"
onClick={() => props.history.push(`./List?parentId=${record.id}&parentName=${record.name}`)}
>
{`${record.name}>`}
</Button>
),
},
{ dataIndex: "typeName", title: "封闭区域类型" },
{
title: "操作",
align: "center",
width: 200,
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setInfoModalVisible(true);
}}
>
查看
</Button>
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setAddModalVisible(true);
}}
>
编辑
</Button>
<Button
type="link"
danger
onClick={() => {
Modal.confirm({
title: "确定删除吗?",
onOk: async () => {
await onDelete(record);
},
});
}}
>
删除
</Button>
</Space>
),
},
]}
{...tableProps}
/>
<AddModal
visible={addModalVisible}
currentId={currentId}
parentId={parentId || 0}
parentName={parentName || "无"}
onCancel={() => {
setAddModalVisible(false);
setCurrentId("");
}}
getData={getData}
/>
<InfoModal
visible={infoModalVisible}
currentId={currentId}
onCancel={() => {
setInfoModalVisible(false);
setCurrentId("");
}}
getData={getData}
/>
</div>
);
}
function AddModalComponent(props) {
const [form] = Form.useForm();
useEffect(() => {
if (props.currentId) {
props["enclosedAreaInfo"]({ id: props.currentId }).then((res) => {
form.setFieldsValue(res.data);
});
}
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
const submit = async (values) => {
await props[!props.currentId ? "enclosedAreaAdd" : "enclosedAreaEdit"]({ ...values, id: props.currentId, parentId: props.parentId });
onCancel();
props.getData();
};
return (
<Modal
open={props.visible}
onCancel={onCancel}
onOk={form.submit}
title={props.currentId ? "编辑" : "新增"}
loading={props.enclosedArea.loading}
>
<FormBuilder
form={form}
span={24}
onFinish={submit}
showActionButtons={false}
options={[
{
name: "parentName",
required: false,
label: "上级区域",
render: <Tag color="processing">{parentName}</Tag>,
},
{ name: "name", label: "封闭区域名称" },
{
name: "type", label: "封闭区域类型", render: (
<DictionarySelect
dictValue="enclosedarea_type"
onGetLabel={(label) => {
form.setFieldValue("typeName", label);
}}
/>
),
},
{ name: "typeName", label: "封闭区域类型", onlyForLabel: true },
{ key: "map", customizeRender: true, render: <Map /> },
]}
/>
</Modal>
);
}
function InfoModalComponent(props) {
const [info, setInfo] = useState({});
const [mapVisible, setMapVisible] = useState(false);
useEffect(() => {
if (props.currentId) {
props["enclosedAreaInfo"]({ id: props.currentId }).then((res) => {
setInfo(res.data);
});
}
}, [props.currentId]);
return (
<Modal
onCancel={props.onCancel}
open={props.visible}
footer={<Button onClick={props.onCancel}>关闭</Button>}
title="查看"
loading={props.enclosedArea.loading}
>
<Descriptions
labelStyle={{ width: 200 }}
column={1}
bordered
items={[
{ children: info.parentName, label: "父级名称" },
{ children: info.name, label: "封闭区域名称" },
{ children: info.typeName, label: "封闭区域" },
{ children: info.corpinfoName, label: "管辖单位" },
{
children: <LocationIcon onClick={() => {
setMapVisible(true);
}} />, label: "位置"
},
]}
/>
<MapSelector
visible={mapVisible}
onClose={() => setMapVisible(false)}
longitude={info.longitude}
latitude={info.latitude}
disable={true}
/>
</Modal>
);
}
const AddModal = Connect([NS_ENCLOSEDAREA], true)(AddModalComponent);
const InfoModal = Connect([NS_ENCLOSEDAREA], true)(InfoModalComponent);
export default Connect([NS_ENCLOSEDAREA], true)(EnclosedArea);

View File

@ -0,0 +1,16 @@
import Area from "../components/Area";
function AreaContainer(props) {
return (
<Area areaLevel={1}
addPermission={"zcloud-mkmj-area-add"}
editPermission={"zcloud-mkmj-area-edit"}
deletePermission={"zcloud-mkmj-area-delete"}
deleteBatchPermission={"zcloud-mkmj-area-delete-batch"}
exportPermission={"zcloud-mkmj-area-export"}
{...props}
/>
);
}
export default AreaContainer;

View File

@ -0,0 +1,16 @@
import Area from "../components/Area";
function AreaContainer(props) {
return (
<Area areaLevel={2}
addPermission={"zcloud-mkmj-area-add"}
editPermission={"zcloud-mkmj-area-edit"}
deletePermission={"zcloud-mkmj-area-delete"}
deleteBatchPermission={"zcloud-mkmj-area-delete-batch"}
exportPermission={"zcloud-mkmj-area-export"}
{...props}
/>
);
}
export default AreaContainer;

View File

@ -0,0 +1,17 @@
import Area from "../components/Area";
function AreaContainer(props) {
return (
<Area areaLevel={2}
isSupervise={true}
addPermission={"zcloud-mkmj-area-add"}
editPermission={"zcloud-mkmj-area-edit"}
deletePermission={"zcloud-mkmj-area-delete"}
deleteBatchPermission={"zcloud-mkmj-area-delete-batch"}
exportPermission={"zcloud-mkmj-area-export"}
{...props}
/>
);
}
export default AreaContainer;

View File

@ -0,0 +1,301 @@
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space, Descriptions } from "antd";
import { useEffect, useState } from "react";
import FormBuilder from "zy-react-library/components/FormBuilder";
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_MKMJAREA } from "~/enumerate/namespace";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
import DeleteIcon from "zy-react-library/components/Icon/DeleteIcon";
import LocationIcon from "zy-react-library/components/Icon/LocationIcon";
import DictionarySelect from "zy-react-library/components/Select/Dictionary";
import Map from "zy-react-library/components/Map";
import MapSelector from "zy-react-library/components/Map/MapSelector";
import { getLabelName } from "zy-react-library/utils"
const AREA_TYPE = [
{ name: "人行口门", bianma: "1" },
{ name: "车行口门", bianma: "2" },
{ name: "综合口门", bianma: "3" },
]
const AREA_STATUS = [
{ name: "停用", bianma: "0" },
{ name: "正常", bianma: "1" },
{ name: "暂时关闭", bianma: "2" },
]
function MkmjArea(props) {
const [addModalVisible, setAddModalVisible] = useState(false);
const [infoModalVisible, setInfoModalVisible] = useState(false);
const [currentId, setCurrentId] = useState("");
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["mkmjAreaList"], {
form,
params: {
eqAreaLevel: props.areaLevel,
}
});
const onDelete = async (record) => {
if (record.videoNum > 0 || record.passageNum > 0)
return message.warning("正在使用中的口门不能删除");
await props["mkmjAreaDelete"]({ id: record.id });
message.success("删除成功");
getData();
};
return (
<div style={{ padding: 10 }}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "likeAreaName", label: "口门名称" },
{ name: "eqAreaStatus", label: "口门状态", render: FORM_ITEM_RENDER_ENUM.SELECT, items: AREA_STATUS },
{ name: "eqAreaType", label: "口门类型", render: FORM_ITEM_RENDER_ENUM.SELECT, items: AREA_TYPE },
{ name: "eqAreaParentId", label: "所属区域", render: <DictionarySelect dictValue="primeport_area" /> },
]}
/>
<Table
rowSelection={{
selectedRowKeys,
onChange: selectedRowKeys => setSelectedRowKeys(selectedRowKeys),
}}
toolBarRender={() => (
<Space>
<Button type="primary" icon={<AddIcon />} onClick={() => setAddModalVisible(true)}>新增</Button>
</Space>
)}
columns={[
{ dataIndex: "areaParentName", title: "所属区域" },
{
dataIndex: "areaType", title: "口门类型", render: (_, record) => {
return record.areaType === 1 ? "人行口门" : record.areaType === 2 ? "车行口门" : "综合口门";
}
},
{ dataIndex: "areaName", title: "口门名称" },
{
dataIndex: "location", title: "口门位置", render: (_, record) => {
return `${record.latitude || ""}--${record.longitude || ""}`;
}
},
{ dataIndex: "corpInfoName", title: "管辖单位", hidden: props.areaLevel === 1 },
{
dataIndex: "videoNum", title: "视频个数", render: (_, record) => {
return record.videoNum || 0;
}
},
{
dataIndex: "areaStatus", title: "口门状态", render: (_, record) => {
return record.areaStatus === 0 ? "停用" : record.areaStatus === 1 ? "正常" : "暂时关闭";
}
},
{
title: "操作",
align: "center",
width: 200,
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setInfoModalVisible(true);
}}
>
查看
</Button>
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setAddModalVisible(true);
}}
>
编辑
</Button>
<Button
type="link"
danger
onClick={() => {
Modal.confirm({
title: "确定删除吗?",
onOk: async () => {
await onDelete(record);
},
});
}}
>
删除
</Button>
<Button
type="link"
onClick={() => {
props.history.push(`./MkmjPassage?areaId=${record.areaId}`)
}}
>
添加通道
</Button>
<Button
type="link"
onClick={() => {
props.history.push(`./MkmjGateVideo?areaGateId=${record.areaId}&deviceType=1`)
}}
>
添加摄像头
</Button>
</Space>
),
},
]}
{...tableProps}
/>
{addModalVisible && <AddModal
currentId={currentId}
areaLevel={props.areaLevel}
isSupervise={props.isSupervise}
onCancel={() => {
setAddModalVisible(false);
setCurrentId("");
}}
getData={getData}
/>}
{infoModalVisible && <InfoModal
currentId={currentId}
areaLevel={props.areaLevel}
isSupervise={props.isSupervise}
onCancel={() => {
setInfoModalVisible(false);
setCurrentId("");
}}
getData={getData}
/>}
</div>
);
}
function AddModalComponent(props) {
const [form] = Form.useForm();
const [corpInfoList, setCorpInfoList] = useState([]);
useEffect(() => {
props["getCorpInfoList"]({}).then((res) => {
setCorpInfoList(res.data || []);
});
if (props.currentId) {
props["mkmjAreaInfo"]({ id: props.currentId }).then((res) => {
form.setFieldsValue({
...res.data,
areaType: res.data.areaType + "",
areaStatus: res.data.areaStatus + "",
});
});
}
}, [props.currentId]);
const onCancel = () => {
form.resetFields();
props.onCancel();
};
const submit = async (values) => {
await props[!props.currentId ? "mkmjAreaAdd" : "mkmjAreaEdit"]({ ...values, areaLevel: props.areaLevel, id: props.currentId });
onCancel();
props.getData();
};
return (
<Modal
open
onCancel={onCancel}
onOk={form.submit}
title={props.currentId ? "编辑" : "新增"}
loading={props.mkmjArea.loading}
width={800}
>
<FormBuilder
labelCol={{ span: 6 }}
form={form}
span={24}
onFinish={submit}
showActionButtons={false}
options={[
{ name: "areaParentId", label: "所属区域", render: <DictionarySelect dictValue="primeport_area" onGetLabel={(label) => form.setFieldValue("areaParentName", label)} /> },
{ name: "areaParentName", label: "所属区域", onlyForLabel: true },
{
name: "corpInfoId", label: "管辖单位", render: FORM_ITEM_RENDER_ENUM.SELECT, items: corpInfoList,
itemsField: { valueKey: "id", labelKey: "corpName" },
componentProps: { onChange: (value) => form.setFieldValue("corpInfoName", getLabelName({ status: value, list: corpInfoList, idKey: "id", nameKey: "corpName" })) },
hidden: !(props.areaLevel === 2 && props.isSupervise),
},
{ name: "corpInfoName", label: "管辖单位", onlyForLabel: true },
{ name: "areaName", label: "口门名称" },
{ name: "areaType", label: "口门类型", render: FORM_ITEM_RENDER_ENUM.SELECT, items: AREA_TYPE },
{ name: "areaRange", label: "口门位置" },
{ key: "map", customizeRender: true, render: <Map /> },
{ name: "areaStatus", label: "口门状态", render: FORM_ITEM_RENDER_ENUM.SELECT, items: AREA_STATUS }
]}
/>
</Modal>
);
}
function InfoModalComponent(props) {
const [info, setInfo] = useState({});
const [mapVisible, setMapVisible] = useState(false);
useEffect(() => {
if (props.currentId) {
props["mkmjAreaInfo"]({ id: props.currentId }).then((res) => {
setInfo(res.data);
});
}
}, [props.currentId]);
return (
<Modal
open
onCancel={props.onCancel}
footer={<Button onClick={props.onCancel}>关闭</Button>}
title="查看"
loading={props.mkmjArea.loading}
>
<Descriptions
labelStyle={{ width: 200 }}
column={1}
bordered
items={[
{ children: info.areaParentName, label: "所属区域" },
{ children: info.corpInfoName, label: "管辖单位", hidden: props.areaLevel ===1},
{ children: info.areaName, label: "口门名称" },
{ children: info.areaRange, label: "口门位置" },
{ children: info.areaLevel === 1 ? "一级" : info.areaLevel === 2 ? "二级" : "三级", label: "区域级别" },
{ children: info.areaType === 1 ? "人行口门" : info.areaType === 2 ? "车行口门" : "综合口门", label: "口门类型" },
{ children: info.areaStatus === 0 ? "停用" : info.areaStatus === 1 ? "正常" : "暂时关闭", label: "区域状态" },
{
children: <LocationIcon onClick={() => {
setMapVisible(true);
}} />, label: "位置"
},
]}
/>
<MapSelector
visible={mapVisible}
onClose={() => setMapVisible(false)}
longitude={info.longitude}
latitude={info.latitude}
disable={true}
/>
</Modal>
);
}
const AddModal = Connect([NS_MKMJAREA], true)(AddModalComponent);
const InfoModal = Connect([NS_MKMJAREA], true)(InfoModalComponent);
export default Connect([NS_MKMJAREA], true)(MkmjArea);