|
|
|
|
|
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}<br/>`;
|
|
|
|
|
|
params.forEach(param => {
|
|
|
|
|
|
result += `${param.seriesName}: ${param.value}<br/>`;
|
|
|
|
|
|
});
|
|
|
|
|
|
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}<br/>`;
|
|
|
|
|
|
params.forEach(param => {
|
|
|
|
|
|
result += `${param.seriesName}: ${param.value}%<br/>`;
|
|
|
|
|
|
});
|
|
|
|
|
|
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 (
|
|
|
|
|
|
<span style={{ color }}>
|
|
|
|
|
|
{text}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟数据
|
|
|
|
|
|
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 (
|
|
|
|
|
|
<div className={styles.analysisContainer}>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.analysisContainerMiddle}>
|
|
|
|
|
|
<div className={styles.analysisSectionContent}>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.analysisMiddleBlock2}>
|
|
|
|
|
|
<div className={styles.analysisMiddleBlock2Title}>
|
|
|
|
|
|
<div className={styles.analysisTitleLeft}>
|
|
|
|
|
|
<div className={styles.analysisTitleIcon}></div>
|
|
|
|
|
|
<div>误报原因分析</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMiddleBlock2Chart} ref={chartRef}>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.analysisMiddleBlock1}>
|
|
|
|
|
|
<div className={styles.analysisBlock1Header}>
|
|
|
|
|
|
<div className={styles.analysisBlock1Title}>
|
|
|
|
|
|
<div className={styles.analysisTitleIcon}></div>
|
|
|
|
|
|
报表统计生成
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisDeviceStatusChart} ref={pieChartRef}>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.analysisMiddleBlock12}>
|
|
|
|
|
|
<div className={styles.analysisBlock1Header}>
|
|
|
|
|
|
<div className={styles.analysisBlock1Title}>
|
|
|
|
|
|
<div className={styles.analysisTitleIcon}></div>
|
|
|
|
|
|
设备运行状态趋势
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.analysisDeviceStatusChart} ref={faultPieChartRef}>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 底部区域 */}
|
|
|
|
|
|
<div className={styles.analysisBottom}>
|
|
|
|
|
|
{/* 左侧维护提醒 */}
|
|
|
|
|
|
<div className={styles.analysisMaintenanceSection}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceTitle}>
|
|
|
|
|
|
<div className={styles.analysisTitleIcon}></div>
|
|
|
|
|
|
<div>实时预警信息</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceContent}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceItem1}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceLeft}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText1}>电路线路过载预警</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText2}>B栋3层配电室丨15分钟前</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText3}>电流持续上升,已超过正常阈值</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceRight}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceStatus}>紧急</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceItem2}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceLeft}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText1}>2号防排烟风机异常</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText2}>地下车库丨2小时前</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText3}>震动频率异常, 建议尽快检修</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceRight2}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceStatus}>警告</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceItem3}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceLeft}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText1}>消防水泵预测维护</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText2}>A栋水泵房丨今天</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText3}>预计15天后需要维护,请提前安排</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceRight2}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceStatus2}>提示</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceItem4}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceLeft}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText1}>消防水泵预测维护</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText2}>A栋水泵房丨今天</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceText3}>预计15天后需要维护,请提前安排</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceRight2}>
|
|
|
|
|
|
<div className={styles.analysisMaintenanceStatus2}>提示</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 右侧表格 */}
|
|
|
|
|
|
<div className={styles.analysisTableSection}>
|
|
|
|
|
|
<div className={styles.analysisTableHeader}>
|
|
|
|
|
|
<div className={styles.analysisTableTitle}>
|
|
|
|
|
|
<div className={styles.analysisTitleIcon}></div>
|
|
|
|
|
|
<div>月度报警统计</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.analysisTableActions}>
|
|
|
|
|
|
<button className={styles.analysisActionButton} onClick={handleExportData}>
|
|
|
|
|
|
<span className={styles.analysisButtonIcon}><ExportOutlined /></span>
|
|
|
|
|
|
<span>导出数据</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.analysisTableContainer}>
|
|
|
|
|
|
<StandardTable
|
|
|
|
|
|
columns={columns}
|
|
|
|
|
|
data={{
|
|
|
|
|
|
list: getCurrentPageData(),
|
|
|
|
|
|
pagination: pagination
|
|
|
|
|
|
}}
|
|
|
|
|
|
loading={loading}
|
|
|
|
|
|
selectionType="checkbox"
|
|
|
|
|
|
onSelectRow={onSelectChange}
|
|
|
|
|
|
onChange={handleTableChange}
|
|
|
|
|
|
pagination={{
|
|
|
|
|
|
...pagination,
|
|
|
|
|
|
showSizeChanger: false,
|
|
|
|
|
|
showQuickJumper: true,
|
|
|
|
|
|
showTotal: (total, range) =>
|
|
|
|
|
|
`共 ${total} 条`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default DataAnalysisWarning;
|