feat(iotalarm): 初始化物联网消防报警模块前端骨架

- 新增多项公共常量配置,便于全局状态和状态选项管理
- 重构全局命名空间常量定义,支持传感器类型等多个领域管理
- 完善 Container 页面主题配置,初始化主题为默认算法和样式
- 新增业务模型组件统一导出,支持设备类型、区域及报警信息等模块
- 更新包依赖版本,确保依赖库兼容最新功能
- 设计并实现多个 API 请求声明,覆盖传感器、设备区域、报警信息及处置
- 新增报警处置分配相关组件,支持单条及批量分配功能
- 新增 Dcs 和 Threshold 两类报警列表组件,支持多维搜索和表格展示
- 实现报警记录组件,支持详情查看和状态管理
- 新增设备区域管理功能,包括绑定传感器和配置负责人模块
- 优化全局上下文定义,设置InjectContext默认值为null,避免潜在错误风险
- 重构首页展示,简化底座模板描述,突出消防报警模块状态提示
main
wangyan 2026-04-03 16:14:26 +08:00
parent 8259ec5b0f
commit a18d61521e
52 changed files with 2017 additions and 37 deletions

View File

@ -31,7 +31,7 @@
"lodash-es": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"zy-react-library": "^1.2.19"
"zy-react-library": "^1.2.20"
},
"devDependencies": {
"@antfu/eslint-config": "^5.4.1",

View File

@ -0,0 +1,6 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const alarmAssignList = declareRequest("alarmDisposeLoading", "Post > @/iotalarm/alarmDispose/assignList");
export const alarmAssign = declareRequest("alarmDisposeLoading", "Post > @/iotalarm/alarmDispose/assign");
export const alarmBatchAssign = declareRequest("alarmDisposeLoading", "Post > @/iotalarm/alarmDispose/batchAssign");
export const alarmRecordList = declareRequest("alarmRecordLoading", "Post > @/iotalarm/alarmRecord/list");

View File

@ -0,0 +1,4 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const dcsAlarmInfoList = declareRequest("dcsAlarmInfoLoading", "Post > @/iotalarm/dcsAlarmInfo/list");
export const thresholdAlarmInfoList = declareRequest("thresholdAlarmInfoLoading", "Post > @/iotalarm/thresholdAlarmInfo/list");

View File

@ -0,0 +1,10 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const alarmRecordList = declareRequest(
"alarmRecordLoading",
`Post > @/iotalarm/alarmRecord/list`,
);
export const alarmRecordInfo = declareRequest(
"alarmRecordLoading",
`Get > /iotalarm/alarmRecord/{id}`,
);

View File

@ -0,0 +1,9 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const deviceRegionList = declareRequest("deviceRegionLoading", "Post > @/iotalarm/deviceRegion/list");
export const deviceRegionFireRegionList = declareRequest("deviceRegionLoading", "Get > @/iotalarm/deviceRegion/fireRegionList");
export const deviceRegionSaveOrUpdate = declareRequest("deviceRegionLoading", "Post > @/iotalarm/deviceRegion/saveOrUpdate");
export const deviceRegionBindSensor = declareRequest("deviceRegionLoading", "Post > @/iotalarm/deviceRegion/bindSensor");
export const deviceRegionUnbindSensor = declareRequest("deviceRegionLoading", "Post > @/iotalarm/deviceRegion/unbindSensor");
export const deviceRegionManager = declareRequest("deviceRegionLoading", "Put > @/iotalarm/deviceRegion/manager");
export const deviceRegionInfo = declareRequest("deviceRegionLoading", "Get > @/iotalarm/deviceRegion/{id}");

View File

@ -0,0 +1,9 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const sensorDeviceList = declareRequest("sensorDeviceLoading", "Post > @/iotalarm/sensorDevice/list");
export const sensorDeviceInfo = declareRequest("sensorDeviceLoading", "Get > @/iotalarm/sensorDevice/{id}");
export const sensorDeviceSave = declareRequest("sensorDeviceLoading", "Post > @/iotalarm/sensorDevice/save");
export const sensorDeviceEdit = declareRequest("sensorDeviceLoading", "Put > @/iotalarm/sensorDevice/edit");
export const sensorDeviceStatus = declareRequest("sensorDeviceLoading", "Put > @/iotalarm/sensorDevice/status");
export const sensorDeviceThreshold = declareRequest("sensorDeviceLoading", "Put > @/iotalarm/sensorDevice/threshold");
export const sensorDeviceRemove = declareRequest("sensorDeviceLoading", "Delete > @/iotalarm/sensorDevice/{id}");

View File

@ -0,0 +1,7 @@
import { declareRequest } from "@cqsjjb/jjb-dva-runtime";
export const sensorTypeList = declareRequest("sensorTypeLoading", "Post > @/iotalarm/sensorType/list");
export const sensorTypeInfo = declareRequest("sensorTypeLoading", "Get > @/iotalarm/sensorType/{id}");
export const sensorTypeSave = declareRequest("sensorTypeLoading", "Post > @/iotalarm/sensorType/save");
export const sensorTypeEdit = declareRequest("sensorTypeLoading", "Put > @/iotalarm/sensorType/edit");
export const sensorTypeRemove = declareRequest("sensorTypeLoading", "Delete > @/iotalarm/sensorType/{id}");

6
src/api/index.js Normal file
View File

@ -0,0 +1,6 @@
export * from "./SensorType";
export * from "./SensorDevice";
export * from "./DeviceRegion";
export * from "./AlarmInfo";
export * from "./AlarmDispose";
export * from "./AlarmRecord";

View File

@ -0,0 +1,147 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Space, Modal } from "antd";
import { useState } from "react";
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 { getLabelName } from "zy-react-library/utils";
import AssignModal from "~/components/AlarmDispose/AssignModal";
import { ALARM_STATUS_OPTIONS, ALARM_SOURCE_OPTIONS, ALARM_LEVEL_OPTIONS, ALARM_TYPE_OPTIONS } from "~/enumerate/constant";
import { NS_ALARMDISPOSE } from "~/enumerate/namespace";
function AssignList(props) {
const [form] = Form.useForm();
const [modalOpen, setModalOpen] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [currentAlarmId, setCurrentAlarmId] = useState(null);
const [isBatch, setIsBatch] = useState(false);
const { tableProps, getData } = useTable(props["alarmAssignList"], {
form,
defaultParams: { status: [10, 20] },
});
const assignPermission = `${props.type}-iotalarm-alarm-assign`;
const handleBatchAssign = () => {
if (selectedRowKeys.length === 0) {
message.warning("请先选择要分配的报警记录");
return;
}
setIsBatch(true);
setCurrentAlarmId(null);
setModalOpen(true);
};
const handleSingleAssign = (record) => {
setIsBatch(false);
setCurrentAlarmId(record.id);
setModalOpen(true);
};
const rowSelection = {
selectedRowKeys,
onChange: setSelectedRowKeys,
};
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "alarmName", label: "报警名称" },
{ name: "deviceName", label: "设备名称" },
{
name: "alarmSource",
label: "报警来源",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ALARM_SOURCE_OPTIONS,
},
{
name: "status",
label: "报警状态",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ALARM_STATUS_OPTIONS.filter(item => item.bianma === 10 || item.bianma === 20),
},
]}
/>
<Table
rowKey="id"
rowSelection={rowSelection}
toolBarRender={() => (
<Space>
{props.permission(assignPermission) && (
<Button type="primary" onClick={handleBatchAssign}>
批量分配
</Button>
)}
</Space>
)}
columns={[
{ title: "报警名称", dataIndex: "alarmName", ellipsis: true },
{ title: "设备名称", dataIndex: "deviceName", ellipsis: true },
{ title: "报警位置", dataIndex: "alarmLocation", ellipsis: true },
{
title: "报警来源",
dataIndex: "alarmSource",
render: value => getLabelName({ status: value, list: ALARM_SOURCE_OPTIONS }) || value || "-",
},
{
title: "报警级别",
dataIndex: "alarmLevel",
render: value => getLabelName({ status: value, list: ALARM_LEVEL_OPTIONS }) || value || "-",
},
{
title: "报警类型",
dataIndex: "alarmType",
render: value => getLabelName({ status: value, list: ALARM_TYPE_OPTIONS }) || value || "-",
},
{
title: "报警状态",
dataIndex: "status",
render: value => getLabelName({ status: value, list: ALARM_STATUS_OPTIONS }) || value || "-",
},
{ title: "报警时间", dataIndex: "alarmTime", width: 180 },
{ title: "报警值", dataIndex: "alarmValue" },
{
title: "操作",
width: 120,
render: (_, record) => (
<Space>
{props.permission(assignPermission) && (
<Button type="link" onClick={() => handleSingleAssign(record)}>
分配
</Button>
)}
</Space>
),
},
]}
{...tableProps}
/>
{modalOpen && (
<AssignModal
open={modalOpen}
isBatch={isBatch}
alarmId={currentAlarmId}
alarmIds={selectedRowKeys}
loading={props.alarmDisposeLoading}
getData={getData}
alarmAssign={props.alarmAssign}
alarmBatchAssign={props.alarmBatchAssign}
onCancel={() => {
setModalOpen(false);
setCurrentAlarmId(null);
setSelectedRowKeys([]);
}}
/>
)}
</Page>
);
}
export default Connect([NS_ALARMDISPOSE], true)(Permission(AssignList));

