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.

720 lines
28 KiB
JavaScript

3 months ago
3 months ago
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button } from 'antd';
3 months ago
import { CheckCircleOutlined } from '@ant-design/icons';
3 months ago
import * as echarts from 'echarts';
3 months ago
import StandardTable from '@/components/StandardTable';
3 months ago
import styles from './RiskAssessment.less';
3 months ago
// import './RiskAssessment.less';
3 months ago
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';
3 months ago
import map1 from '@/assets/safe_majorHazard/online_monitoring/map.png';
3 months ago
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';
3 months ago
const RiskAssessment = () => {
3 months ago
const chartRef = useRef(null);
3 months ago
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,
});
3 months ago
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
// 强制初始化时调整大小
setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 100);
const option = {
color: ['#FF2526', '#FF8800', '#FFC403', '#65E5F9'],
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: ['9/22', '9/23', '9/24', '9/25', '9/26', '9/27', '9/28'],
axisLabel: {
fontSize: 10
}
},
yAxis: {
type: 'value',
min: 0,
max: 30,
axisLabel: {
formatter: '{value}',
fontSize: 10
}
},
series: [
{
name: '重大风险',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#FF2526'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(38, 12, 12, 0.4)' },
{ offset: 1, color: 'rgba(255, 37, 38, 0)' }
]
}
},
symbol: 'none',
data: [8, 15, 12, 22, 18, 26, 20]
},
{
name: '较高风险',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#FF8800'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(255, 136, 0, 0.4)' },
{ offset: 1, color: 'rgba(255, 136, 0, 0)' }
]
}
},
symbol: 'none',
data: [5, 8, 6, 12, 10, 15, 13]
},
{
name: '一般风险',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#FFC403'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(255, 196, 3, 0.4)' },
{ offset: 1, color: 'rgba(255, 196, 3, 0)' }
]
}
},
symbol: 'none',
data: [12, 18, 15, 25, 22, 24, 26]
},
{
name: '低风险',
type: 'line',
smooth: true,
lineStyle: {
width: 1.5,
color: '#65E5F9'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(101, 229, 249, 0.4)' },
{ offset: 1, color: 'rgba(101, 229, 249, 0)' }
]
}
},
symbol: 'none',
data: [3, 5, 7, 9, 6, 8, 4]
}
]
};
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();
}
};
}
}, []);
3 months ago
// 表格列定义
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: 'deviceId',
key: 'deviceId',
width: 120,
},
{
title: '风险等级',
dataIndex: 'riskLevel',
key: 'riskLevel',
width: 100,
render: (text) => {
const colorMap = {
'高': '#FF4D4F',
'中': '#FAAD14',
'低': '#52C41A'
};
return <span style={{ color: colorMap[text] || '#333' }}>{text}</span>;
}
},
{
title: '预警时间',
dataIndex: 'warningTime',
key: 'warningTime',
width: 150,
},
{
title: '主要原因',
dataIndex: 'mainReason',
key: 'mainReason',
width: 200,
},
{
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: '操作',
key: 'action',
width: 120,
render: (_, record) => (
<div>
<Button type="link" size="small" style={{ padding: 0, marginRight: 8 }}>
查看
</Button>
<Button type="link" size="small" style={{ padding: 0 }}>
详情
</Button>
</div>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
deviceId: 'DEV-001',
riskLevel: '高',
warningTime: '2024-01-15 08:30:25',
mainReason: '设备温度异常升高',
status: '未处理',
},
{
key: '2',
id: '002',
deviceId: 'DEV-002',
riskLevel: '中',
warningTime: '2024-01-15 09:15:10',
mainReason: '压力波动超出正常范围',
status: '处理中',
},
{
key: '3',
id: '003',
deviceId: 'DEV-003',
riskLevel: '高',
warningTime: '2024-01-15 10:45:30',
mainReason: '液位传感器故障',
status: '已处理',
},
{
key: '4',
id: '004',
deviceId: 'DEV-004',
riskLevel: '高',
warningTime: '2024-01-15 11:20:45',
mainReason: '检测到气体泄漏',
status: '未处理',
},
{
key: '5',
id: '005',
deviceId: 'DEV-005',
riskLevel: '低',
warningTime: '2024-01-15 12:10:20',
mainReason: '设备振动异常',
status: '已处理',
},
{
key: '6',
id: '006',
deviceId: 'DEV-006',
riskLevel: '中',
warningTime: '2024-01-15 13:25:15',
mainReason: '流量传感器读数异常',
status: '未处理',
},
{
key: '7',
id: '007',
deviceId: 'DEV-007',
riskLevel: '高',
warningTime: '2024-01-15 14:10:30',
mainReason: '储罐压力超限',
status: '处理中',
},
{
key: '8',
id: '008',
deviceId: 'DEV-008',
riskLevel: '中',
warningTime: '2024-01-15 15:45:20',
mainReason: '管道温度异常',
status: '已处理',
},
{
key: '9',
id: '009',
deviceId: 'DEV-009',
riskLevel: '高',
warningTime: '2024-01-15 16:30:45',
mainReason: '阀门控制系统故障',
status: '未处理',
},
{
key: '10',
id: '010',
deviceId: 'DEV-010',
riskLevel: '低',
warningTime: '2024-01-15 17:15:10',
mainReason: '轻微泄漏检测',
status: '已处理',
},
{
key: '11',
id: '011',
deviceId: 'DEV-011',
riskLevel: '高',
warningTime: '2024-01-15 18:20:35',
mainReason: '安全阀异常开启',
status: '处理中',
},
{
key: '12',
id: '012',
deviceId: 'DEV-012',
riskLevel: '中',
warningTime: '2024-01-15 19:05:50',
mainReason: '液位计读数不稳定',
status: '未处理',
},
];
// 初始化数据
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,
}));
};
3 months ago
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}>65</div>
<div className={styles.blockChange}>
<span className={styles.arrow}></span>
较昨日 +2
</div>
</div>
<div className={styles.blockRight}>
<img src={img1} alt="总危险源数量" className={styles.blockImage} />
</div>
</div>
{/* 块2 */}
<div className={styles.blockItem}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>高风险设备</div>
<div className={styles.blockNumber}>65</div>
<div className={styles.blockChange}>
<span className={styles.arrow}></span>
较昨日 +2
</div>
</div>
<div className={styles.blockRight}>
<img src={img2} alt="高风险设备" className={styles.blockImage} />
</div>
</div>
{/* 块3 */}
<div className={styles.blockItem}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>今日预警次数</div>
<div className={styles.blockNumber}>65</div>
<div className={styles.blockChange}>
<span className={styles.arrow}></span>
较昨日 +2
</div>
</div>
<div className={styles.blockRight}>
<img src={img3} alt="今日预警次数" className={styles.blockImage} />
</div>
</div>
{/* 块4 */}
<div className={styles.blockItem}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>未处理预警</div>
<div className={styles.blockNumber}>65</div>
<div className={styles.blockChange}>
<span className={styles.arrow}></span>
较昨日 +2
</div>
</div>
<div className={styles.blockRight}>
<img src={img1} alt="未处理预警" className={styles.blockImage} />
</div>
</div>
{/* 块5 */}
<div className={styles.blockItem}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>已处理预警</div>
<div className={styles.blockNumber}>65</div>
<div className={styles.blockChange}>
<CheckCircleOutlined className={styles.checkIcon} />
已完成
</div>
</div>
<div className={styles.blockRight}>
<img src={img2} 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>
<Select
className={styles.block1Select}
defaultValue="全部区域"
options={[
{ value: '全部区域', label: '全部区域' },
{ value: '区域A', label: '区域A' },
{ value: '区域B', label: '区域B' },
{ value: '区域C', label: '区域C' }
]}
/>
</div>
3 months ago
{/* 风险等级图例 */}
<div className={styles.riskLegend}>
<div className={styles.legendItem}>
<div className={styles.legendDot} style={{ backgroundColor: '#F53F3F' }}></div>
<span className={styles.legendText}>重大风险</span>
</div>
<div className={styles.legendItem}>
<div className={styles.legendDot} style={{ backgroundColor: '#FF7D00' }}></div>
<span className={styles.legendText}>较大风险</span>
</div>
<div className={styles.legendItem}>
<div className={styles.legendDot} style={{ backgroundColor: '#FFAA01' }}></div>
<span className={styles.legendText}>一般风险</span>
</div>
<div className={styles.legendItem}>
<div className={styles.legendDot} style={{ backgroundColor: '#65E5F9' }}></div>
<span className={styles.legendText}>低风险</span>
</div>
</div>
3 months ago
<div className={styles.block1Chart}>
3 months ago
<img src={map1} alt="地图" className={styles.mapImage} />
3 months ago
</div>
</div>
<div className={styles.middleBlock2}>
3 months ago
<div className={styles.middleBlock2Title}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>风险等级趋势</div>
</div>
<div className={styles.titleRight}>
<Select
style={{ width: 100 }}
defaultValue="近七天"
options={[
{ value: '近三天', label: '近3天' },
{ value: '近30天', label: '近30天' },
]}
/>
</div>
</div>
<div className={styles.middleBlock2Chart} ref={chartRef}>
</div>
3 months ago
</div>
</div>
</div>
3 months ago
{/* 第三个div - 占满剩余位置 */}
<div className={styles.RcontainerBottom}>
<div className={styles.sectionContent}>
<div className={styles.leftBlock}>
3 months ago
{/* 第一行块 - 蓝色方块加标题 */}
<div className={styles.leftBlockTitle}>
<div className={styles.titleIcon}></div>
<div>风险评估参数</div>
</div>
{/* 第二行块 - 图片 */}
<div className={styles.leftBlockImage}>
3 months ago
{/* <img src={risk2} alt="风险评估图" style={{ width: '40%', height: '40%', objectFit: 'cover' }} /> */}
3 months ago
</div>
{/* 第三行块 */}
<div className={styles.leftBlockItem}>
<div className={styles.itemTitle}>风险等级</div>
<div className={styles.itemValue}>高风险</div>
</div>
{/* 第四行块 */}
<div className={styles.leftBlockItem}>
<div className={styles.itemTitle}>评估时间</div>
<div className={styles.itemValue}>2024-01-15</div>
</div>
{/* 第五行块 */}
<div className={styles.leftBlockItem}>
<div className={styles.itemTitle}>评估人员</div>
<div className={styles.itemValue}>张三</div>
</div>
3 months ago
</div>
3 months ago
3 months ago
<div className={styles.rightBlock}>
3 months ago
{/* 表格 */}
<div className={styles.tableHeader}>
<div className={styles.tableTitle}>
<div className={styles.titleIcon}></div>
<div>最新预警信息</div>
</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>
3 months ago
</div>
</div>
</div>
3 months ago
</div>
);
};
export default RiskAssessment;