import React, { useEffect, useRef, useState } from 'react'; import { Card, Result, Select, Button, Segmented, Progress, Input } from 'antd'; import { CheckCircleOutlined, ExportOutlined, HeartFilled, LineHeightOutlined, ExclamationCircleOutlined, SearchOutlined } from '@ant-design/icons'; import * as echarts from 'echarts'; import StandardTable from '@/components/StandardTable'; import styles from './DataAnalysisWarning.less'; import img1 from '@/assets/safe_majorHazard/online_monitoring/img1.png'; import img2 from '@/assets/safe_majorHazard/online_monitoring/img2.png'; import img3 from '@/assets/safe_majorHazard/online_monitoring/img3.png'; import map1 from '@/assets/safe_majorHazard/online_monitoring/map.png'; import risk1 from '@/assets/safe_majorHazard/online_monitoring/risk1.png'; import risk2 from '@/assets/safe_majorHazard/online_monitoring/risk2.png'; import risk3 from '@/assets/safe_majorHazard/online_monitoring/risk3.png'; import eqicon1 from '@/assets/business_basic/eqicon1.png'; import eqicon2 from '@/assets/business_basic/eqicon2.png'; import eqicon3 from '@/assets/business_basic/eqicon3.png'; import eqicon4 from '@/assets/business_basic/eqicon4.png'; const DataAnalysisWarning = () => { const chartRef = useRef(null); const pieChartRef = useRef(null); const faultPieChartRef = useRef(null); const [selectedRowKeys, setSelectedRowKeys] = useState([]); const [selectedRows, setSelectedRows] = useState([]); const [loading, setLoading] = useState(false); const [dataSource, setDataSource] = useState([]); const [pagination, setPagination] = useState({ current: 1, pageSize: 5, total: 0, }); const [searchText, setSearchText] = useState(''); // 柱状图初始化 useEffect(() => { if (pieChartRef.current) { const barChart = echarts.init(pieChartRef.current); const barOption = { grid: { left: '5%', right: '5%', bottom: '10%', top: '18%', containLabel: true }, xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], axisLabel: { fontSize: 12, color: '#333', interval: 0, rotate: 0 }, axisLine: { show: false }, axisTick: { show: false } }, yAxis: { type: 'value', min: 0, max: 35, interval: 5, axisLabel: { fontSize: 12, color: '#666', formatter: '{value}' }, axisLine: { show: false }, axisTick: { show: false }, splitLine: { lineStyle: { color: '#00001A26', type: 'dashed' } } }, series: [ { name: '火灾报警', type: 'bar', stack: 'total', barWidth: 20, data: [12, 8, 15, 10, 18, 14, 16, 13, 11, 17, 19, 15], itemStyle: { color: '#8979FF' } }, { name: '故障报警', type: 'bar', stack: 'total', barWidth: 20, data: [6, 9, 7, 12, 8, 11, 9, 14, 10, 7, 8, 6], itemStyle: { color: '#FF928A' } }, { name: '误报', type: 'bar', stack: 'total', barWidth: 20, data: [3, 5, 4, 7, 6, 8, 5, 9, 7, 4, 6, 5], itemStyle: { color: '#3CC3DF' } } ], legend: { show: true, top: '8%', left: 'center', itemWidth: 8, itemHeight: 8, textStyle: { fontSize: 12, color: '#333' }, data: [ { name: '火灾报警', icon: 'rect', itemStyle: { color: '#8979FF' } }, { name: '故障报警', icon: 'rect', itemStyle: { color: '#FF928A' } }, { name: '误报', icon: 'rect', itemStyle: { color: '#3CC3DF' } } ] }, tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: function (params) { let result = `${params[0].name}
`; params.forEach(param => { result += `${param.seriesName}: ${param.value}
`; }); return result; } } }; barChart.setOption(barOption); // 响应式调整 const handleBarResize = () => { if (barChart && !barChart.isDisposed()) { barChart.resize(); } }; window.addEventListener('resize', handleBarResize); return () => { window.removeEventListener('resize', handleBarResize); if (barChart && !barChart.isDisposed()) { barChart.dispose(); } }; } }, []); // 设备运行状态趋势折线图初始化 useEffect(() => { if (faultPieChartRef.current) { const faultPieChart = echarts.init(faultPieChartRef.current); const faultPieOption = { legend: { show: true, top: '5%', left: 'center', itemWidth: 20, itemHeight: 8, textStyle: { color: '#333', fontSize: 12 } }, grid: { left: '5%', right: '5%', bottom: '10%', top: '20%', containLabel: true }, xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], axisLine: { lineStyle: { color: '#E5E5E5' } }, axisTick: { show: false }, axisLabel: { color: '#666', fontSize: 12, interval: 0 } }, yAxis: { type: 'value', min: 0, max: 100, interval: 20, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { color: '#666', fontSize: 12, formatter: '{value}%' }, splitLine: { lineStyle: { color: '#00001A26', type: 'dashed' } } }, series: [ { name: '正常运行率', type: 'line', data: [85, 78, 92, 88, 95, 90, 87, 93, 89, 91, 86, 88], smooth: false, symbol: 'circle', symbolSize: 6, lineStyle: { color: '#3CC3DF', width: 1 }, itemStyle: { color: '#FFF', borderColor: '#3CC3DF', borderWidth: 1 }, areaStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'rgba(60, 195, 223, 0.3)' }, { offset: 1, color: 'rgba(60, 195, 223, 0.05)' }] } } }, { name: '故障率', type: 'line', data: [15, 22, 8, 12, 5, 10, 13, 7, 11, 9, 14, 12], smooth: false, symbol: 'circle', symbolSize: 6, lineStyle: { color: '#8979FF', width: 1 }, itemStyle: { color: '#fff', borderColor: '#8979FF', borderWidth: 1 }, areaStyle: { color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'rgba(137, 121, 255, 0.3)' }, { offset: 1, color: 'rgba(137, 121, 255, 0.05)' }] } } } ], tooltip: { trigger: 'axis', axisPointer: { type: 'cross' }, formatter: function (params) { let result = `${params[0].name}
`; params.forEach(param => { result += `${param.seriesName}: ${param.value}%
`; }); return result; } } }; faultPieChart.setOption(faultPieOption); // 响应式调整 const handleFaultPieResize = () => { if (faultPieChart && !faultPieChart.isDisposed()) { faultPieChart.resize(); } }; window.addEventListener('resize', handleFaultPieResize); return () => { window.removeEventListener('resize', handleFaultPieResize); if (faultPieChart && !faultPieChart.isDisposed()) { faultPieChart.dispose(); } }; } }, []); useEffect(() => { if (chartRef.current) { const chart = echarts.init(chartRef.current); // 强制初始化时调整大小 setTimeout(() => { if (chart && !chart.isDisposed()) { chart.resize(); } }, 100); const option = { tooltip: { trigger: 'item' }, legend: { bottom: '4%', left: 'center', itemWidth: 16, itemHeight: 5, textStyle: { fontSize: 12, } }, series: [ { name: '设备故障原因', type: 'pie', radius: ['20%', '65%'], center: ['50%', '40%'], avoidLabelOverlap: false, padAngle: 5, itemStyle: { borderRadius: 8, borderColor: '#fff', borderWidth: 2 }, label: { show: false, position: 'center' }, // emphasis: { // label: { // show: true, // fontSize: 40, // fontWeight: 'bold' // } // }, labelLine: { show: false }, data: [ { value: 1048, name: '环境因素(粉尘)', itemStyle: { color: '#44BB5F' } }, { value: 735, name: '环境因素(湿度)', itemStyle: { color: '#F8C541' } }, { value: 580, name: '设备故障', itemStyle: { color: '#A493FB' } }, { value: 484, name: '施工干扰', itemStyle: { color: '#4B69F1' } }, { value: 300, name: '其他', itemStyle: { color: '#949FD0' } } ] } ] }; chart.setOption(option); // 响应式调整 - 使用多种方式监听容器尺寸变化 let resizeTimer = null; const handleResize = () => { // 防抖处理,避免频繁调用resize if (resizeTimer) { clearTimeout(resizeTimer); } resizeTimer = setTimeout(() => { if (chart && !chart.isDisposed()) { chart.resize(); } }, 50); // 减少延迟时间 }; // 监听窗口大小变化 window.addEventListener('resize', handleResize); // 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题) let resizeObserver = null; if (window.ResizeObserver) { resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { // 使用requestAnimationFrame确保在下一帧执行 requestAnimationFrame(() => { handleResize(); }); } }); resizeObserver.observe(chartRef.current); } // 额外监听父容器的尺寸变化 const parentContainer = chartRef.current?.parentElement; let parentObserver = null; if (parentContainer && window.ResizeObserver) { parentObserver = new ResizeObserver((entries) => { for (let entry of entries) { requestAnimationFrame(() => { handleResize(); }); } }); parentObserver.observe(parentContainer); } // 使用MutationObserver监听DOM结构变化(菜单展开收起时) const mutationObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'attributes' && (mutation.attributeName === 'class' || mutation.attributeName === 'style')) { // 延迟执行,确保DOM更新完成 setTimeout(() => { handleResize(); }, 200); } }); }); // 监听整个页面的class和style变化 mutationObserver.observe(document.body, { attributes: true, attributeFilter: ['class', 'style'], subtree: true }); return () => { window.removeEventListener('resize', handleResize); if (resizeObserver) { resizeObserver.disconnect(); } if (parentObserver) { parentObserver.disconnect(); } if (mutationObserver) { mutationObserver.disconnect(); } if (resizeTimer) { clearTimeout(resizeTimer); } if (chart && !chart.isDisposed()) { chart.dispose(); } }; } }, []); // 表格列定义 const columns = [ { title: '序号', dataIndex: 'id', key: 'id', width: 60, render: (text, record, index) => { const page = pagination.current || 1; const pageSize = pagination.pageSize || 5; const number = (page - 1) * pageSize + index + 1; return number; } }, { title: '区域', dataIndex: 'area', key: 'area', width: 120, }, { title: '火灾报警', dataIndex: 'fireAlarm', key: 'fireAlarm', width: 100, }, { title: '故障报警', dataIndex: 'faultAlarm', key: 'faultAlarm', width: 100, }, { title: '误报', dataIndex: 'falseAlarm', key: 'falseAlarm', width: 100, }, { title: '总计', dataIndex: 'total', key: 'total', width: 100, }, { title: '同比变化', dataIndex: 'yearOverYear', key: 'yearOverYear', width: 120, render: (text) => { const isPositive = text.includes('↑'); const color = isPositive ? '#FF3E48' : '#44BB5F'; const icon = isPositive ? '↑' : '↓'; return ( {text} ); } }, ]; // 模拟数据 const mockData = [ { key: '1', id: '1', area: 'A栋', fireAlarm: 3, faultAlarm: 7, falseAlarm: 10, total: 23, yearOverYear: '↓ 12%', }, { key: '2', id: '2', area: 'B栋', fireAlarm: 2, faultAlarm: 9, falseAlarm: 6, total: 18, yearOverYear: '↑ 8%', }, { key: '3', id: '3', area: 'C栋', fireAlarm: 1, faultAlarm: 5, falseAlarm: 8, total: 16, yearOverYear: '↓ 15%', }, { key: '4', id: '4', area: 'D栋', fireAlarm: 0, faultAlarm: 2, falseAlarm: 7, total: 11, yearOverYear: '↓ 16%', }, { key: '5', id: '5', area: 'E栋', fireAlarm: 4, faultAlarm: 6, falseAlarm: 5, total: 15, yearOverYear: '↓ 5%', }, { key: '6', id: '6', area: 'F栋', fireAlarm: 2, faultAlarm: 8, falseAlarm: 9, total: 19, yearOverYear: '↑ 3%', }, { key: '7', id: '7', area: 'G栋', fireAlarm: 1, faultAlarm: 4, falseAlarm: 6, total: 11, yearOverYear: '↓ 8%', }, { key: '8', id: '8', area: 'H栋', fireAlarm: 3, faultAlarm: 3, falseAlarm: 4, total: 10, yearOverYear: '↓ 20%', }, { key: '9', id: '9', area: 'I栋', fireAlarm: 0, faultAlarm: 1, falseAlarm: 3, total: 4, yearOverYear: '↓ 25%', }, { key: '10', id: '10', area: 'J栋', fireAlarm: 2, faultAlarm: 5, falseAlarm: 7, total: 14, yearOverYear: '↑ 2%', }, ]; // 初始化数据 useEffect(() => { setPagination(prev => ({ ...prev, total: mockData.length })); }, []); // 根据分页获取当前页数据 const getCurrentPageData = () => { const { current, pageSize } = pagination; const startIndex = (current - 1) * pageSize; const endIndex = startIndex + pageSize; return mockData.slice(startIndex, endIndex); }; // 表格选择变化 const onSelectChange = (newSelectedRowKeys, newSelectedRows) => { setSelectedRowKeys(newSelectedRowKeys); setSelectedRows(newSelectedRows); }; // 新增设备按钮点击事件 const handleAddDevice = () => { console.log('新增设备'); // TODO: 实现新增设备逻辑 }; // 导出数据按钮点击事件 const handleExportData = () => { console.log('导出数据'); // TODO: 实现导出数据逻辑 }; // 分页变化处理 const handleTableChange = (pagination) => { setPagination(prev => ({ ...prev, current: pagination.current, pageSize: pagination.pageSize, })); }; // 搜索处理 const handleSearchChange = (e) => { setSearchText(e.target.value); console.log('搜索:', e.target.value); // TODO: 实现搜索逻辑,根据设备名称、编号等筛选数据 }; return (
误报原因分析
报表统计生成
设备运行状态趋势
{/* 底部区域 */}
{/* 左侧维护提醒 */}
实时预警信息
电路线路过载预警
B栋3层配电室丨15分钟前
电流持续上升,已超过正常阈值
紧急
2号防排烟风机异常
地下车库丨2小时前
震动频率异常, 建议尽快检修
警告
消防水泵预测维护
A栋水泵房丨今天
预计15天后需要维护,请提前安排
提示
消防水泵预测维护
A栋水泵房丨今天
预计15天后需要维护,请提前安排
提示
{/* 右侧表格 */}
月度报警统计
`共 ${total} 条`, }} />
); }; export default DataAnalysisWarning;