View File

@ -0,0 +1,90 @@
import { Form, Modal, Select } from "antd";
import { useEffect } from "react";
import PersonnelSelect from "zy-react-library/components/Select/Personnel/Gwj";
import { ALARM_LEVEL_OPTIONS, ALARM_TYPE_OPTIONS } from "~/enumerate/constant";
function AssignModal(props) {
const [form] = Form.useForm();
useEffect(() => {
if (!props.open) {
return;
}
form.resetFields();
}, [form, props.open]);
const handleCancel = () => {
form.resetFields();
props.onCancel();
};
const handleSubmit = async (values) => {
if (props.isBatch) {
await props.alarmBatchAssign({
alarmIds: props.alarmIds,
disposeUserId: values.disposeUserId,
alarmLevel: values.alarmLevel,
alarmType: values.alarmType,
});
} else {
await props.alarmAssign({
alarmId: props.alarmId,
disposeUserId: values.disposeUserId,
alarmLevel: values.alarmLevel,
alarmType: values.alarmType,
});
}
handleCancel();
props.getData();
};
return (
<Modal
destroyOnClose
open={props.open}
title={props.isBatch ? "批量分配" : "分配处置"}
onCancel={handleCancel}
onOk={form.submit}
confirmLoading={props.loading}
width={640}
>
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Form.Item
label="处置人"
name="disposeUserId"
rules={[{ required: true, message: "请选择处置人" }]}
>
<PersonnelSelect placeholder="请选择处置人" />
</Form.Item>
<Form.Item
label="报警级别"
name="alarmLevel"
rules={[{ required: true, message: "请选择报警级别" }]}
>
<Select placeholder="请选择报警级别">
{ALARM_LEVEL_OPTIONS.map(item => (
<Select.Option key={item.bianma} value={item.bianma}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
<Form.Item
label="报警类型"
name="alarmType"
rules={[{ required: true, message: "请选择报警类型" }]}
>
<Select placeholder="请选择报警类型">
{ALARM_TYPE_OPTIONS.map(item => (
<Select.Option key={item.bianma} value={item.bianma}>
{item.name}
</Select.Option>
))}
</Select>
</Form.Item>
</Form>
</Modal>
);
}
export default AssignModal;

View File

@ -0,0 +1,2 @@
export { default as AssignList } from "./AssignList";
export { default as AssignModal } from "./AssignModal";

View File

@ -0,0 +1,64 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Form } from "antd";
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 { getLabelName } from "zy-react-library/utils";
import { ALARM_STATUS_OPTIONS } from "~/enumerate/constant";
import { NS_DCSALARMINFO } from "~/enumerate/namespace";
function DcsList(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["dcsAlarmInfoList"], { form });
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "alarmNo", label: "报警编号" },
{ name: "sensorCode", label: "传感器编码" },
{
name: "status",
label: "报警状态",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ALARM_STATUS_OPTIONS,
},
{
name: "alarmTimeStart",
label: "开始时间",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
{
name: "alarmTimeEnd",
label: "结束时间",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
]}
/>
<Table
rowKey="id"
columns={[
{ title: "报警编号", dataIndex: "alarmNo" },
{ title: "传感器编码", dataIndex: "sensorCode" },
{ title: "报警时间", dataIndex: "alarmTime" },
{ title: "报警等级", dataIndex: "alarmLevel" },
{ title: "报警描述", dataIndex: "alarmDesc", ellipsis: true },
{
title: "报警状态",
dataIndex: "status",
render: value => getLabelName({ status: value, list: ALARM_STATUS_OPTIONS }) || value || "-",
},
{ title: "设备来源", dataIndex: "deviceSourceDesc" },
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_DCSALARMINFO], true)(Permission(DcsList));

View File

@ -0,0 +1,64 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Form } from "antd";
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 { getLabelName } from "zy-react-library/utils";
import { ALARM_STATUS_OPTIONS } from "~/enumerate/constant";
import { NS_THRESHOLDALARMINFO } from "~/enumerate/namespace";
function ThresholdList(props) {
const [form] = Form.useForm();
const { tableProps, getData } = useTable(props["thresholdAlarmInfoList"], { form });
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "alarmNo", label: "报警编号" },
{ name: "sensorCode", label: "传感器编码" },
{
name: "status",
label: "报警状态",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ALARM_STATUS_OPTIONS,
},
{
name: "alarmTimeStart",
label: "开始时间",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
{
name: "alarmTimeEnd",
label: "结束时间",
render: FORM_ITEM_RENDER_ENUM.DATE,
},
]}
/>
<Table
rowKey="id"
columns={[
{ title: "报警编号", dataIndex: "alarmNo" },
{ title: "传感器编码", dataIndex: "sensorCode" },
{ title: "报警时间", dataIndex: "alarmTime" },
{ title: "报警等级", dataIndex: "alarmLevel" },
{ title: "报警描述", dataIndex: "alarmDesc", ellipsis: true },
{
title: "报警状态",
dataIndex: "status",
render: value => getLabelName({ status: value, list: ALARM_STATUS_OPTIONS }) || value || "-",
},
{ title: "设备来源", dataIndex: "deviceSourceDesc" },
]}
{...tableProps}
/>
</Page>
);
}
export default Connect([NS_THRESHOLDALARMINFO], true)(Permission(ThresholdList));

View File

@ -0,0 +1,2 @@
export { default as DcsList } from "./DcsList";
export { default as ThresholdList } from "./ThresholdList";

View File

