From d1ca9180751e54aeb170231d01e651066f51ced0 Mon Sep 17 00:00:00 2001 From: zjlnb666 <14659021+zhangjianlong666@user.noreply.gitee.com> Date: Thu, 20 Nov 2025 14:23:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=AE=BE=E5=A4=87=E7=8A=B6=E6=80=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E9=A1=B5=E9=9D=A2=E5=8F=8A=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加状态管理页面路由配置和导航菜单项 - 实现状态管理页面基础布局和功能组件 - 包含设备状态图表展示和列表管理功能 - 删除无用的样式文件untitled-1.txt --- config/routes.js | 15 +- .../AssetGrouping.js | 11 +- .../assetmangement_maintenance/Maintenance.js | 3 +- .../assetmangement_maintenance/untitled-1.txt | 17 - .../StateManagement.js | 533 ++++++++++++++++++ .../StateManagement.less | 34 ++ .../nav_system_content/SystemContentList.js | 24 +- src/pages/topnavbar/TopNavBar.js | 5 + 8 files changed, 612 insertions(+), 30 deletions(-) delete mode 100644 src/pages/assetmangement_maintenance/untitled-1.txt create mode 100644 src/pages/devicemonitoring_statemanagement/StateManagement.js create mode 100644 src/pages/devicemonitoring_statemanagement/StateManagement.less diff --git a/config/routes.js b/config/routes.js index a970a4c..6699ac8 100644 --- a/config/routes.js +++ b/config/routes.js @@ -65,7 +65,20 @@ export default [ component: './assetmangement_maintenance/Maintenance', }, ] - } + }, + // 设备监控 + { + path: '/topnavbar00/business/devicemonitoring', + name: 'devicemonitoring', + routes: [ + // 状态管理 + { + path: '/topnavbar00/business/devicemonitoring/statemanagement', + name: 'statemanagement', + component: './devicemonitoring_statemanagement/StateManagement', + }, + ] + }, ], }, ], diff --git a/src/pages/assetmangement_assetgrouping/AssetGrouping.js b/src/pages/assetmangement_assetgrouping/AssetGrouping.js index c4b650c..6bd8e57 100644 --- a/src/pages/assetmangement_assetgrouping/AssetGrouping.js +++ b/src/pages/assetmangement_assetgrouping/AssetGrouping.js @@ -351,16 +351,7 @@ const AssetGrouping = () => { - {/**/} - + diff --git a/src/pages/assetmangement_maintenance/Maintenance.js b/src/pages/assetmangement_maintenance/Maintenance.js index 97b95eb..bb40d8a 100644 --- a/src/pages/assetmangement_maintenance/Maintenance.js +++ b/src/pages/assetmangement_maintenance/Maintenance.js @@ -100,7 +100,7 @@ const Maintenance = ()=>{ ]; return (
-
+
@@ -143,6 +143,7 @@ const Maintenance = ()=>{
+ diff --git a/src/pages/assetmangement_maintenance/untitled-1.txt b/src/pages/assetmangement_maintenance/untitled-1.txt deleted file mode 100644 index a56f5ea..0000000 --- a/src/pages/assetmangement_maintenance/untitled-1.txt +++ /dev/null @@ -1,17 +0,0 @@ -.item{ - padding: 20px; - border: 1px solid; - border-image-slice: 1; - border-image-source: conic-gradient(from 102.21deg at 52.75% 38.75%, rgba(249, 249, 249, 0.5) -32.95deg, rgba(64, 64, 64, 0.5) 10.52deg, rgba(64, 64, 64, 0.35) 32.12deg, #FFFFFF 60.28deg, rgba(255, 255, 255, 0.5) 107.79deg, rgba(64, 64, 64, 0.35) 187.59deg, #F9F9F9 207.58deg, #FFFFFF 287.31deg, rgba(249, 249, 249, 0.5) 327.05deg, rgba(64, 64, 64, 0.5) 370.52deg); - background: #FFFFFF4D; - backdrop-filter: blur(15px); - border-radius: 8px; - - // 合并多个阴影效果,用逗号分隔 - box-shadow: - -2px 4px 10px 0px #9191910D, - -7px 17px 18px 0px #9191910A, - -15px 37px 24px 0px #91919108, - -27px 66px 29px 0px #91919103, - -42px 103px 31px 0px #91919100; -} diff --git a/src/pages/devicemonitoring_statemanagement/StateManagement.js b/src/pages/devicemonitoring_statemanagement/StateManagement.js new file mode 100644 index 0000000..922e373 --- /dev/null +++ b/src/pages/devicemonitoring_statemanagement/StateManagement.js @@ -0,0 +1,533 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { Row, Col, Card, Button, Input, Select, DatePicker, Space, Tag, Progress } from 'antd'; +import { SearchOutlined, InfoCircleOutlined, BellOutlined, DownloadOutlined, ReloadOutlined,PlusOutlined } from '@ant-design/icons'; +import * as echarts from 'echarts'; +import TableWithPagination from '@/components/assetmangement/table'; +import Title from '../homepage/compontent/title'; +import styles from './StateManagement.less'; + +const { Option } = Select; + +const StateManagement = () => { + const pieChartRef = useRef(null); + const lineChartRef = useRef(null); + const barChartRef = useRef(null); + const [searchValue, setSearchValue] = useState(''); + const [selectedStatus, setSelectedStatus] = useState('all'); + const [selectedDate, setSelectedDate] = useState(null); + + // 设备状态数据 + const deviceStatusData = [ + { key: '1', deviceId: 'DEV-001', deviceName: '温度传感器A', deviceType: '传感器', status: '在线', ip: '192.168.1.10', protocol: 'MQTT', location: '车间A', uptime: '在线8小时' }, + { key: '2', deviceId: 'DEV-002', deviceName: '工业网关B', deviceType: '网关', status: '离线', ip: '192.168.1.11', protocol: 'CoAP', location: '机房B', uptime: '离线2小时' }, + { key: '3', deviceId: 'DEV-003', deviceName: '高清摄像头C', deviceType: '摄像头', status: '故障', ip: '192.168.1.12', protocol: 'HTTP', location: '园区C', uptime: '离线1天(故障)' }, + { key: '4', deviceId: 'DEV-004', deviceName: '压力传感器D', deviceType: '传感器', status: '在线', ip: '192.168.1.13', protocol: 'Modbus', location: '产线D', uptime: '在线12小时' }, + { key: '5', deviceId: 'DEV-005', deviceName: '边缘计算节点E', deviceType: '网关', status: '在线', ip: '192.168.1.14', protocol: 'MQTT', location: '机房A', uptime: '在线24小时' }, + { key: '6', deviceId: 'DEV-006', deviceName: '电磁阀F', deviceType: '执行器', status: '离线', ip: '192.168.1.15', protocol: 'Modbus', location: '车间B', uptime: '离线5小时' }, + ]; + + // 设备状态占比数据 + const statusDistributionData = [ + { value: 60, name: '在线', itemStyle: { color: '#006665' } }, + { value: 25, name: '离线', itemStyle: { color: '#999999' } }, + { value: 15, name: '故障', itemStyle: { color: '#FF826D' } }, + ]; + + // 在线率趋势数据 + const onlineRateData = { + dates: ['星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], + rates: [88, 85, 90, 87, 82, 89] + }; + + // 设备类型分布数据 + const deviceTypeData = { + categories: ['传感器A', '网关A', '摄像头', '传感器B', '网关B', '执行器'], + values: [40, 88, 60, 100, 68, 58] + }; + + // 表格列定义 + const columns = [ + { title: '', dataIndex: 'checkbox', key: 'checkbox', width: 40 }, + { title: '设备ID', dataIndex: 'deviceId', key: 'deviceId' }, + { title: '设备名称', dataIndex: 'deviceName', key: 'deviceName' }, + { title: '设备类型', dataIndex: 'deviceType', key: 'deviceType' }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + render: (status) => { + const colorMap = { + '在线': 'green', + '离线': 'gray', + '故障': 'red' + }; + const statusTextMap = { + '在线': '在线(绿色)', + '离线': '离线(灰色)', + '故障': '故障(红色)' + }; + return {statusTextMap[status]}; + } + }, + { title: 'IP地址', dataIndex: 'ip', key: 'ip' }, + { title: '协议类型', dataIndex: 'protocol', key: 'protocol' }, + { title: '位置信息', dataIndex: 'location', key: 'location' }, + { title: '在线时长/离线时间', dataIndex: 'uptime', key: 'uptime' }, + { + title: '操作', + key: 'action', + align: 'center', + render: (_, record) => ( + + 查看详情 + 编辑 + 删除 + {record.status === '在线' && 巡检} + {record.status === '离线' && 工单} + {record.status === '故障' && 升级} + {record.status === '离线' && record.deviceType === '执行器' && 报修} + + ) + } + ]; + + // 初始化图表 + useEffect(() => { + // 设备状态占比饼图 + if (pieChartRef.current) { + const pieChart = echarts.init(pieChartRef.current); + const pieOption = { + tooltip: { + trigger: 'item', + formatter: '{a}
{b}: {c} ({d}%)' + }, + legend: { + orient: 'vertical', + left: 10, + top: 'center', + itemWidth: 14, + itemHeight: 14, + textStyle: { + fontSize: 12 + }, + itemGap: 30, + formatter: function(name) { + // 找到对应的数值 + const item = statusDistributionData.find(item => item.name === name); + return `${name} ${item.value}%`; + } + }, + series: [{ + name: '设备状态', + type: 'pie', + radius: [0, '70%'], + center: ['60%', '50%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 0, + borderColor: '#fff', + borderWidth: 2 + }, + label: { + show: false + }, + emphasis: { + label: { + show: true, + fontSize: 14, + fontWeight: 'bold' + } + }, + data: statusDistributionData + }] + }; + pieChart.setOption(pieOption); + } + + // 在线率趋势折线图 + if (lineChartRef.current) { + const lineChart = echarts.init(lineChartRef.current); + const lineOption = { + tooltip: { + trigger: 'axis', + formatter: '{b}
{a}: {c}%' + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + top:'5%', + containLabel: true + }, + xAxis: { + type: 'category', + data: onlineRateData.dates, + axisLine: { + lineStyle: { + color: '#d9d9d9', + type: 'dashed' + }, + show:true + }, + axisLabel: { + color: '#666', + show:true + }, + axisTick: { + alignWithLabel: true // 使x轴刻度与标签对齐 + }, + boundaryGap: false, // 设置坐标轴两端是否留白 + splitLine: { + lineStyle: { + color: '#CCCCCC', + type: 'deshed' + }, + show:true + } + + }, + yAxis: { + type: 'value', + min: 0, + max: 100, + axisLine: { + lineStyle: { + color: '#d9d9d9' + } + }, + axisLabel: { + formatter: '{value}%', + color: '#666' + }, + splitLine: { + lineStyle: { + color: '#f0f0f0', + type: 'solid' + } + } + }, + series: [{ + name: '在线率', + type: 'line', + data: onlineRateData.rates, + smooth: false, + lineStyle: { + color: '#2C9E9D', + width: 3, + // type: 'dashed' + }, + itemStyle: { + color: '#2C9E9D' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, color: '#E8F3EFcc' + }, { + offset: 1, color: '#EAF6F2cc' + }] + } + }, + emphasis: { + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, color: '#E8F3EFcc' + }, { + offset: 1, color: '#EAF6F2cc' + }] + } + } + } + }] + }; + lineChart.setOption(lineOption); + } + + // 设备类型分布柱状图 + if (barChartRef.current) { + const barChart = echarts.init(barChartRef.current); + const barOption = { + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow' + } + }, + grid: { + left: '3%', + right: '4%', + bottom: '3%', + top:'10%', + containLabel: true + }, + xAxis: { + type: 'category', + data: deviceTypeData.categories, + axisLine: { + lineStyle: { + color: '#d9d9d9' + } + }, + axisLabel: { + color: '#666', + interval: 0 + }, + splitLine: { + show: true, + lineStyle: { + color: '#CCCCCC', + type: 'deshed' + }, + } + }, + yAxis: { + type: 'value', + axisLine: { + lineStyle: { + color: '#d9d9d9' + } + }, + axisLabel: { + color: '#666' + }, + splitLine: { + lineStyle: { + color: '#f0f0f0', + type: 'dashed' // 类目之间的虚线 + } + }, + max:()=>{ + return Math.max(...deviceTypeData.values) * 1.2; + } + }, + series: [{ + name: '设备数量', + type: 'bar', + data: deviceTypeData.values, + itemStyle: { + color: function(params) { + // 根据数据值设置不同的颜色 + const colors = ['#86bbd8', '#8884d8', '#006665', '#2e8bc0', '#a0c4e2', '#e8d4b0']; + return colors[params.dataIndex % colors.length]; + } + }, + barWidth: '30%', + label: { + normal: { + show: true, + position: 'top', + textStyle: { + color: '#666' + } + } + } + }] + }; + barChart.setOption(barOption); + } + + // 响应式处理 + const handleResize = () => { + if (pieChartRef.current) { + const pieChart = echarts.getInstanceByDom(pieChartRef.current); + pieChart && pieChart.resize(); + } + if (lineChartRef.current) { + const lineChart = echarts.getInstanceByDom(lineChartRef.current); + lineChart && lineChart.resize(); + } + if (barChartRef.current) { + const barChart = echarts.getInstanceByDom(barChartRef.current); + barChart && barChart.resize(); + } + }; + + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, []); + + // 筛选处理函数 + const handleSearch = () => { + console.log('搜索条件:', { searchValue, selectedStatus, selectedDate }); + }; + + const handleReset = () => { + setSearchValue(''); + setSelectedStatus('all'); + setSelectedDate(null); + }; + + return ( +
+ {/* 筛选条件区域 */} + + + + <Col span={8} style={{ textAlign: 'right' }}> + <Space> + <Button + type="primary" + icon={<SearchOutlined />} + onClick={handleSearch} + style={{ borderRadius: '6px' }} + className={styles['search-button']} + > + 查询 + </Button> + <Button + onClick={handleReset} + style={{ borderRadius: '6px' }} + className={styles['reset-button']} + icon={<ReloadOutlined /> } + > + 重置 + </Button> + </Space> + </Col> + </Row> + <Row gutter={16} align="middle" style={{margin:'20px 0'}} justify='space-between'> + <Col span={6}> + <Input + placeholder="请输入设备名称或编号" + value={searchValue} + onChange={(e) => setSearchValue(e.target.value)} + style={{ borderRadius: '6px' }} + suffix={<SearchOutlined style={{ color: '#bfbfbf' }} />} + /> + </Col> + <Col span={4}> + <label>设备状态:</label> + <Select + value={selectedStatus} + onChange={setSelectedStatus} + style={{ width: '152px', borderRadius: '6px' ,marginLeft:'10px'}} + > + <Option value="all">全部状态</Option> + <Option value="online">在线</Option> + <Option value="offline">离线</Option> + <Option value="abnormal">异常</Option> + </Select> + </Col> + <Col span={4}> + <label>位置信息:</label> + <Select + value={selectedStatus} + // onChange={setSelectedStatus} + style={{ width: '152px', borderRadius: '6px' ,marginLeft:'10px'}} + > + <Option value="all">全部</Option> + </Select> + </Col> + <Col span={4}> + <label>设备状态:</label> + <Select + value={selectedStatus} + // onChange={setSelectedStatus} + style={{ width: '152px', borderRadius: '6px' ,marginLeft:'10px'}} + > + <Option value="all">全部</Option> + </Select> + </Col> + <Col span={4}> + <label>协议类型:</label> + <Select + value={selectedStatus} + // onChange={setSelectedStatus} + style={{ width: '152px', borderRadius: '6px' ,marginLeft:'10px'}} + > + <Option value="all">全部</Option> + </Select> + </Col> + </Row> + </> + } + > + + </Card> + + {/* 数据可视化区域 */} + <Row gutter={20} style={{ marginBottom: '20px' }}> + <Col span={6}> + <Card + title= {<Title title="设备状态占比饼图"/>} + style={{ borderRadius: '8px', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }} + bodyStyle={{ padding: '16px', height: '200px' }} + > + <div ref={pieChartRef} style={{ width: '100%', height: '100%' }} /> + </Card> + </Col> + <Col span={9}> + <Card + title={<Title title="设备在线率趋势折线图"/>} + style={{ borderRadius: '8px', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }} + bodyStyle={{ padding: '16px', height: '200px' }} + > + <div ref={lineChartRef} style={{ width: '100%', height: '100%' }} /> + </Card> + </Col> + <Col span={9}> + <Card + title={<Title title="设备类型分布柱状图"/>} + style={{ borderRadius: '8px', boxShadow: '0 2px 8px rgba(0,0,0,0.1)' }} + bodyStyle={{ padding: '16px', height: '200px' }} + > + <div ref={barChartRef} style={{ width: '100%', height: '100%' }} /> + </Card> + </Col> + </Row> + + {/* 设备列表表格 */} + <Card + style={{ + borderRadius: '8px', + boxShadow: '0 2px 8px rgba(0,0,0,0.1)' + }} + bodyStyle={{ padding: '0' }} + > + <div style={{ padding: '20px 24px', borderBottom: '1px solid #f0f0f0' }}> + <Row justify="space-between" align="middle"> + <Title title="设备状态列表"/> + </Row> + <Row justify="space-between" align="middle" style={{ margin: '20px 0' }}> + <Col span={12}> + <Button style={{marginRight:30}} className={styles['search-button']}>导入数据</Button> + <Button style={{marginRight:30}} className={styles['reset-button']}>导出数据</Button> + <Button className={styles['del-button']}>删除</Button> + </Col> + <Col span={12} style={{ textAlign: 'right' }}> + <Button className={styles['search-button']} icon={<PlusOutlined />}>新增设备</Button> + </Col> + </Row> + <TableWithPagination + columns={columns} + dataSource={deviceStatusData} + rowSelection={true} + style={{ padding: '0 24px' }} + /> + </div> + + </Card> + </div> + ); +}; + +export default StateManagement; diff --git a/src/pages/devicemonitoring_statemanagement/StateManagement.less b/src/pages/devicemonitoring_statemanagement/StateManagement.less new file mode 100644 index 0000000..3cfe143 --- /dev/null +++ b/src/pages/devicemonitoring_statemanagement/StateManagement.less @@ -0,0 +1,34 @@ +.search-button{ + background-image: url('../../assets/img/assetmangement1.png'); + background-repeat: no-repeat; + background-size: cover; + background-position:center; + color: #fff; + border-radius: 4px; + height: 36px; + border-color:#d9d9d9 ; + background-color: #045F5E80; +} +.reset-button{ + background-image: url('../../assets/img/assetmangement2.png'); + background-repeat: no-repeat; + background-size: cover; + background-position:center; + color: rgba(0, 102, 101, 1); + border-radius: 4px; + height: 36px; + border-color:#d9d9d9 ; + background-color: #B7E5D533; +} +.del-button{ + background-image: url('../../assets/img/assetmangement3.png'); + background-repeat: no-repeat; + background-size: cover; + background-position:center; + color: #000; + border-radius: 4px; + height: 36px; + width:88px; + border-color:#d9d9d9 ; + background-color: #E5B7B733; +} \ No newline at end of file diff --git a/src/pages/nav_system_content/SystemContentList.js b/src/pages/nav_system_content/SystemContentList.js index 0f797a8..4ba6b10 100644 --- a/src/pages/nav_system_content/SystemContentList.js +++ b/src/pages/nav_system_content/SystemContentList.js @@ -154,7 +154,29 @@ const SystemContentList = (props) => { "label": "维保管理" }, ] - } + }, + // 设备监控 + { + path: '/topnavbar00/business/devicemonitoring', + icon: <img + src={icon1} + alt="设备监控" + style={{ + width: '16px', + height: '16px', + opacity: selectedKey.includes('/topnavbar00/business/devicemonitoring') ? 1 : 0.6 + }} + />, + key: "/topnavbar00/business/devicemonitoring", + "label": "设备监控", + children: [ + { + path: '/topnavbar00/business/devicemonitoring/statemanagement', + key: "/topnavbar00/business/devicemonitoring/statemanagement", + "label": "状态管理" + }, + ] + }, ] setMenuItems(fixedMenuItems) // 初始化默认路由 diff --git a/src/pages/topnavbar/TopNavBar.js b/src/pages/topnavbar/TopNavBar.js index bf839d9..c85e188 100644 --- a/src/pages/topnavbar/TopNavBar.js +++ b/src/pages/topnavbar/TopNavBar.js @@ -34,6 +34,11 @@ const menuItem = [ label: '维保管理', key: '/topnavbar00/business/assetmanagement/maintenance', }, + // 状态管理 + { + label: '状态管理', + key: '/topnavbar00/business/devicemonitoring/statemanagement', + }, ] const TopNavBar = (props) => {