|
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useRef, useState } from 'react';
|
|
|
|
|
|
import { Card, Result, Select, Button, Segmented } from 'antd';
|
|
|
|
|
|
import { CheckCircleOutlined, ExportOutlined } from '@ant-design/icons';
|
|
|
|
|
|
import * as echarts from 'echarts';
|
|
|
|
|
|
import StandardTable from '@/components/StandardTable';
|
|
|
|
|
|
import styles from './RiskAssessment.less';
|
|
|
|
|
|
// import './RiskAssessment.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 RiskAssessment = () => {
|
|
|
|
|
|
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,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 饼图初始化
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (pieChartRef.current) {
|
|
|
|
|
|
const pieChart = echarts.init(pieChartRef.current);
|
|
|
|
|
|
|
|
|
|
|
|
const pieOption = {
|
|
|
|
|
|
color: ['#44BB5F', '#F8C541', '#A493FB', '#4B69F1', '#949FD0'],
|
|
|
|
|
|
legend: {
|
|
|
|
|
|
orient: 'vertical',
|
|
|
|
|
|
right: '10%',
|
|
|
|
|
|
top: 'center',
|
|
|
|
|
|
itemWidth: 8,
|
|
|
|
|
|
itemHeight: 8,
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
color: '#333'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [{
|
|
|
|
|
|
name: '设备状态',
|
|
|
|
|
|
type: 'pie',
|
|
|
|
|
|
radius: ['40%', '70%'],
|
|
|
|
|
|
center: ['35%', '50%'],
|
|
|
|
|
|
avoidLabelOverlap: false,
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
position: 'center'
|
|
|
|
|
|
},
|
|
|
|
|
|
emphasis: {
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
fontSize: '14',
|
|
|
|
|
|
fontWeight: 'bold'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
labelLine: {
|
|
|
|
|
|
show: false
|
|
|
|
|
|
},
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ value: 480, name: '正常' },
|
|
|
|
|
|
{ value: 289, name: '故障' },
|
|
|
|
|
|
{ value: 200, name: '维修中' },
|
|
|
|
|
|
{ value: 150, name: '待验收' },
|
|
|
|
|
|
{ value: 161, name: '停用' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
pieChart.setOption(pieOption);
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式调整
|
|
|
|
|
|
const handlePieResize = () => {
|
|
|
|
|
|
if (pieChart && !pieChart.isDisposed()) {
|
|
|
|
|
|
pieChart.resize();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
window.addEventListener('resize', handlePieResize);
|
|
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
|
window.removeEventListener('resize', handlePieResize);
|
|
|
|
|
|
if (pieChart && !pieChart.isDisposed()) {
|
|
|
|
|
|
pieChart.dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
|
|
// 故障类型饼图初始化
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (faultPieChartRef.current) {
|
|
|
|
|
|
const faultPieChart = echarts.init(faultPieChartRef.current);
|
|
|
|
|
|
|
|
|
|
|
|
const faultPieOption = {
|
|
|
|
|
|
color: ['#FF3E48', '#FF8800', '#FFC403'],
|
|
|
|
|
|
legend: {
|
|
|
|
|
|
orient: 'vertical',
|
|
|
|
|
|
right: '10%',
|
|
|
|
|
|
top: 'center',
|
|
|
|
|
|
itemWidth: 8,
|
|
|
|
|
|
itemHeight: 8,
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
color: '#333'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [{
|
|
|
|
|
|
name: '设备故障类型',
|
|
|
|
|
|
type: 'pie',
|
|
|
|
|
|
radius: '70%',
|
|
|
|
|
|
center: ['35%', '50%'],
|
|
|
|
|
|
avoidLabelOverlap: false,
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
position: 'outside',
|
|
|
|
|
|
formatter: '{b}: {c}',
|
|
|
|
|
|
fontSize: 12
|
|
|
|
|
|
},
|
|
|
|
|
|
emphasis: {
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: true,
|
|
|
|
|
|
fontSize: '14',
|
|
|
|
|
|
fontWeight: 'bold'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
labelLine: {
|
|
|
|
|
|
show: true
|
|
|
|
|
|
},
|
|
|
|
|
|
data: [
|
|
|
|
|
|
{ value: 120, name: '紧急' },
|
|
|
|
|
|
{ value: 80, name: '重要' },
|
|
|
|
|
|
{ value: 60, name: '一般' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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 = {
|
|
|
|
|
|
color: ['#8979FF', '#3CC3DF'],
|
|
|
|
|
|
|
|
|
|
|
|
legend: {
|
|
|
|
|
|
// data: ['消防水泵1', '消防水泵2'],
|
|
|
|
|
|
top: "-3px",
|
|
|
|
|
|
// left: "center",
|
|
|
|
|
|
// itemGap: 40,
|
|
|
|
|
|
itemWidth: 20,
|
|
|
|
|
|
itemHeight: 8,
|
|
|
|
|
|
// icon: 'path://M902 472.7H747.9c-19.1-113.3-117.7-200-236.4-200s-217.3 86.7-236.4 200H119.7c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h155.5c19.1 113.3 117.7 200 236.4 200S728.9 666 748 552.7h154c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z m-390.5 200c-88.2 0-160-71.8-160-160s71.8-160 160-160 160 71.8 160 160-71.8 160-160 160z',
|
|
|
|
|
|
textStyle: {
|
|
|
|
|
|
fontSize: 10
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
grid: {
|
|
|
|
|
|
left: '2%',
|
|
|
|
|
|
right: '4%',
|
|
|
|
|
|
bottom: '2%',
|
|
|
|
|
|
top: '12%',
|
|
|
|
|
|
containLabel: true
|
|
|
|
|
|
},
|
|
|
|
|
|
xAxis: {
|
|
|
|
|
|
type: 'category',
|
|
|
|
|
|
boundaryGap: false,
|
|
|
|
|
|
data: ['9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00'],
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
fontSize: 10
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
yAxis: {
|
|
|
|
|
|
type: 'value',
|
|
|
|
|
|
min: 0,
|
|
|
|
|
|
max: 30,
|
|
|
|
|
|
axisLabel: {
|
|
|
|
|
|
formatter: '{value}',
|
|
|
|
|
|
fontSize: 10
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '消防水泵1',
|
|
|
|
|
|
type: 'line',
|
|
|
|
|
|
smooth: false,
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
width: 2,
|
|
|
|
|
|
color: '#8979FF'
|
|
|
|
|
|
},
|
|
|
|
|
|
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)' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
symbol: 'circle',
|
|
|
|
|
|
symbolSize: 4,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
borderColor: '#8979FF',
|
|
|
|
|
|
borderWidth: 1
|
|
|
|
|
|
},
|
|
|
|
|
|
data: [12, 15, 18, 14, 16, 20, 22, 19, 17, 21, 23, 25]
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
name: '消防水泵2',
|
|
|
|
|
|
type: 'line',
|
|
|
|
|
|
smooth: false,
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
width: 2,
|
|
|
|
|
|
color: '#3CC3DF'
|
|
|
|
|
|
},
|
|
|
|
|
|
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)' }
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
symbol: 'circle',
|
|
|
|
|
|
symbolSize: 4,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
color: '#fff',
|
|
|
|
|
|
borderColor: '#3CC3DF',
|
|
|
|
|
|
borderWidth: 1
|
|
|
|
|
|
},
|
|
|
|
|
|
data: [8, 11, 14, 10, 13, 17, 19, 16, 14, 18, 20, 22]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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 `0${number}`.slice(-2);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '设备编号',
|
|
|
|
|
|
dataIndex: 'deviceId',
|
|
|
|
|
|
key: 'deviceId',
|
|
|
|
|
|
width: 140,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '设备名称',
|
|
|
|
|
|
dataIndex: 'deviceName',
|
|
|
|
|
|
key: 'deviceName',
|
|
|
|
|
|
width: 110,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '型号规格',
|
|
|
|
|
|
dataIndex: 'modelSpec',
|
|
|
|
|
|
key: 'modelSpec',
|
|
|
|
|
|
width: 140,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '安装位置',
|
|
|
|
|
|
dataIndex: 'installLocation',
|
|
|
|
|
|
key: 'installLocation',
|
|
|
|
|
|
width: 200,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '状态',
|
|
|
|
|
|
dataIndex: 'status',
|
|
|
|
|
|
key: 'status',
|
|
|
|
|
|
width: 80,
|
|
|
|
|
|
render: (text) => {
|
|
|
|
|
|
const statusMap = {
|
|
|
|
|
|
'故障': { color: '#FF4D4F', bg: '#FFF2F0' },
|
|
|
|
|
|
'预警': { color: '#FAAD14', bg: '#FFF3E9' },
|
|
|
|
|
|
'正常': { color: '#44BB5F', bg: '#D8F7DE' }
|
|
|
|
|
|
};
|
|
|
|
|
|
const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' };
|
|
|
|
|
|
return (
|
|
|
|
|
|
<span style={{
|
|
|
|
|
|
color: status.color,
|
|
|
|
|
|
backgroundColor: status.bg,
|
|
|
|
|
|
padding: '2px 8px',
|
|
|
|
|
|
borderRadius: '4px',
|
|
|
|
|
|
fontSize: '12px'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
{text}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '最后维护',
|
|
|
|
|
|
dataIndex: 'lastMaintenance',
|
|
|
|
|
|
key: 'lastMaintenance',
|
|
|
|
|
|
width: 150,
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
title: '操作',
|
|
|
|
|
|
key: 'action',
|
|
|
|
|
|
width: 140,
|
|
|
|
|
|
render: (_, record) => (
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<Button type="link" size="small" style={{
|
|
|
|
|
|
padding: '2px 8px',
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
marginRight: 8,
|
|
|
|
|
|
border: '1px solid #E6E9FB',
|
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
|
borderRadius: '4px'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
编辑
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
<Button type="link" size="small" style={{
|
|
|
|
|
|
padding: '2px 8px',
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
border: '1px solid #E6E9FB',
|
|
|
|
|
|
backgroundColor: 'transparent',
|
|
|
|
|
|
borderRadius: '4px'
|
|
|
|
|
|
}}>
|
|
|
|
|
|
详情
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
),
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 模拟数据
|
|
|
|
|
|
const mockData = [
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '1',
|
|
|
|
|
|
id: '001',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼1层大厅',
|
|
|
|
|
|
status: '故障',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '2',
|
|
|
|
|
|
id: '002',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼3层 东区',
|
|
|
|
|
|
status: '预警',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '3',
|
|
|
|
|
|
id: '003',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '正常',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '4',
|
|
|
|
|
|
id: '004',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '故障',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '5',
|
|
|
|
|
|
id: '005',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '正常',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '6',
|
|
|
|
|
|
id: '006',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '预警',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '7',
|
|
|
|
|
|
id: '007',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '故障',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '8',
|
|
|
|
|
|
id: '008',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '正常',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '9',
|
|
|
|
|
|
id: '009',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '预警',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '10',
|
|
|
|
|
|
id: '010',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '故障',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '11',
|
|
|
|
|
|
id: '011',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '正常',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
key: '12',
|
|
|
|
|
|
id: '012',
|
|
|
|
|
|
deviceId: 'HQ-XF-01-001',
|
|
|
|
|
|
deviceName: '消防水泵',
|
|
|
|
|
|
modelSpec: 'XBD5.0/30-125',
|
|
|
|
|
|
installLocation: '总部大楼地下一层',
|
|
|
|
|
|
status: '预警',
|
|
|
|
|
|
lastMaintenance: '2025-09-10',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化数据
|
|
|
|
|
|
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,
|
|
|
|
|
|
}));
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className={styles.Rcontainer}>
|
|
|
|
|
|
{/* 第一个div - 高度20% */}
|
|
|
|
|
|
<div className={styles.RcontainerTop}>
|
|
|
|
|
|
<div className={styles.sectionContent}>
|
|
|
|
|
|
<div className={styles.blocksContainer}>
|
|
|
|
|
|
{/* 块1 */}
|
|
|
|
|
|
<div className={styles.blockItem}>
|
|
|
|
|
|
<div className={styles.blockLeft}>
|
|
|
|
|
|
<div className={styles.blockTitle}>设备总数</div>
|
|
|
|
|
|
<div className={styles.blockNumber}>1280</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.blockRight}>
|
|
|
|
|
|
<img src={eqicon1} alt="设备总数" className={styles.blockImage} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 块2 */}
|
|
|
|
|
|
<div className={styles.blockItem}>
|
|
|
|
|
|
<div className={styles.blockLeft}>
|
|
|
|
|
|
<div className={styles.blockTitle}>正常运行</div>
|
|
|
|
|
|
<div className={styles.blockNumber}>480</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.blockRight}>
|
|
|
|
|
|
<img src={eqicon2} alt="高风险设备" className={styles.blockImage} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 块3 */}
|
|
|
|
|
|
<div className={styles.blockItem}>
|
|
|
|
|
|
<div className={styles.blockLeft}>
|
|
|
|
|
|
<div className={styles.blockTitle}>需要维护</div>
|
|
|
|
|
|
<div className={styles.blockNumber}>347</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.blockRight}>
|
|
|
|
|
|
<img src={eqicon3} alt="今日预警次数" className={styles.blockImage} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 块4 */}
|
|
|
|
|
|
<div className={styles.blockItem}>
|
|
|
|
|
|
<div className={styles.blockLeft}>
|
|
|
|
|
|
<div className={styles.blockTitle}>故障设备</div>
|
|
|
|
|
|
<div className={styles.blockNumber}>289</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.blockRight}>
|
|
|
|
|
|
<img src={eqicon4} alt="未处理预警" className={styles.blockImage} />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.RcontainerMiddle}>
|
|
|
|
|
|
<div className={styles.sectionContent}>
|
|
|
|
|
|
<div className={styles.middleBlock1}>
|
|
|
|
|
|
<div className={styles.block1Header}>
|
|
|
|
|
|
<div className={styles.block1Title}>
|
|
|
|
|
|
<div className={styles.titleIcon}></div>
|
|
|
|
|
|
设备状态分布
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Segmented
|
|
|
|
|
|
className={styles.block1Segmented}
|
|
|
|
|
|
options={['月', '季', '年']}
|
|
|
|
|
|
onChange={(value) => {
|
|
|
|
|
|
console.log(value);
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
{/* 设备状态饼图 */}
|
|
|
|
|
|
<div className={styles.deviceStatusChart} ref={pieChartRef}>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.middleBlock12}>
|
|
|
|
|
|
<div className={styles.block1Header}>
|
|
|
|
|
|
<div className={styles.block1Title}>
|
|
|
|
|
|
<div className={styles.titleIcon}></div>
|
|
|
|
|
|
设备故障类型分布
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Select
|
|
|
|
|
|
className={styles.customSelect}
|
|
|
|
|
|
style={{
|
|
|
|
|
|
width: 120,
|
|
|
|
|
|
display: 'flex',
|
|
|
|
|
|
alignItems: 'center'
|
|
|
|
|
|
}}
|
|
|
|
|
|
defaultValue="全部区域"
|
|
|
|
|
|
options={[
|
|
|
|
|
|
{ value: '全部区域', label: '全部区域' },
|
|
|
|
|
|
{ value: '部分区域', label: '部分区域' },
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 设备故障类型饼图 */}
|
|
|
|
|
|
<div className={styles.deviceStatusChart} ref={faultPieChartRef}>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.middleBlock2}>
|
|
|
|
|
|
<div className={styles.middleBlock2Title}>
|
|
|
|
|
|
<div className={styles.titleLeft}>
|
|
|
|
|
|
<div className={styles.titleIcon}></div>
|
|
|
|
|
|
<div>设备运行参数</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.titleRight}>
|
|
|
|
|
|
|
|
|
|
|
|
<Select
|
|
|
|
|
|
style={{ width: 80 }}
|
|
|
|
|
|
defaultValue="今日"
|
|
|
|
|
|
options={[
|
|
|
|
|
|
{ value: '近3天', label: '近3天' },
|
|
|
|
|
|
{ value: '近7天', label: '近7天' },
|
|
|
|
|
|
]}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.middleBlock2Chart} ref={chartRef}>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 第三个div - 占满剩余位置 */}
|
|
|
|
|
|
<div className={styles.RcontainerBottom}>
|
|
|
|
|
|
<div className={styles.sectionContent}>
|
|
|
|
|
|
<div className={styles.leftBlock}>
|
|
|
|
|
|
{/* 第一行块 - 蓝色方块加标题 */}
|
|
|
|
|
|
<div className={styles.leftBlockTitle}>
|
|
|
|
|
|
<div className={styles.titleIcon}></div>
|
|
|
|
|
|
<div>预警信息</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.developmentContainer}>
|
|
|
|
|
|
<div className={styles.developmentBlock1}>
|
|
|
|
|
|
<div className={styles.leftContent}>
|
|
|
|
|
|
<div className={styles.mainText}>灭火器压力不足</div>
|
|
|
|
|
|
<div className={styles.subText}>2号楼3层 丨 15分钟前</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.rightContent}>
|
|
|
|
|
|
<div className={styles.importantTag}>重要</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.developmentBlock1}>
|
|
|
|
|
|
<div className={styles.leftContent}>
|
|
|
|
|
|
<div className={styles.mainText}>烟雾探测器电池低电量</div>
|
|
|
|
|
|
<div className={styles.subText}>1号楼5层 丨 1小时前</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.rightContent}>
|
|
|
|
|
|
<div className={styles.importantTag}>重要</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.developmentBlock1}>
|
|
|
|
|
|
<div className={styles.leftContent}>
|
|
|
|
|
|
<div className={styles.mainText}>消防栓维护到期</div>
|
|
|
|
|
|
<div className={styles.subText}>3号楼1层 丨 2小时前</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.rightContent}>
|
|
|
|
|
|
<div className={styles.normalTag}>一般</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.developmentBlock1}>
|
|
|
|
|
|
<div className={styles.leftContent}>
|
|
|
|
|
|
<div className={styles.mainText}>应急照明故障</div>
|
|
|
|
|
|
<div className={styles.subText}>地下停车场 丨 3小时前</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className={styles.rightContent}>
|
|
|
|
|
|
<div className={styles.normalTag}>一般</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.rightBlock}>
|
|
|
|
|
|
{/* 表格 */}
|
|
|
|
|
|
<div className={styles.tableHeader}>
|
|
|
|
|
|
<div className={styles.tableTitle}>
|
|
|
|
|
|
<div className={styles.titleIcon}></div>
|
|
|
|
|
|
<div>消防设备台账</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div className={styles.tableActions}>
|
|
|
|
|
|
<button className={styles.actionButton} onClick={handleAddDevice}>
|
|
|
|
|
|
<span className={styles.buttonIcon}>+</span>
|
|
|
|
|
|
<span>新增设备</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button className={styles.actionButton} onClick={handleExportData}>
|
|
|
|
|
|
<span className={styles.buttonIcon}><ExportOutlined /></span>
|
|
|
|
|
|
<span>导出数据</span>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 表格 */}
|
|
|
|
|
|
<div className={styles.tableContainer}>
|
|
|
|
|
|
<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} 条`,
|
|
|
|
|
|
}}
|
|
|
|
|
|
// scroll={{ x: 1200 }}
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default RiskAssessment;
|