@ -0,0 +1,55 @@
import { Descriptions, Modal } from "antd";
import { useEffect, useState } from "react";
import { getLabelName } from "zy-react-library/utils";
import { ALARM_SOURCE_OPTIONS, ALARM_STATUS_OPTIONS } from "~/enumerate/constant";
function DetailModal(props) {
const [detail, setDetail] = useState(null);
useEffect(() => {
if (!props.open) {
setDetail(null);
return;
}
props.alarmRecordInfo({ id: props.currentId }).then((res) => {
if (res?.success) {
setDetail(res.data || {});
}
});
}, [props.open, props.currentId, props.alarmRecordInfo]);
return (
<Modal
destroyOnClose
open={props.open}
title="报警详情"
onCancel={props.onCancel}
footer={null}
width={720}
>
{detail && (
<Descriptions column={2} bordered>
<Descriptions.Item label="报警编号">{detail.alarmNo || "-"}</Descriptions.Item>
<Descriptions.Item label="报警来源">
{getLabelName({ status: detail.alarmSource, list: ALARM_SOURCE_OPTIONS }) || detail.alarmSource || "-"}
</Descriptions.Item>
<Descriptions.Item label="传感器编码">{detail.sensorCode || "-"}</Descriptions.Item>
<Descriptions.Item label="传感器名称">{detail.sensorName || "-"}</Descriptions.Item>
<Descriptions.Item label="报警时间">{detail.alarmTime || "-"}</Descriptions.Item>
<Descriptions.Item label="报警级别">{detail.alarmLevel || "-"}</Descriptions.Item>
<Descriptions.Item label="状态">
{getLabelName({ status: detail.status, list: ALARM_STATUS_OPTIONS }) || detail.status || "-"}
</Descriptions.Item>
<Descriptions.Item label="处置人">{detail.disposeUserName || "-"}</Descriptions.Item>
<Descriptions.Item label="设备来源描述" span={2}>
{detail.deviceSourceDesc || "-"}
</Descriptions.Item>
<Descriptions.Item label="处置时间">{detail.disposeTime || "-"}</Descriptions.Item>
<Descriptions.Item label="处置备注">{detail.disposeRemark || "-"}</Descriptions.Item>
</Descriptions>
)}
</Modal>
);
}
export default DetailModal;

View File

@ -0,0 +1,105 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Space } from "antd";
import { useState } from "react";
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 { getLabelName } from "zy-react-library/utils";
import DetailModal from "~/components/AlarmRecord/DetailModal";
import { ALARM_SOURCE_OPTIONS, ALARM_STATUS_OPTIONS } from "~/enumerate/constant";
import { NS_ALARMRECORD } from "~/enumerate/namespace";
function AlarmRecordList(props) {
const [form] = Form.useForm();
const [detailOpen, setDetailOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const { tableProps, getData } = useTable(props["alarmRecordList"], { form });
const handleViewDetail = (record) => {
setCurrentId(record.id);
setDetailOpen(true);
};
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "alarmNo", label: "报警编号" },
{ name: "sensorName", label: "传感器名称" },
{
name: "alarmSource",
label: "报警来源",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ALARM_SOURCE_OPTIONS,
},
{
name: "status",
label: "状态",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ALARM_STATUS_OPTIONS,
},
{
name: "alarmTime",
label: "报警时间",
render: FORM_ITEM_RENDER_ENUM.DATETIME_RANGE,
},
]}
/>
<Table
rowKey="id"
columns={[
{ title: "报警编号", dataIndex: "alarmNo" },
{
title: "报警来源",
dataIndex: "alarmSource",
render: value => getLabelName({ status: value, list: ALARM_SOURCE_OPTIONS }) || value || "-",
},
{ title: "传感器编码", dataIndex: "sensorCode" },
{ title: "传感器名称", dataIndex: "sensorName" },
{ title: "报警时间", dataIndex: "alarmTime" },
{ title: "报警级别", dataIndex: "alarmLevel" },
{
title: "状态",
dataIndex: "status",
render: value => getLabelName({ status: value, list: ALARM_STATUS_OPTIONS }) || value || "-",
},
{ title: "处置人", dataIndex: "disposeUserName" },
{ title: "设备来源描述", dataIndex: "deviceSourceDesc", ellipsis: true },
{
title: "操作",
width: 100,
render: (_, record) => (
<Space>
<Button
type="link"
onClick={() => handleViewDetail(record)}
>
详情
</Button>
</Space>
),
},
]}
{...tableProps}
/>
{detailOpen && (
<DetailModal
open={detailOpen}
currentId={currentId}
alarmRecordInfo={props.alarmRecordInfo}
onCancel={() => {
setDetailOpen(false);
setCurrentId("");
}}
/>
)}
</Page>
);
}
export default Connect([NS_ALARMRECORD], true)(Permission(AlarmRecordList));

View File

@ -0,0 +1,3 @@
import AlarmRecordList from "~/components/AlarmRecord/List";
export default AlarmRecordList;

View File

@ -0,0 +1,117 @@
import { Checkbox, Empty, Modal, Typography, message } from "antd";
import { useEffect, useMemo, useState } from "react";
function BindSensorModal(props) {
const [loading, setLoading] = useState(false);
const [detail, setDetail] = useState({});
const [options, setOptions] = useState([]);
const [initialSensorIds, setInitialSensorIds] = useState([]);
const [selectedSensorIds, setSelectedSensorIds] = useState([]);
useEffect(() => {
if (!props.open || !props.currentId) {
return;
}
const loadData = async () => {
setLoading(true);
try {
const [detailRes, deviceRes] = await Promise.all([
props.deviceRegionInfo({ id: props.currentId }),
props.sensorDeviceList({ pageIndex: 1, pageSize: 500 }),
]);
const regionDetail = detailRes?.data || {};
const deviceList = deviceRes?.data || [];
const boundIds = (regionDetail.boundSensors || []).map(item => item.id);
setDetail(regionDetail);
setInitialSensorIds(boundIds);
setSelectedSensorIds(boundIds);
setOptions(
deviceList.map(item => ({
value: item.id,
label: `${item.sensorCode} - ${item.sensorName}${item.sensorTypeName ? `${item.sensorTypeName}` : ""}`,
})),
);
}
finally {
setLoading(false);
}
};
loadData();
}, [props.currentId, props.deviceRegionInfo, props.open, props.sensorDeviceList]);
const changes = useMemo(() => {
const initialSet = new Set(initialSensorIds);
const selectedSet = new Set(selectedSensorIds);
return {
addIds: selectedSensorIds.filter(id => !initialSet.has(id)),
removeIds: initialSensorIds.filter(id => !selectedSet.has(id)),
};
}, [initialSensorIds, selectedSensorIds]);
const handleSubmit = async () => {
setLoading(true);
try {
if (changes.addIds.length) {
await props.deviceRegionBindSensor({
regionConfigId: props.currentId,
sensorIds: changes.addIds,
});
}
if (changes.removeIds.length) {
await props.deviceRegionUnbindSensor({
regionConfigId: props.currentId,
sensorIds: changes.removeIds,
});
}
message.success("传感器绑定已更新");
props.onCancel();
props.getData();
}
finally {
setLoading(false);
}
};
return (
<Modal
destroyOnClose
open={props.open}
title="绑定传感器"
onCancel={props.onCancel}
onOk={handleSubmit}
confirmLoading={loading || props.loading}
width={760}
>
<Typography.Paragraph style={{ marginBottom: 16 }}>
当前区域{detail.fireRegionName || "-"} / {detail.fireRegionCode || "-"}
</Typography.Paragraph>
{options.length ? (
<Checkbox.Group
value={selectedSensorIds}
onChange={setSelectedSensorIds}
style={{ width: "100%" }}
>
<div
style={{
maxHeight: 420,
overflowY: "auto",
border: "1px solid #f0f0f0",
borderRadius: 6,
padding: 12,
}}
>
{options.map(item => (
<div key={item.value} style={{ padding: "6px 0" }}>
<Checkbox value={item.value}>{item.label}</Checkbox>
</div>
))}
</div>
</Checkbox.Group>
) : (
<Empty description="暂无可绑定的传感器设备" />
)}
</Modal>
);
}
export default BindSensorModal;

View File

