You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

716 lines
29 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button } from 'antd';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './RealtimeMonitoring.less';
import alarm0 from '@/assets/safe_majorHazard/online_monitoring/alarm0.png';
import alarm1 from '@/assets/safe_majorHazard/online_monitoring/alarm1.png';
import alarm2 from '@/assets/safe_majorHazard/online_monitoring/alarm2.png';
import alarm3 from '@/assets/safe_majorHazard/online_monitoring/alarm3.png';
import exportIcon from '@/assets/safe_majorHazard/online_monitoring/export.png';
import deleteIcon from '@/assets/safe_majorHazard/online_monitoring/delete.png';
const RealtimeMonitoring = () => {
const chartRef = 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 (chartRef.current) {
const chart = echarts.init(chartRef.current);
const option = {
color: ['#04A7F3', '#E7C42C', '#EC6941'],
legend: {
data: ['液位', '温度', '压力'],
top: "-3px",
left: "center",
itemGap: 40, // 图例间距
textStyle: {
fontSize: 10
}
},
grid: {
left: '2%',
right: '4%',
bottom: '2%',
top: '12%',
containLabel: true
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00'],
axisLabel: {
fontSize: 10
}
},
yAxis: {
type: 'value',
min: 0,
max: 500,
axisLabel: {
formatter: '{value}',
fontSize: 10
}
},
series: [
{
name: '液位',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#04A7F3'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(4, 167, 243, 0.3)' },
{ offset: 1, color: 'rgba(4, 167, 243, 0)' }
]
}
},
symbol: 'none', // 不显示数据点
data: [120, 200, 150, 300, 250, 400, 350, 280, 320, 180, 220, 160, 140]
},
{
name: '温度',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#E7C42C'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(231, 196, 44, 0.3)' },
{ offset: 1, color: 'rgba(231, 196, 44, 0)' }
]
}
},
symbol: 'none',
data: [80, 120, 100, 180, 160, 220, 200, 150, 170, 90, 110, 85, 75]
},
{
name: '压力',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#EC6941'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 1,
x2: 0,
y2: 0,
colorStops: [
{ offset: 0, color: 'rgba(236, 105, 65, 0)' },
{ offset: 1, color: 'rgba(236, 105, 65, 0.3)' }
]
}
},
symbol: 'none',
data: [200, 300, 250, 450, 400, 430, 480, 420, 480, 280, 320, 260, 240]
}
]
};
chart.setOption(option);
// 响应式调整 - 使用ResizeObserver监听容器尺寸变化
let resizeTimer = null;
const handleResize = () => {
// 防抖处理避免频繁调用resize
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(() => {
chart.resize();
}, 100);
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题)
let resizeObserver = null;
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver(() => {
// 使用setTimeout确保DOM更新完成后再调整图表
setTimeout(() => {
handleResize();
}, 0);
});
resizeObserver.observe(chartRef.current);
}
return () => {
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
}
if (resizeTimer) {
clearTimeout(resizeTimer);
}
chart.dispose();
};
}
}, []);
// 表格列定义
const columns = [
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 80,
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: 'alarmTime',
key: 'alarmTime',
width: 150,
},
{
title: '报警传感器名称',
dataIndex: 'sensorName',
key: 'sensorName',
width: 150,
},
{
title: '报警类型',
dataIndex: 'alarmType',
key: 'alarmType',
width: 120,
},
{
title: '报警内容',
dataIndex: 'alarmContent',
key: 'alarmContent',
width: 200,
},
{
title: '优先级',
dataIndex: 'priority',
key: 'priority',
width: 80,
render: (text) => {
const colorMap = {
'高': '#FF4D4F',
'中': '#FAAD14',
'低': '#52C41A'
};
return <span style={{ color: colorMap[text] || '#333' }}>{text}</span>;
}
},
{
title: '处理状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (text) => {
const statusMap = {
'未处理': { color: '#FF4D4F', bg: '#FFF2F0' },
'处理中': { color: '#FAAD14', bg: '#FFFBE6' },
'已处理': { color: '#52C41A', bg: '#F6FFED' }
};
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: 'processTime',
key: 'processTime',
width: 150,
},
{
title: '处理人',
dataIndex: 'processor',
key: 'processor',
width: 100,
},
{
title: '操作',
key: 'action',
width: 120,
render: (_, record) => (
<div>
<Button type="link" size="small" style={{ padding: 0, marginRight: 8 }}>
查看
</Button>
</div>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
alarmTime: '2024-01-15 08:30:25',
sensorName: 'LNG储罐',
alarmType: '温度超限',
alarmContent: '储罐温度超过安全阈值',
priority: '高',
status: '未处理',
processTime: '-',
processor: '-',
},
{
key: '2',
id: '002',
alarmTime: '2024-01-15 09:15:10',
sensorName: 'LNG储罐',
alarmType: '压力异常',
alarmContent: '管道压力异常波动',
priority: '中',
status: '处理中',
processTime: '2024-01-15 09:20:00',
processor: '张三',
},
{
key: '3',
id: '003',
alarmTime: '2024-01-15 10:45:30',
sensorName: 'LNG储罐',
alarmType: '液位异常',
alarmContent: '储罐液位低于警戒线',
priority: '高',
status: '已处理',
processTime: '2024-01-15 11:00:15',
processor: '李四',
},
{
key: '4',
id: '004',
alarmTime: '2024-01-15 11:20:45',
sensorName: 'LNG储罐',
alarmType: '气体泄漏',
alarmContent: '检测到可燃气体泄漏',
priority: '高',
status: '未处理',
processTime: '-',
processor: '-',
},
{
key: '5',
id: '005',
alarmTime: '2024-01-15 12:10:20',
sensorName: 'LNG储罐',
alarmType: '设备振动',
alarmContent: '设备异常振动',
priority: '低',
status: '已处理',
processTime: '2024-01-15 12:30:00',
processor: '王五',
},
{
key: '6',
id: '006',
alarmTime: '2024-01-15 13:25:15',
sensorName: 'LNG管道',
alarmType: '流量异常',
alarmContent: '管道流量异常波动',
priority: '中',
status: '未处理',
processTime: '-',
processor: '-',
},
{
key: '7',
id: '007',
alarmTime: '2024-01-15 14:10:30',
sensorName: 'LNG储罐',
alarmType: '温度异常',
alarmContent: '储罐温度异常升高',
priority: '高',
status: '处理中',
processTime: '2024-01-15 14:15:00',
processor: '赵六',
},
{
key: '8',
id: '008',
alarmTime: '2024-01-15 15:45:20',
sensorName: 'LNG管道',
alarmType: '压力超限',
alarmContent: '管道压力超过安全阈值',
priority: '高',
status: '已处理',
processTime: '2024-01-15 16:00:00',
processor: '孙七',
},
{
key: '9',
id: '009',
alarmTime: '2024-01-15 16:30:45',
sensorName: 'LNG储罐',
alarmType: '液位超限',
alarmContent: '储罐液位超过警戒线',
priority: '中',
status: '未处理',
processTime: '-',
processor: '-',
},
{
key: '10',
id: '010',
alarmTime: '2024-01-15 17:15:10',
sensorName: 'LNG管道',
alarmType: '泄漏检测',
alarmContent: '检测到轻微气体泄漏',
priority: '低',
status: '已处理',
processTime: '2024-01-15 17:30:00',
processor: '周八',
},
{
key: '11',
id: '011',
alarmTime: '2024-01-15 18:20:35',
sensorName: 'LNG储罐',
alarmType: '设备故障',
alarmContent: '储罐阀门异常关闭',
priority: '高',
status: '处理中',
processTime: '2024-01-15 18:25:00',
processor: '吴九',
},
{
key: '12',
id: '012',
alarmTime: '2024-01-15 19:05:50',
sensorName: 'LNG管道',
alarmType: '温度异常',
alarmContent: '管道温度异常下降',
priority: '中',
status: '未处理',
processTime: '-',
processor: '-',
},
];
// 初始化数据
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 handleTableChange = (pagination) => {
setPagination(prev => ({
...prev,
current: pagination.current,
pageSize: pagination.pageSize,
}));
};
// 导出功能
const handleExport = () => {
console.log('导出数据');
// 这里可以添加导出逻辑
};
// 批量删除功能
const handleBatchDelete = () => {
if (selectedRowKeys.length === 0) {
console.log('没有选中任何行');
// 可以在这里添加提示用户选择行的逻辑
return;
}
console.log('批量删除', selectedRowKeys);
// 这里可以添加批量删除逻辑
};
return (
<div className={styles.realtimeContainer}>
<div className={styles.realtimeContainerTop}>
<div className={styles.realtimeContainerTopLeft}>
<div className={styles.realtimeContainerTopLeftTop}>
<div className={styles.realtimeAlarmO}>
<div className={styles.realtimeAlarmOLeft}>
<img style={{ width: 58, height: 47 }} src={alarm0} alt='alarm0' />
</div>
<div className={styles.realtimeAlarmORight}>
<div className={styles.realtimeAlarmORightText1}>总报警</div>
<div className={styles.realtimeAlarmORightText2}>1456</div>
<div className={styles.realtimeAlarmORightText3}>
<div>
未处理 <text style={{ color: '#FF4D4F' }}>6</text>
</div>
<div>
处理中 <text style={{ color: '#2e4cd4' }}>10</text>
</div>
</div>
</div>
</div>
<div className={styles.realtimeAlarmTw}>
<div className={styles.realtimeAlarmTwLeft}>
<img style={{ width: 58, height: 47 }} src={alarm1} alt='alarm1' />
</div>
<div className={styles.realtimeAlarmTwRight}>
<div className={styles.realtimeAlarmTwRightText1}>一级报警</div>
<div className={styles.realtimeAlarmTwRightText2}>357</div>
<div className={styles.realtimeAlarmTwRightText3}>
<div>
未处理 <text style={{ color: '#FF4D4F' }}>6</text>
</div>
<div>
处理中 <text style={{ color: '#2e4cd4' }}>10</text>
</div>
</div>
</div>
</div>
<div className={styles.realtimeAlarmTh}>
<div className={styles.realtimeAlarmThLeft}>
<img style={{ width: 58, height: 47 }} src={alarm2} alt='alarm2' />
</div>
<div className={styles.realtimeAlarmThRight}>
<div className={styles.realtimeAlarmThRightText1}>二级报警</div>
<div className={styles.realtimeAlarmThRightText2}>401</div>
<div className={styles.realtimeAlarmThRightText3}>
<div>
未处理 <text style={{ color: '#FF4D4F' }}>6</text>
</div>
<div>
处理中 <text style={{ color: '#2e4cd4' }}>10</text>
</div>
</div>
</div>
</div>
<div className={styles.realtimeAlarmF}>
<div className={styles.realtimeAlarmFLeft}>
<img style={{ width: 58, height: 47 }} src={alarm3} alt='alarm3' />
</div>
<div className={styles.realtimeAlarmFRight}>
<div className={styles.realtimeAlarmFRightText1}>三级报警</div>
<div className={styles.realtimeAlarmFRightText2}>556</div>
<div className={styles.realtimeAlarmFRightText3}>
<div>
未处理 <text style={{ color: '#FF4D4F' }}>6</text>
</div>
<div>
处理中 <text style={{ color: '#2e4cd4' }}>10</text>
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeContainerTopLeftBottom}>
<div className={styles.realtimeContainerTopLeftBottomTitle}>
<div className={styles.realtimeTitleLeft}>
<div className={styles.realtimeTitleIcon}></div>
<div>预警看板</div>
</div>
<div className={styles.realtimeTitleRight}>
<div>检测对象</div>
<Select
style={{ width: 80 }}
defaultValue="储罐"
options={[
{ value: '储罐', label: '储罐' },
{ value: '管道', label: '管道' },
{ value: '设备', label: '设备' }
]}
/>
</div>
</div>
<div className={styles.realtimeContainerTopLeftBottomChart} ref={chartRef}>
</div>
</div>
</div>
<div className={styles.realtimeContainerTopRight}>
<div className={styles.realtimeDataHeader}>
<div className={styles.realtimeTitleLeft}>
<div className={styles.realtimeTitleIcon}></div>
<div>实时数据采集</div>
</div>
<div className={styles.realtimeTotalCount}>
总数 <text style={{ color: '#2e4cd4' }}>1378</text>
</div>
</div>
<div className={styles.realtimeDataItem1}>
<div className={styles.realtimeDataItemLeft}>
<div className={styles.realtimeAreaName}>储罐液化装置区</div>
<div className={styles.realtimeRValue}>R值: 1765</div>
<div className={styles.realtimeCodeNumber}>编号:XXXXXXXX</div>
</div>
<div className={styles.realtimeDataItemRight}>
<div className={styles.realtimeCircleContainer}>
<div className={styles.realtimeOuterCircle}>
<div className={styles.realtimeInnerCircle}>
<div className={styles.realtimeLevelText}>三级</div>
<div className={styles.realtimeRiskText}>危险等级</div>
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeDataItem2}>
<div className={styles.realtimeDataItemLeft}>
<div className={styles.realtimeAreaName}>储罐液化装置区</div>
<div className={styles.realtimeRValue}>R值: 1765</div>
<div className={styles.realtimeCodeNumber}>编号:XXXXXXXX</div>
</div>
<div className={styles.realtimeDataItemRight}>
<div className={styles.realtimeCircleContainer}>
<div className={styles.realtimeOuterCircle}>
<div className={styles.realtimeInnerCircle}>
<div className={styles.realtimeLevelText}>一级</div>
<div className={styles.realtimeRiskText}>危险等级</div>
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeDataItem3}>
<div className={styles.realtimeDataItemLeft}>
<div className={styles.realtimeAreaName}>储罐液化装置区</div>
<div className={styles.realtimeRValue}>R值: 1765</div>
<div className={styles.realtimeCodeNumber}>编号:XXXXXXXX</div>
</div>
<div className={styles.realtimeDataItemRight}>
<div className={styles.realtimeCircleContainer}>
<div className={styles.realtimeOuterCircle}>
<div className={styles.realtimeInnerCircle}>
<div className={styles.realtimeLevelText}>二级</div>
<div className={styles.realtimeRiskText}>危险等级</div>
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeDataItem4}>
<div className={styles.realtimeDataItemLeft}>
<div className={styles.realtimeAreaName}>储罐液化装置区</div>
<div className={styles.realtimeRValue}>R值: 1765</div>
<div className={styles.realtimeCodeNumber}>编号:XXXXXXXX</div>
</div>
<div className={styles.realtimeDataItemRight}>
<div className={styles.realtimeCircleContainer}>
<div className={styles.realtimeOuterCircle}>
<div className={styles.realtimeInnerCircle}>
<div className={styles.realtimeLevelText}>三级</div>
<div className={styles.realtimeRiskText}>危险等级</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* 表格 */}
<div className={styles.realtimeContainerBottom}>
{/* 首行 左侧标题左对齐 右侧按钮右对齐 */}
<div className={styles.realtimeTableHeader}>
<div className={styles.realtimeTableTitle}>
<div className={styles.realtimeTitleIcon}></div>
<div>报警信息列表</div>
</div>
<div className={styles.realtimeTableActions}>
<Button
type="primary"
onClick={handleExport}
style={{ marginRight: 8 }}
>
<img src={exportIcon} alt="导出" style={{ width: 16, height: 16, margin: '-2px 6px 0 0px'}} />
导出word 报告
</Button>
<Button
type="primary"
onClick={handleBatchDelete}
>
<img src={deleteIcon} alt="删除" style={{ width: 16, height: 16, margin: '-2px 6px 0 0px' }} />
批量删除
</Button>
</div>
</div>
{/* 表格 5行10列 带页码 每页5条数据 */}
<div className={styles.realtimeTableContainer}>
<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>
);
};
export default RealtimeMonitoring;