@ -0,0 +1,244 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, Input, Modal, Select, Space, message } from "antd";
import { useEffect, useState } from "react";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
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 { getLabelName } from "zy-react-library/utils";
import BindSensorModal from "~/components/DeviceRegion/BindSensorModal";
import ManagerModal from "~/components/DeviceRegion/ManagerModal";
import { ENABLE_STATUS_OPTIONS } from "~/enumerate/constant";
import { NS_DEVICEREGION, NS_SENSORDEVICE } from "~/enumerate/namespace";
function DeviceRegionList(props) {
const [form] = Form.useForm();
const [configForm] = Form.useForm();
const [configOpen, setConfigOpen] = useState(false);
const [managerOpen, setManagerOpen] = useState(false);
const [bindOpen, setBindOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const [fireRegionOptions, setFireRegionOptions] = useState([]);
const { tableProps, getData } = useTable(props["deviceRegionList"], { form });
const editPermission = `${props.type}-iotalarm-device-region-edit`;
const loadFireRegionOptions = async () => {
const res = await props["deviceRegionFireRegionList"]();
if (res?.success) {
setFireRegionOptions(res.data || []);
}
};
useEffect(() => {
loadFireRegionOptions();
}, [props.deviceRegionFireRegionList]);
useEffect(() => {
if (!configOpen) {
return;
}
if (!currentId) {
configForm.resetFields();
configForm.setFieldsValue({ status: 1 });
return;
}
props["deviceRegionInfo"]({ id: currentId }).then((res) => {
if (res?.success) {
configForm.setFieldsValue({
fireRegionId: res.data?.fireRegionId,
status: res.data?.status,
remarks: res.data?.remarks,
});
}
});
}, [configForm, configOpen, currentId, props.deviceRegionInfo]);
const handleConfigSubmit = async (values) => {
const selectedFireRegion = fireRegionOptions.find(item => item.id === values.fireRegionId);
await props["deviceRegionSaveOrUpdate"]({
...values,
id: currentId || undefined,
fireRegionCode: selectedFireRegion?.fireRegionCode,
});
message.success(currentId ? "设备区域配置已更新" : "设备区域配置已新增");
setConfigOpen(false);
setCurrentId("");
configForm.resetFields();
getData();
};
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "fireRegionName", label: "消防区域名称" },
{ name: "fireRegionCode", label: "消防区域编码" },
{
name: "status",
label: "状态",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ENABLE_STATUS_OPTIONS,
},
]}
/>
<Table
rowKey="id"
toolBarRender={() => (
<Space>
{props.permission(editPermission) && (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setCurrentId("");
setConfigOpen(true);
}}
>
新增
</Button>
)}
</Space>
)}
columns={[
{ title: "消防区域名称", dataIndex: "fireRegionName" },
{ title: "消防区域编码", dataIndex: "fireRegionCode" },
{ title: "负责部门", dataIndex: "deptName" },
{ title: "负责人", dataIndex: "managerName" },
{ title: "已绑定传感器", dataIndex: "bindSensorCount" },
{
title: "状态",
dataIndex: "status",
render: value => getLabelName({ status: value, list: ENABLE_STATUS_OPTIONS }) || value || "-",
},
{ title: "备注", dataIndex: "remarks", ellipsis: true },
{
title: "操作",
width: 280,
render: (_, record) => (
<Space>
{props.permission(editPermission) && (
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setConfigOpen(true);
}}
>
编辑配置
</Button>
)}
{props.permission(editPermission) && (
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setManagerOpen(true);
}}
>
负责人
</Button>
)}
{props.permission(editPermission) && (
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setBindOpen(true);
}}
>
绑定传感器
</Button>
)}
</Space>
),
},
]}
{...tableProps}
/>
<Modal
destroyOnClose
open={configOpen}
title={currentId ? "编辑设备区域配置" : "新增设备区域配置"}
onCancel={() => {
setConfigOpen(false);
setCurrentId("");
configForm.resetFields();
}}
onOk={configForm.submit}
confirmLoading={props.deviceRegionLoading}
width={680}
>
<Form form={configForm} layout="vertical" onFinish={handleConfigSubmit} initialValues={{ status: 1 }}>
<Form.Item
label="消防区域"
name="fireRegionId"
rules={[{ required: true, message: "请选择消防区域" }]}
>
<Select
allowClear
showSearch
optionFilterProp="label"
disabled={!!currentId}
placeholder="请选择消防区域"
options={fireRegionOptions.map(item => ({
value: item.id,
label: `${item.fireRegionName}${item.fireRegionCode ? `${item.fireRegionCode}` : ""}`,
}))}
/>
</Form.Item>
<Form.Item label="状态" name="status">
<Select
allowClear
placeholder="请选择状态"
options={ENABLE_STATUS_OPTIONS.map(item => ({
value: item.bianma,
label: item.name,
}))}
/>
</Form.Item>
<Form.Item label="备注" name="remarks">
<Input.TextArea rows={4} maxLength={255} placeholder="请输入备注" />
</Form.Item>
</Form>
</Modal>
{managerOpen && (
<ManagerModal
open={managerOpen}
currentId={currentId}
loading={props.deviceRegionLoading}
getData={getData}
deviceRegionInfo={props.deviceRegionInfo}
deviceRegionManager={props.deviceRegionManager}
onCancel={() => {
setManagerOpen(false);
setCurrentId("");
}}
/>
)}
{bindOpen && (
<BindSensorModal
open={bindOpen}
currentId={currentId}
loading={props.deviceRegionLoading || props.sensorDeviceLoading}
getData={getData}
deviceRegionInfo={props.deviceRegionInfo}
deviceRegionBindSensor={props.deviceRegionBindSensor}
deviceRegionUnbindSensor={props.deviceRegionUnbindSensor}
sensorDeviceList={props.sensorDeviceList}
onCancel={() => {
setBindOpen(false);
setCurrentId("");
}}
/>
)}
</Page>
);
}
export default Connect([NS_DEVICEREGION, NS_SENSORDEVICE], true)(Permission(DeviceRegionList));

View File

@ -0,0 +1,66 @@
import { Form, Modal } from "antd";
import { useEffect, useState } from "react";
import PersonnelSelect from "zy-react-library/components/Select/Personnel/Gwj";
import DepartmentSelectTree from "zy-react-library/components/SelectTree/Department/Gwj";
function ManagerModal(props) {
const [form] = Form.useForm();
const [departmentId, setDepartmentId] = useState();
useEffect(() => {
if (!props.open || !props.currentId) {
return;
}
props.deviceRegionInfo({ id: props.currentId }).then((res) => {
if (!res?.success) {
return;
}
const data = res.data || {};
setDepartmentId(data.deptId);
form.setFieldsValue({
deptId: data.deptId,
managerId: data.managerId,
});
});
}, [form, props.currentId, props.deviceRegionInfo, props.open]);
const handleCancel = () => {
form.resetFields();
setDepartmentId(undefined);
props.onCancel();
};
const handleSubmit = async (values) => {
await props.deviceRegionManager({ ...values, id: props.currentId });
handleCancel();
props.getData();
};
return (
<Modal
destroyOnClose
open={props.open}
title="负责人配置"
onCancel={handleCancel}
onOk={form.submit}
confirmLoading={props.loading}
width={640}
>
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Form.Item label="负责部门" name="deptId">
<DepartmentSelectTree
onChange={(value) => {
setDepartmentId(value);
form.setFieldValue("managerId", undefined);
}}
/>
</Form.Item>
<Form.Item label="负责人" name="managerId">
<PersonnelSelect params={{ departmentId }} />
</Form.Item>
</Form>
</Modal>
);
}
export default ManagerModal;

View File

@ -0,0 +1,6 @@
export { default as List } from "./List";
export { default as ManagerModal } from "./ManagerModal";
export { default as BindSensorModal } from "./BindSensorModal";
// 默认导出 List 作为主入口组件
export { default } from "./List";

View File

@ -0,0 +1,238 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space, Switch } from "antd";
import { useEffect, useState } from "react";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
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 { getLabelName } from "zy-react-library/utils";
import SensorDeviceModal from "~/components/SensorDevice/Modal";
import RealtimeModal from "~/components/SensorDevice/RealtimeModal";
import ThresholdModal from "~/components/SensorDevice/ThresholdModal";
import { ENABLE_STATUS_OPTIONS, SENSOR_STATUS_OPTIONS, YES_NO_OPTIONS } from "~/enumerate/constant";
import { NS_SENSORDEVICE, NS_SENSORTYPE } from "~/enumerate/namespace";
function SensorDeviceList(props) {
const [form] = Form.useForm();
const [modalOpen, setModalOpen] = useState(false);
const [thresholdOpen, setThresholdOpen] = useState(false);
const [realtimeOpen, setRealtimeOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const [currentRecord, setCurrentRecord] = useState({});
const [sensorTypeOptions, setSensorTypeOptions] = useState([]);
const { tableProps, getData } = useTable(props["sensorDeviceList"], { form });
const editPermission = `${props.type}-iotalarm-sensor-device-edit`;
const loadSensorTypeOptions = async () => {
const res = await props["sensorTypeList"]({
pageIndex: 1,
pageSize: 500,
status: 1,
});
if (res?.success) {
setSensorTypeOptions(res.data || []);
}
};
useEffect(() => {
loadSensorTypeOptions();
}, [props.sensorTypeList]);
const sensorTypeSelectOptions = sensorTypeOptions.map(item => ({
bianma: item.id,
name: item.typeName,
}));
const handleDelete = (record) => {
Modal.confirm({
title: "确定删除这条传感器设备吗?",
onOk: async () => {
await props["sensorDeviceRemove"]({ id: record.id });
message.success("删除成功");
getData();
},
});
};
const handleAlarmSwitch = async (record, checked) => {
await props["sensorDeviceStatus"]({ id: record.id, alarmSwitch: checked ? 1 : 0 });
message.success("报警开关更新成功");
getData();
};
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "sensorCode", label: "传感器编码" },
{ name: "sensorName", label: "传感器名称" },
{
name: "sensorTypeId",
label: "传感器类型",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: sensorTypeSelectOptions,
},
{
name: "alarmSwitch",
label: "报警开关",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ENABLE_STATUS_OPTIONS,
},
{
name: "thresholdFlag",
label: "阈值配置",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: YES_NO_OPTIONS,
},
]}
/>
<Table
rowKey="id"
toolBarRender={() => (
<Space>
{props.permission(editPermission) && (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setCurrentId("");
setModalOpen(true);
}}
>
新增
</Button>
)}
</Space>
)}
columns={[
{ title: "传感器编码", dataIndex: "sensorCode" },
{ title: "传感器名称", dataIndex: "sensorName" },
{ title: "传感器类型", dataIndex: "sensorTypeName" },
{
title: "传感器状态",
dataIndex: "sensorStatus",
render: value => getLabelName({ status: value, list: SENSOR_STATUS_OPTIONS }) || value || "-",
},
{
title: "报警开关",
dataIndex: "alarmSwitch",
render: (_, record) => (
<Switch
checked={record.alarmSwitch === 1}
checkedChildren="开"
unCheckedChildren="关"
disabled={!props.permission(editPermission)}
onChange={checked => handleAlarmSwitch(record, checked)}
/>
),
},
{
title: "阈值配置",
dataIndex: "thresholdFlag",
render: value => getLabelName({ status: value, list: YES_NO_OPTIONS }) || value || "-",
},
{ title: "安装位置", dataIndex: "installPosition", ellipsis: true },
{ title: "单位", dataIndex: "unitName" },
{
title: "量程",
render: (_, record) => `${record.rangeMin ?? "-"} ~ ${record.rangeMax ?? "-"}`,
},
{ title: "备注", dataIndex: "remarks", ellipsis: true },
{
title: "操作",
width: 260,
render: (_, record) => (
<Space>
{props.permission(editPermission) && record.sensorAttr === "NUMBER" && (
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setThresholdOpen(true);
}}
>
阈值配置
</Button>
)}
<Button
type="link"
onClick={() => {
setCurrentRecord(record);
setRealtimeOpen(true);
}}
>
实时概览
</Button>
{props.permission(editPermission) && (
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setModalOpen(true);
}}
>
编辑
</Button>
)}
{props.permission(editPermission) && (
<Button danger type="link" onClick={() => handleDelete(record)}>
删除
</Button>
)}
</Space>
),
},
]}
{...tableProps}
/>
{modalOpen && (
<SensorDeviceModal
open={modalOpen}
currentId={currentId}
loading={props.sensorDeviceLoading}
getData={getData}
sensorTypeOptions={sensorTypeOptions}
sensorDeviceInfo={props.sensorDeviceInfo}
sensorDeviceSave={props.sensorDeviceSave}
sensorDeviceEdit={props.sensorDeviceEdit}
onCancel={() => {
setModalOpen(false);
setCurrentId("");
}}
/>
)}
{thresholdOpen && (
<ThresholdModal
open={thresholdOpen}
currentId={currentId}
loading={props.sensorDeviceLoading}
getData={getData}
sensorDeviceInfo={props.sensorDeviceInfo}
sensorDeviceThreshold={props.sensorDeviceThreshold}
onCancel={() => {
setThresholdOpen(false);
setCurrentId("");
}}
/>
)}
{realtimeOpen && (
<RealtimeModal
open={realtimeOpen}
record={currentRecord}
onCancel={() => {
setRealtimeOpen(false);
setCurrentRecord({});
}}
/>
)}
</Page>
);
}
export default Connect([NS_SENSORDEVICE, NS_SENSORTYPE], true)(Permission(SensorDeviceList));

View File

@ -0,0 +1,157 @@
import { DatePicker, Form, Input, InputNumber, Modal, Radio, Select } from "antd";
import dayjs from "dayjs";
import { useEffect } from "react";
import { ENABLE_STATUS_OPTIONS, SENSOR_STATUS_OPTIONS, YES_NO_OPTIONS } from "~/enumerate/constant";
function SensorDeviceModal(props) {
const [form] = Form.useForm();
useEffect(() => {
if (!props.open) {
return;
}
if (!props.currentId) {
form.resetFields();
form.setFieldsValue({
sensorStatus: "NORMAL",
positioningFlag: 0,
alarmSwitch: 1,
});
return;
}
props.sensorDeviceInfo({ id: props.currentId }).then((res) => {
if (!res?.success) {
return;
}
const data = { ...(res.data || {}) };
if (data.factoryDate) {
data.factoryDate = dayjs(data.factoryDate);
}
form.setFieldsValue(data);
});
}, [form, props.currentId, props.open, props.sensorDeviceInfo]);
const handleCancel = () => {
form.resetFields();
props.onCancel();
};
const handleSubmit = async (values) => {
const payload = {
...values,
factoryDate: values.factoryDate ? values.factoryDate.format("YYYY-MM-DD") : undefined,
};
if (props.currentId) {
await props.sensorDeviceEdit({ ...payload, id: props.currentId });
}
else {
await props.sensorDeviceSave(payload);
}
handleCancel();
props.getData();
};
return (
<Modal
destroyOnClose
open={props.open}
title={props.currentId ? "编辑传感器设备" : "新增传感器设备"}
onCancel={handleCancel}
onOk={form.submit}
confirmLoading={props.loading}
width={720}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
initialValues={{
sensorStatus: "NORMAL",
positioningFlag: 0,
alarmSwitch: 1,
}}
>
<Form.Item
label="传感器编码"
name="sensorCode"
rules={[{ required: true, message: "请输入传感器编码" }]}
>
<Input maxLength={100} placeholder="请输入传感器编码" />
</Form.Item>
<Form.Item
label="传感器名称"
name="sensorName"
rules={[{ required: true, message: "请输入传感器名称" }]}
>
<Input maxLength={200} placeholder="请输入传感器名称" />
</Form.Item>
<Form.Item
label="传感器类型"
name="sensorTypeId"
rules={[{ required: true, message: "请选择传感器类型" }]}
>
<Select
allowClear
showSearch
optionFilterProp="label"
placeholder="请选择传感器类型"
options={props.sensorTypeOptions.map(item => ({
value: item.id,
label: `${item.typeName}${item.typeCode ? `${item.typeCode}` : ""}`,
}))}
/>
</Form.Item>
<Form.Item label="安装位置" name="installPosition">
<Input maxLength={255} placeholder="请输入安装位置" />
</Form.Item>
<Form.Item label="传感器状态" name="sensorStatus">
<Radio.Group>
{SENSOR_STATUS_OPTIONS.map(item => (
<Radio key={item.bianma} value={item.bianma}>
{item.name}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item label="是否定位" name="positioningFlag">
<Radio.Group>
{YES_NO_OPTIONS.map(item => (
<Radio key={item.bianma} value={item.bianma}>
{item.name}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item label="报警开关" name="alarmSwitch">
<Radio.Group>
{ENABLE_STATUS_OPTIONS.map(item => (
<Radio key={item.bianma} value={item.bianma}>
{item.name}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item label="单位" name="unitName">
<Input maxLength={50} placeholder="请输入单位" />
</Form.Item>
<Form.Item label="量程下限" name="rangeMin">
<InputNumber style={{ width: "100%" }} placeholder="请输入量程下限" />
</Form.Item>
<Form.Item label="量程上限" name="rangeMax">
<InputNumber style={{ width: "100%" }} placeholder="请输入量程上限" />
</Form.Item>
<Form.Item label="出厂日期" name="factoryDate">
<DatePicker style={{ width: "100%" }} placeholder="请选择出厂日期" />
</Form.Item>
<Form.Item label="企业侧源编码" name="enterpriseSourceCode">
<Input maxLength={100} placeholder="请输入企业侧源编码" />
</Form.Item>
<Form.Item label="备注" name="remarks">
<Input.TextArea rows={4} maxLength={255} placeholder="请输入备注" />
</Form.Item>
</Form>
</Modal>
);
}
export default SensorDeviceModal;

View File

@ -0,0 +1,50 @@
import { Descriptions, Modal } from "antd";
import { getLabelName } from "zy-react-library/utils";
import { ENABLE_STATUS_OPTIONS, SENSOR_STATUS_OPTIONS, YES_NO_OPTIONS } from "~/enumerate/constant";
function RealtimeModal(props) {
const record = props.record || {};
return (
<Modal
open={props.open}
title="实时概览"
onCancel={props.onCancel}
footer={null}
width={720}
>
<Descriptions
bordered
column={2}
items={[
{ label: "传感器编码", children: record.sensorCode || "-" },
{ label: "传感器名称", children: record.sensorName || "-" },
{ label: "传感器类型", children: record.sensorTypeName || "-" },
{
label: "传感器状态",
children: getLabelName({ status: record.sensorStatus, list: SENSOR_STATUS_OPTIONS }) || record.sensorStatus || "-",
},
{
label: "报警开关",
children: getLabelName({ status: record.alarmSwitch, list: ENABLE_STATUS_OPTIONS }) || record.alarmSwitch || "-",
},
{
label: "是否定位",
children: getLabelName({ status: record.positioningFlag, list: YES_NO_OPTIONS }) || record.positioningFlag || "-",
},
{ label: "当前量程", children: `${record.rangeMin ?? "-"} ~ ${record.rangeMax ?? "-"}` },
{ label: "单位", children: record.unitName || "-" },
{ label: "安装位置", children: record.installPosition || "-" },
{ label: "低低报", children: record.thresholdLowLow ?? "-" },
{ label: "低报", children: record.thresholdLow ?? "-" },
{ label: "高报", children: record.thresholdHigh ?? "-" },
{ label: "高高报", children: record.thresholdHighHigh ?? "-" },
{ label: "企业侧源编码", children: record.enterpriseSourceCode || "-" },
{ label: "备注", children: record.remarks || "-" },
]}
/>
</Modal>
);
}
export default RealtimeModal;

View File

@ -0,0 +1,74 @@
import { Alert, Form, InputNumber, Modal } from "antd";
import { useEffect, useState } from "react";
function ThresholdModal(props) {
const [form] = Form.useForm();
const [sensorAttr, setSensorAttr] = useState("");
useEffect(() => {
if (!props.open || !props.currentId) {
return;
}
props.sensorDeviceInfo({ id: props.currentId }).then((res) => {
if (res?.success) {
form.setFieldsValue(res.data || {});
setSensorAttr(res.data?.sensorAttr || "");
}
});
}, [form, props.currentId, props.open, props.sensorDeviceInfo]);
const handleCancel = () => {
form.resetFields();
setSensorAttr("");
props.onCancel();
};
const handleSubmit = async (values) => {
await props.sensorDeviceThreshold({ ...values, id: props.currentId });
handleCancel();
props.getData();
};
const isNumberType = sensorAttr === "NUMBER";
return (
<Modal
destroyOnClose
open={props.open}
title="阈值配置"
onCancel={handleCancel}
onOk={isNumberType ? form.submit : undefined}
confirmLoading={props.loading}
width={560}
footer={isNumberType ? undefined : null}
>
{!isNumberType && sensorAttr && (
<Alert
type="warning"
message="仅数值型传感器支持阈值配置"
description="当前传感器属性为开关量,无需配置四级阈值。"
showIcon
style={{ marginBottom: 16 }}
/>
)}
{isNumberType && (
<Form form={form} layout="vertical" onFinish={handleSubmit}>
<Form.Item label="低低报阈值" name="thresholdLowLow">
<InputNumber style={{ width: "100%" }} placeholder="请输入低低报阈值" />
</Form.Item>
<Form.Item label="低报阈值" name="thresholdLow">
<InputNumber style={{ width: "100%" }} placeholder="请输入低报阈值" />
</Form.Item>
<Form.Item label="高报阈值" name="thresholdHigh">
<InputNumber style={{ width: "100%" }} placeholder="请输入高报阈值" />
</Form.Item>
<Form.Item label="高高报阈值" name="thresholdHighHigh">
<InputNumber style={{ width: "100%" }} placeholder="请输入高高报阈值" />
</Form.Item>
</Form>
)}
</Modal>
);
}
export default ThresholdModal;

View File

@ -0,0 +1,8 @@
// 导出子组件
export { default as List } from "./List";
export { default as Modal } from "./Modal";
export { default as ThresholdModal } from "./ThresholdModal";
export { default as RealtimeModal } from "./RealtimeModal";
// 默认导出 List 作为主入口
export { default } from "./List";

View File

@ -0,0 +1,136 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import { Connect } from "@cqsjjb/jjb-dva-runtime";
import { Button, Form, message, Modal, Space } from "antd";
import { useState } from "react";
import AddIcon from "zy-react-library/components/Icon/AddIcon";
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 { getLabelName } from "zy-react-library/utils";
import SensorTypeModal from "~/components/SensorType/Modal";
import { ENABLE_STATUS_OPTIONS, SENSOR_ATTR_OPTIONS } from "~/enumerate/constant";
import { NS_SENSORTYPE } from "~/enumerate/namespace";
function SensorTypeList(props) {
const [form] = Form.useForm();
const [modalOpen, setModalOpen] = useState(false);
const [currentId, setCurrentId] = useState("");
const { tableProps, getData } = useTable(props["sensorTypeList"], { form });
const editPermission = `${props.type}-iotalarm-sensor-type-edit`;
const handleDelete = (record) => {
Modal.confirm({
title: "确定删除这条传感器类型吗?",
onOk: async () => {
await props["sensorTypeRemove"]({ id: record.id });
message.success("删除成功");
getData();
},
});
};
return (
<Page isShowAllAction={false}>
<Search
form={form}
onFinish={getData}
options={[
{ name: "typeCode", label: "类型编码" },
{ name: "typeName", label: "类型名称" },
{
name: "sensorAttr",
label: "传感器属性",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: SENSOR_ATTR_OPTIONS,
},
{
name: "status",
label: "状态",
render: FORM_ITEM_RENDER_ENUM.SELECT,
items: ENABLE_STATUS_OPTIONS,
},
]}
/>
<Table
rowKey="id"
toolBarRender={() => (
<Space>
{props.permission(editPermission) && (
<Button
type="primary"
icon={<AddIcon />}
onClick={() => {
setCurrentId("");
setModalOpen(true);
}}
>
新增
</Button>
)}
</Space>
)}
columns={[
{ title: "类型编码", dataIndex: "typeCode" },
{ title: "类型名称", dataIndex: "typeName" },
{
title: "传感器属性",
dataIndex: "sensorAttr",
render: value => getLabelName({ status: value, list: SENSOR_ATTR_OPTIONS }) || value || "-",
},
{ title: "排序", dataIndex: "sortNo" },
{
title: "状态",
dataIndex: "status",
render: value => getLabelName({ status: value, list: ENABLE_STATUS_OPTIONS }) || value || "-",
},
{ title: "备注", dataIndex: "remarks", ellipsis: true },
{
title: "操作",
width: 180,
render: (_, record) => (
<Space>
{props.permission(editPermission) && (
<Button
type="link"
onClick={() => {
setCurrentId(record.id);
setModalOpen(true);
}}
>
编辑
</Button>
)}
{props.permission(editPermission) && (
<Button danger type="link" onClick={() => handleDelete(record)}>
删除
</Button>
)}
</Space>
),
},
]}
{...tableProps}
/>
{modalOpen && (
<SensorTypeModal
open={modalOpen}
currentId={currentId}
loading={props.sensorTypeLoading}
getData={getData}
sensorTypeInfo={props.sensorTypeInfo}
sensorTypeSave={props.sensorTypeSave}
sensorTypeEdit={props.sensorTypeEdit}
onCancel={() => {
setModalOpen(false);
setCurrentId("");
}}
/>
)}
</Page>
);
}
export default Connect([NS_SENSORTYPE], true)(Permission(SensorTypeList));

View File

@ -0,0 +1,115 @@
import { Form, Input, InputNumber, Modal, Radio } from "antd";
import { useEffect } from "react";
import { ENABLE_STATUS_OPTIONS, SENSOR_ATTR_OPTIONS } from "~/enumerate/constant";
function SensorTypeModal(props) {
const [form] = Form.useForm();
useEffect(() => {
if (!props.open) {
return;
}
if (!props.currentId) {
form.resetFields();
form.setFieldsValue({
sensorAttr: "NUMBER",
status: 1,
sortNo: 0,
});
return;
}
props.sensorTypeInfo({ id: props.currentId }).then((res) => {
if (res?.success) {
form.setFieldsValue(res.data || {});
}
});
}, [form, props.currentId, props.open, props.sensorTypeInfo]);
const handleCancel = () => {
form.resetFields();
props.onCancel();
};
const handleSubmit = async (values) => {
if (props.currentId) {
await props.sensorTypeEdit({ ...values, id: props.currentId });
}
else {
await props.sensorTypeSave(values);
}
handleCancel();
props.getData();
};
return (
<Modal
destroyOnClose
open={props.open}
title={props.currentId ? "编辑传感器类型" : "新增传感器类型"}
onCancel={handleCancel}
onOk={form.submit}
confirmLoading={props.loading}
width={640}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
initialValues={{
sensorAttr: "NUMBER",
status: 1,
sortNo: 0,
}}
>
<Form.Item
label="类型编码"
name="typeCode"
rules={[{ required: true, message: "请输入类型编码" }]}
>
<Input maxLength={64} placeholder="请输入类型编码" />
</Form.Item>
<Form.Item
label="类型名称"
name="typeName"
rules={[{ required: true, message: "请输入类型名称" }]}
>
<Input maxLength={100} placeholder="请输入类型名称" />
</Form.Item>
<Form.Item
label="传感器属性"
name="sensorAttr"
rules={[{ required: true, message: "请选择传感器属性" }]}
>
<Radio.Group>
{SENSOR_ATTR_OPTIONS.map(item => (
<Radio key={item.bianma} value={item.bianma}>
{item.name}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item label="排序" name="sortNo">
<InputNumber min={0} precision={0} style={{ width: "100%" }} placeholder="请输入排序" />
</Form.Item>
<Form.Item
label="状态"
name="status"
rules={[{ required: true, message: "请选择状态" }]}
>
<Radio.Group>
{ENABLE_STATUS_OPTIONS.map(item => (
<Radio key={item.bianma} value={item.bianma}>
{item.name}
</Radio>
))}
</Radio.Group>
</Form.Item>
<Form.Item label="备注" name="remarks">
<Input.TextArea rows={4} maxLength={255} placeholder="请输入备注" />
</Form.Item>
</Form>
</Modal>
);
}
export default SensorTypeModal;

View File

@ -0,0 +1,5 @@
export { default as List } from "./List";
export { default as Modal } from "./Modal";
// 默认导出 List 作为主入口组件
export { default } from "./List";

View File

@ -1 +1,6 @@
export {};
export { default as SensorType } from "./SensorType";
export { default as SensorDevice } from "./SensorDevice";
export { default as DeviceRegion } from "./DeviceRegion";
export { default as AlarmInfo } from "./AlarmInfo";
export { default as AlarmDispose } from "./AlarmDispose";
export { default as AlarmRecord } from "./AlarmRecord";

View File

@ -1,5 +1,46 @@
/**
* 全局常量定义
*/
export const APP_IDENTIFIER = "iotalarm";
export {};
export const SENSOR_ATTR_OPTIONS = [
{ bianma: "NUMBER", name: "数值型" },
{ bianma: "SWITCH", name: "开关量" },
];
export const ENABLE_STATUS_OPTIONS = [
{ bianma: 1, name: "启用" },
{ bianma: 0, name: "停用" },
];
export const YES_NO_OPTIONS = [
{ bianma: 1, name: "是" },
{ bianma: 0, name: "否" },
];
export const SENSOR_STATUS_OPTIONS = [
{ bianma: "NORMAL", name: "正常" },
{ bianma: "FAULT", name: "故障" },
{ bianma: "OFFLINE", name: "离线" },
];
export const ALARM_STATUS_OPTIONS = [
{ bianma: 10, name: "报警中-待研判" },
{ bianma: 20, name: "报警中-待处置" },
{ bianma: 30, name: "已消警" },
{ bianma: 40, name: "误报" },
];
export const ALARM_SOURCE_OPTIONS = [
{ bianma: "DCS", name: "DCS" },
{ bianma: "THRESHOLD", name: "阈值报警" },
];
export const ALARM_LEVEL_OPTIONS = [
{ bianma: "HIGH", name: "高" },
{ bianma: "MEDIUM", name: "中" },
{ bianma: "LOW", name: "低" },
];
export const ALARM_TYPE_OPTIONS = [
{ bianma: "FAULT", name: "故障报警" },
{ bianma: "THRESHOLD", name: "阈值报警" },
{ bianma: "MANUAL", name: "人工报警" },
];

View File

@ -1,8 +1,3 @@
/**
* 全局上下文定义
*/
import React from "react";
// 获取antd全局静态方法
export const InjectContext = React.createContext({});
export const InjectContext = React.createContext(null);

3
src/enumerate/index.js Normal file
View File

@ -0,0 +1,3 @@
export * from "./context";
export * from "./constant";
export * from "./namespace";

View File

@ -1,7 +1,7 @@
/**
* 全局数据状态管理模块定义
*/
import { defineNamespace } from "@cqsjjb/jjb-dva-runtime";
export const NS_GLOBAL = defineNamespace("global");
export const NS_SENSORTYPE = "sensorType";
export const NS_SENSORDEVICE = "sensorDevice";
export const NS_DEVICEREGION = "deviceRegion";
export const NS_DCSALARMINFO = "dcsAlarmInfo";
export const NS_THRESHOLDALARMINFO = "thresholdAlarmInfo";
export const NS_ALARMDISPOSE = "alarmDispose";
export const NS_ALARMRECORD = "alarmRecord";

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import AssignList from "~/components/AlarmDispose/AssignList";
function AlarmAssignPage(props) {
return <AssignList {...props} type="fgs" />;
}
export default Permission(AlarmAssignPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import AlarmRecordList from "~/components/AlarmRecord/List";
function AlarmRecordPage(props) {
return <AlarmRecordList {...props} type="fgs" />;
}
export default Permission(AlarmRecordPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import DcsList from "~/components/AlarmInfo/DcsList";
function DcsAlarmInfoPage(props) {
return <DcsList {...props} type="fgs" />;
}
export default Permission(DcsAlarmInfoPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import DeviceRegionList from "~/components/DeviceRegion/List";
function DeviceRegionPage(props) {
return <DeviceRegionList {...props} type="fgs" />;
}
export default Permission(DeviceRegionPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import SensorDeviceList from "~/components/SensorDevice/List";
function SensorDevicePage(props) {
return <SensorDeviceList {...props} type="fgs" />;
}
export default Permission(SensorDevicePage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import SensorTypeList from "~/components/SensorType/List";
function SensorTypePage(props) {
return <SensorTypeList {...props} type="fgs" />;
}
export default Permission(SensorTypePage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import ThresholdList from "~/components/AlarmInfo/ThresholdList";
function ThresholdAlarmInfoPage(props) {
return <ThresholdList {...props} type="fgs" />;
}
export default Permission(ThresholdAlarmInfoPage);

View File

@ -0,0 +1,7 @@
export default function BranchCompany(props) {
if (props.children) {
return props.children;
}
return <div style={{ padding: 24, border: "1px dashed #d9d9d9" }}>分公司端壳页面</div>;
}

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import AssignList from "~/components/AlarmDispose/AssignList";
function AlarmAssignPage(props) {
return <AssignList {...props} type="gfd" />;
}
export default Permission(AlarmAssignPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import AlarmRecordList from "~/components/AlarmRecord/List";
function AlarmRecordPage(props) {
return <AlarmRecordList {...props} type="gfd" />;
}
export default Permission(AlarmRecordPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import DcsList from "~/components/AlarmInfo/DcsList";
function DcsAlarmInfoPage(props) {
return <DcsList {...props} type="gfd" />;
}
export default Permission(DcsAlarmInfoPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import DeviceRegionList from "~/components/DeviceRegion/List";
function DeviceRegionPage(props) {
return <DeviceRegionList {...props} type="gfd" />;
}
export default Permission(DeviceRegionPage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import SensorDeviceList from "~/components/SensorDevice/List";
function SensorDevicePage(props) {
return <SensorDeviceList {...props} type="gfd" />;
}
export default Permission(SensorDevicePage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import SensorTypeList from "~/components/SensorType/List";
function SensorTypePage(props) {
return <SensorTypeList {...props} type="gfd" />;
}
export default Permission(SensorTypePage);

View File

@ -0,0 +1,8 @@
import { Permission } from "@cqsjjb/jjb-common-decorator/permission";
import ThresholdList from "~/components/AlarmInfo/ThresholdList";
function ThresholdAlarmInfoPage(props) {
return <ThresholdList {...props} type="gfd" />;
}
export default Permission(ThresholdAlarmInfoPage);

View File

@ -0,0 +1,7 @@
export default function Supervision(props) {
if (props.children) {
return props.children;
}
return <div style={{ padding: 24, border: "1px dashed #d9d9d9" }}>股份端壳页面</div>;
}

View File

@ -7,25 +7,22 @@ import { InjectContext } from "~/enumerate/context";
export default class Container extends React.Component {
state = window?.base?.themeConfig || {
algorithm: window.process.env.app.antd.algorithm,
borderRadius: window.process.env.app.antd.borderRadius,
colorPrimary: window.process.env.app.antd.colorPrimary,
algorithm: "defaultAlgorithm",
borderRadius: 6,
colorPrimary: "#0b6cff",
};
get token() {
const {
colorPrimary,
borderRadius,
} = this.state;
const { colorPrimary, borderRadius } = this.state;
return {
fontFamily: window.process.env.app.antd.fontFamily,
colorPrimary,
borderRadius,
fontFamily: "Microsoft YaHei, PingFang SC, sans-serif",
};
}
get algorithm() {
return antdTheme[this.state.algorithm];
return antdTheme[this.state.algorithm] || antdTheme.defaultAlgorithm;
}
componentDidMount() {
@ -46,7 +43,6 @@ export default class Container extends React.Component {
algorithm: this.algorithm,
}}
locale={language}
prefixCls={window.process.env.app.antd["ant-prefix"]}
>
<App style={{ height: "100%" }}>
<AppMiddle {...this.props} />

View File

@ -1,8 +1,23 @@
export default function () {
return (
<h1>
底座微应用模板技术文档
<a rel="noreferrer noopener" target="_blank" href="https://www.yuque.com/buhangjiecheshen-ymbtb/qc0093/gxdun1dphetcurko">https://www.yuque.com/buhangjiecheshen-ymbtb/qc0093/gxdun1dphetcurko</a>
</h1>
);
export { default as Container } from "./Container";
export { default as BranchCompany } from "./Container/BranchCompany";
export { default as Supervision } from "./Container/Supervision";
export { default as BranchCompanySensorType } from "./Container/BranchCompany/SensorType";
export { default as BranchCompanySensorDevice } from "./Container/BranchCompany/SensorDevice";
export { default as BranchCompanyDeviceRegion } from "./Container/BranchCompany/DeviceRegion";
export { default as BranchCompanyDcsAlarmInfo } from "./Container/BranchCompany/DcsAlarmInfo";
export { default as BranchCompanyThresholdAlarmInfo } from "./Container/BranchCompany/ThresholdAlarmInfo";
export { default as BranchCompanyAlarmAssign } from "./Container/BranchCompany/AlarmAssign";
export { default as BranchCompanyAlarmRecord } from "./Container/BranchCompany/AlarmRecord";
export { default as SupervisionSensorType } from "./Container/Supervision/SensorType";
export { default as SupervisionSensorDevice } from "./Container/Supervision/SensorDevice";
export { default as SupervisionDeviceRegion } from "./Container/Supervision/DeviceRegion";
export { default as SupervisionDcsAlarmInfo } from "./Container/Supervision/DcsAlarmInfo";
export { default as SupervisionThresholdAlarmInfo } from "./Container/Supervision/ThresholdAlarmInfo";
export { default as SupervisionAlarmAssign } from "./Container/Supervision/AlarmAssign";
export { default as SupervisionAlarmRecord } from "./Container/Supervision/AlarmRecord";
export default function HomePage() {
return <div style={{ padding: 24 }}>物联网消防报警模块前端骨架已完成</div>;
}