重点部位基础信息管理和应急演练记录关联管理页面开发

main
jiangxucong 2 months ago
parent c8fe3cfdad
commit 492b4f70b2

@ -30,6 +30,12 @@ export default [
name: 'basic', name: 'basic',
component: './business_basic/basic', component: './business_basic/basic',
}, },
// 消防重点部位管理
{
path: '/topnavbar00/business/firekeynotearea',
name: 'firekeynotearea',
component: './business_firekeynotearea/FireKeynoteArea',
},
// 安全管理基础信息 // 安全管理基础信息
{ {
path: '/topnavbar00/business/basicinformation', path: '/topnavbar00/business/basicinformation',

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

@ -0,0 +1,10 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1365_19978)">
<path d="M13.7011 12.2837C15.0335 12.7693 15.875 13.4844 15.875 14.2812C15.875 15.7475 13.0211 16.9375 9.5 16.9375C5.97888 16.9375 3.125 15.7475 3.125 14.2812C3.125 13.4844 3.9665 12.7704 5.29888 12.2837C5.87687 12.9733 6.56644 13.6788 7.36756 14.4003C7.95273 14.9271 8.71229 15.2185 9.49967 15.2183C10.2871 15.2181 11.0465 14.9263 11.6314 14.3992C12.4304 13.6788 13.1189 12.9754 13.6958 12.2869L13.7011 12.2837ZM9.5 2.0625C12.4336 2.0625 14.8125 4.1875 14.8125 7.375C14.8125 9.194 13.5152 11.2723 10.9206 13.6097C10.5308 13.9613 10.0246 14.1559 9.49967 14.1561C8.97479 14.1563 8.46844 13.962 8.07838 13.6108C5.48588 11.2723 4.1875 9.194 4.1875 7.375C4.1875 4.1875 6.56644 2.0625 9.5 2.0625ZM9.5 5.25C8.93641 5.25 8.39591 5.47388 7.9974 5.8724C7.59888 6.27091 7.375 6.81141 7.375 7.375C7.375 7.93859 7.59888 8.47909 7.9974 8.8776C8.39591 9.27612 8.93641 9.5 9.5 9.5C10.0636 9.5 10.6041 9.27612 11.0026 8.8776C11.4011 8.47909 11.625 7.93859 11.625 7.375C11.625 6.81141 11.4011 6.27091 11.0026 5.8724C10.6041 5.47388 10.0636 5.25 9.5 5.25Z" fill="#2E4CD4"/>
</g>
<defs>
<clipPath id="clip0_1365_19978">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@ -0,0 +1,15 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1365_19988)">
<g clip-path="url(#clip1_1365_19988)">
<path d="M13.7011 13.2837C15.0335 13.7693 15.875 14.4844 15.875 15.2812C15.875 16.7475 13.0211 17.9375 9.5 17.9375C5.97888 17.9375 3.125 16.7475 3.125 15.2812C3.125 14.4844 3.9665 13.7704 5.29888 13.2837C5.87687 13.9733 6.56644 14.6788 7.36756 15.4003C7.95273 15.9271 8.71229 16.2185 9.49967 16.2183C10.2871 16.2181 11.0465 15.9263 11.6314 15.3992C12.4304 14.6788 13.1189 13.9754 13.6958 13.2869L13.7011 13.2837ZM9.5 3.0625C12.4336 3.0625 14.8125 5.1875 14.8125 8.375C14.8125 10.194 13.5152 12.2723 10.9206 14.6097C10.5308 14.9613 10.0246 15.1559 9.49967 15.1561C8.97479 15.1563 8.46844 14.962 8.07838 14.6108C5.48588 12.2723 4.1875 10.194 4.1875 8.375C4.1875 5.1875 6.56644 3.0625 9.5 3.0625ZM9.5 6.25C8.93641 6.25 8.39591 6.47388 7.9974 6.8724C7.59888 7.27091 7.375 7.81141 7.375 8.375C7.375 8.93859 7.59888 9.47909 7.9974 9.8776C8.39591 10.2761 8.93641 10.5 9.5 10.5C10.0636 10.5 10.6041 10.2761 11.0026 9.8776C11.4011 9.47909 11.625 8.93859 11.625 8.375C11.625 7.81141 11.4011 7.27091 11.0026 6.8724C10.6041 6.47388 10.0636 6.25 9.5 6.25Z" fill="#C5C9D2"/>
</g>
</g>
<defs>
<clipPath id="clip0_1365_19988">
<rect width="18" height="18" fill="white"/>
</clipPath>
<clipPath id="clip1_1365_19988">
<rect width="18" height="18" fill="white" transform="translate(0 1)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,15 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1365_20013)">
<mask id="mask0_1365_20013" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="1" width="18" height="16">
<path d="M17.0984 1.80078H0.898438V16.2008H17.0984V1.80078Z" fill="white"/>
</mask>
<g mask="url(#mask0_1365_20013)">
<path d="M15.4755 4.50146H9.8099L8.67288 2.61098C8.35219 2.07085 7.86628 1.80078 7.29292 1.80078H2.52135C1.62729 1.80078 0.898438 2.61098 0.898438 3.60483V14.4075C0.898438 15.4014 1.62729 16.2116 2.52135 16.2116H15.4755C16.3696 16.2116 17.0984 15.4014 17.0984 14.4075V6.30551C17.0984 5.31166 16.3696 4.50146 15.4755 4.50146ZM12.1034 12.7115H5.8838C5.45621 12.7115 5.10636 12.3226 5.10636 11.8473C5.10636 11.372 5.45621 10.9831 5.8838 10.9831H12.1034C12.531 10.9831 12.8808 11.372 12.8808 11.8473C12.8808 12.3226 12.5407 12.7115 12.1034 12.7115Z" fill="#C5C9D2"/>
</g>
</g>
<defs>
<clipPath id="clip0_1365_20013">
<rect width="18" height="18" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.9347 9.53008V10.2426C13.9241 10.2773 13.9111 10.3119 13.904 10.3473C13.8312 10.7239 13.7902 11.1103 13.6803 11.4758C13.3545 12.5542 12.744 13.453 11.8882 14.1867C11.2281 14.7531 10.4853 15.1612 9.63609 15.362C9.35687 15.4282 9.07055 15.4672 8.78733 15.5188H8.07523C8.04055 15.5081 8.00507 15.492 7.96969 15.4896C7.21773 15.4428 6.50523 15.247 5.83687 14.9055C4.57335 14.262 3.66195 13.3037 3.24569 11.9252C3.15703 11.6306 3.12007 11.3217 3.05859 11.0193V10.3395C3.08539 10.1724 3.10859 10.0039 3.14015 9.83696C3.27483 9.1075 3.59899 8.46156 4.02827 7.86524C4.43319 7.30398 4.86327 6.76048 5.24727 6.185C5.66241 5.56078 5.86327 4.8679 5.74555 4.1082C5.72235 3.95812 5.70063 3.80806 5.67781 3.65876C5.69313 3.65484 5.70851 3.65172 5.72391 3.64734C6.65063 4.6186 6.80149 5.81086 6.66913 7.09922C6.70305 7.06298 6.73141 7.03774 6.75305 7.0086C7.53961 5.94478 7.87749 4.76954 7.62109 3.45318C7.45813 2.61584 7.10555 1.8631 6.52227 1.23732C6.26897 0.96634 5.96255 0.74462 5.68013 0.5H5.97161C5.99561 0.50984 6.01811 0.5268 6.04247 0.52914C6.55529 0.5732 7.03701 0.73156 7.49591 0.95414C8.54545 1.4622 9.39623 2.21328 10.108 3.12946C11.1541 4.47758 11.8343 5.97312 11.969 7.69508C12.0206 8.35242 11.9536 8.99126 11.7784 9.62148C11.7122 9.86016 11.6188 10.0909 11.5334 10.3394C11.8728 10.1949 12.1309 9.97398 12.3302 9.68764C12.9245 8.83178 13.0907 7.8774 12.956 6.82232C12.9998 6.8731 13.0222 6.89318 13.0375 6.91834C13.3955 7.48396 13.6634 8.08732 13.8032 8.7431C13.8587 9.00266 13.891 9.2682 13.9347 9.53008ZM8.33553 8.27524C8.32217 8.2847 8.30755 8.29454 8.29381 8.304C8.27529 8.564 8.26147 8.82432 8.23631 9.08424C8.19927 9.48088 8.10435 9.8633 7.91881 10.219C7.69193 10.6546 7.31271 10.9256 6.89239 11.141C6.43911 11.3749 6.15357 11.709 6.11653 12.2364C6.07795 12.7787 6.24099 13.2343 6.67817 13.5676C7.08779 13.8795 7.56631 13.9812 8.07357 13.9643C8.88341 13.9379 9.48677 13.553 9.72865 12.7425C9.90233 12.1607 9.87397 11.575 9.75779 10.9894C9.59903 10.1867 9.30833 9.4384 8.81091 8.78262C8.67115 8.59938 8.49467 8.44382 8.33553 8.27524Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@ -0,0 +1,56 @@
import React, { useState } from 'react';
import { Card, Row, Col, Statistic, Progress, Button, Space } from 'antd';
import styles from './FireKeynoteArea.less';
import KeypartsBasicInformation from './components/KeypartsBasicInformation'; //重点部位基础信息管理
import EmergencyPlanAssociation from './components/EmergencyPlanAssociation'; //应急预案关联管理
import EmergencyDrillRecordAssociation from './components/EmergencyDrillRecordAssociation'; //应急演练记录关联管理
const FireKeynoteArea = () => {
const [activeModule, setActiveModule] = useState('1');
const handleModuleClick = (module) => {
setActiveModule(module)
}
const renderModule = () => {
switch (activeModule) {
case '1':
return <KeypartsBasicInformation />;
case '2':
return <EmergencyPlanAssociation />;
case '3':
return <EmergencyDrillRecordAssociation />;
default:
return <KeypartsBasicInformation />;
}
};
return (
<div className={styles.container}>
<div className={styles.TopButton}>
<Button
className={`${styles.TopButtonItem} ${activeModule === "1" ? styles.active : ""}`}
onClick={() => handleModuleClick("1")}
>重点部位基础信息管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "2" ? styles.active : ""}`}
onClick={() => handleModuleClick("2")}
>应急预案关联管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "3" ? styles.active : ""}`}
onClick={() => handleModuleClick("3")}
>应急演练记录关联管理
</Button>
</div>
<div className={styles.content}>
{renderModule()}
</div>
</div>
);
};
export default FireKeynoteArea;

@ -0,0 +1,66 @@
.container {
background-color: transparent;
width: 100%;
height: 89vh;
overflow: hidden;
display: flex;
flex-direction: column;
.TopButton {
background-color: white;
width: 100%;
padding: 10px 30px;
display: flex;
gap: 24px;
margin-left: 6px;
.TopButtonItem {
background-color: transparent !important;
color: #333333 !important;
font-family: 'PingFang SC', sans-serif !important;
font-weight: 500 !important;
font-size: 14px !important;
line-height: 100% !important;
border-radius: 8px !important;
padding: 6px 10px !important;
height: auto !important;
border: none !important;
box-shadow: none !important;
position: relative !important;
&:hover {
color: #333333 !important;
border: none !important;
}
&:focus {
color: #2E4CD4 !important;
border: none !important;
}
&.active {
color: #2E4CD4 !important;
&::after {
content: '';
position: absolute;
bottom: -10px;
left: 0;
right: 0;
width: 100%;
height: 4px;
background-color: #2E4CD4;
border-radius: 0;
opacity: 1;
}
}
}
}
.content {
// ======== 内容区域样式 ========
flex: 1; // ======== 占据剩余空间 ========
overflow-y: auto; // ======== 允许垂直滚动 ========
padding: 0; // ======== 无内边距 ========
}
}

@ -0,0 +1,749 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button, Segmented, Input } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './EmergencyDrillRecordAssociation.less';
import eqicon1 from '@/assets/business_firekeynotearea/eqicon1.png';
import eqicon6 from '@/assets/business_firekeynotearea/eqicon6.png';
import eqicon7 from '@/assets/business_firekeynotearea/eqicon7.png';
import eqicon8 from '@/assets/business_firekeynotearea/eqicon8.png';
const EmergencyDrillRecordAssociation = () => {
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: 13,
itemHeight: 4,
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: 'drillId',
key: 'drillId',
width: 140,
},
{
title: '演练时间',
dataIndex: 'drillTime',
key: 'drillTime',
width: 180,
},
{
title: '演练类型',
dataIndex: 'drillType',
key: 'drillType',
width: 120,
},
{
title: '关联部位',
dataIndex: 'relatedPart',
key: 'relatedPart',
width: 120,
},
{
title: '部位名称',
dataIndex: 'partName',
key: 'partName',
width: 160,
},
{
title: '演练次数',
dataIndex: 'drillCount',
key: 'drillCount',
width: 100,
},
{
title: '参与人数',
dataIndex: 'participantCount',
key: 'participantCount',
width: 100,
},
{
title: '效果评估',
dataIndex: 'effect',
key: 'effect',
width: 120,
render: (text) => {
let color = '#FFF3E9', fontColor = '#FF8800', label = text;
if (text === '优秀') {
color = '#D8F7DE';
fontColor = '#44BB5F';
}
return (
<span style={{
background: color,
color: fontColor,
borderRadius: 5,
padding: '3px 8px',
fontSize: 12,
fontWeight: 500
}}>{label}</span>
);
}
},
{
title: '操作',
key: 'action',
width: 120,
render: (_, record) => (
<>
<span style={{ color: '#2E4CD4', cursor: 'pointer', marginRight: 16 }}>编辑</span>
<span style={{ color: '#FF2526', cursor: 'pointer' }}>删除</span>
</>
),
},
];
// 表格数据(同步图片)
const mockData = [
{
key: '1',
drillId: 'YL202310001',
drillTime: '2025-09-10 14:23:45',
drillType: '实战演练',
relatedPart: '三楼东侧',
partName: '数据机房 A区',
drillCount: 5,
participantCount: 25,
effect: '良好',
},
{
key: '2',
drillId: 'YL202310002',
drillTime: '2025-09-10 14:23:45',
drillType: '桌面推演',
relatedPart: '二楼西侧',
partName: 'B区厨房',
drillCount: 6,
participantCount: 18,
effect: '良好',
},
{
key: '3',
drillId: 'YL202310003',
drillTime: '2025-09-10 14:23:45',
drillType: '模拟演练',
relatedPart: '地下一层',
partName: '数据中心机房',
drillCount: 3,
participantCount: 32,
effect: '优秀',
},
];
// 初始化数据
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 keypartsData = [
{
name: '配电房',
location: '1楼 - 东区',
risk: '高风险',
riskColor: '#FFE0E2',
riskTextColor: '#FF3E48',
},
{
name: '消防控制室',
location: '1楼 - 西区',
risk: '中风险',
riskColor: '#FFF8E2',
riskTextColor: '#FFC403',
},
{
name: '档案室',
location: '2楼 - 中区',
risk: '低风险',
riskColor: '#DAF3FF',
riskTextColor: '#00AAFA',
}
];
const [selectedKeypartIdx, setSelectedKeypartIdx] = useState(0);
return (
<div className={styles.Rcontainer}>
{/* 第一个div - 高度20% */}
<div className={styles.RcontainerTop}>
<div className={styles.sectionContent}>
<div className={styles.blocksContainer}>
{/* 块1 */}
<div className={styles.blockItem + ' ' + styles.bgBlock1}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>演练记录总数</div>
<div className={styles.blockNumber}>120</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon1} alt="演练记录总数" className={styles.blockImage} />
</div>
</div>
{/* 块2 */}
<div className={styles.blockItem + ' ' + styles.bgBlock2}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>重点部位数量</div>
<div className={styles.blockNumber}>32</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon6} alt="重点部位数量" className={styles.blockImage} />
</div>
</div>
{/* 块3 */}
<div className={styles.blockItem + ' ' + styles.bgBlock3}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>参与演练人员</div>
<div className={styles.blockNumber}>69</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon7} alt="参与演练人员" className={styles.blockImage} />
</div>
</div>
{/* 块4 */}
<div className={styles.blockItem + ' ' + styles.bgBlock4}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>平均演练达标率</div>
<div className={styles.blockNumber}>72%</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon8} alt="平均演练达标率" className={styles.blockImage} />
</div>
</div>
</div>
</div>
</div>
<div className={styles.RcontainerMiddle}>
<div className={styles.sectionContent}>
<div className={styles.middleBlock2}>
<div className={styles.middleBlock2Title}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>重点部位分布图</div>
</div>
<div className={styles.titleRight}>
实时定位
</div>
</div>
<div className={styles.middleBlock2Chart}>
{/* 地图图片 */}
<img
src={require('@/assets/business_firekeynotearea/map.png')}
alt="重点部位分布图"
style={{ width: '100%', height: '100%' }}
/>
</div>
</div>
<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.middleBlock1}>
<div className={styles.block1Header}>
<div className={styles.block1Title}>
<div className={styles.titleIcon}></div>
重点部位列表
</div>
<div> 3 </div>
</div>
{/* 重点部位列表 */}
<div className={styles.keypartsList}>
{keypartsData.map((item, idx) => (
<div
key={item.name}
className={styles.keypartsCard + ' ' + (selectedKeypartIdx === idx ? styles.keypartsCardActive : '')}
onClick={() => setSelectedKeypartIdx(idx)}
style={{ cursor: 'pointer' }}
>
<div>
<div className={styles.keypartsCardHeader}>
<span className={styles.keypartsCardTitle}>{item.name}</span>
</div>
<div className={styles.keypartsCardSub}>{item.location}</div>
</div>
<div
className={styles.keypartsRiskTag}
style={{ background: item.riskColor, color: item.riskTextColor }}
>
{item.risk}
</div>
</div>
))}
</div>
</div>
</div>
</div>
{/* 第三个div - 占满剩余位置 */}
<div className={styles.RcontainerBottom}>
<div className={styles.sectionContent}>
<div className={styles.tableBlock}>
{/* 表格 */}
<div className={styles.tableHeader}>
<div className={styles.tableTitle}>
<div className={styles.titleIcon}></div>
<div>演练记录录入</div>
</div>
<div className={styles.tableActions}>
<div className={styles.searchInputWrap}>
{/* 搜索输入框 */}
<Input.Search
className={styles.searchInput}
style={{ width: 200 }}
placeholder="搜索部位名称..."
allowClear
/>
</div>
<div className={styles.searchBarWrap}>
{/* 下拉选择 */}
<Select
className={styles.selectAll}
defaultValue="所属类别"
style={{ width: 100 }}
options={[{ value: '所属类别', label: '所属类别' }]}
/>
{/* 新增按钮 */}
<Button className={styles.addBtn} type="primary">
<PlusOutlined /> 新增演练记录
</Button>
</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>
</div>
</div>
</div>
</div>
);
};
export default EmergencyDrillRecordAssociation;

@ -0,0 +1,500 @@
// 重点部位列表样式
.keypartsList {
display: flex;
flex-direction: column;
gap: 10px;
height: 200px;
margin: 35px 0 0 0;
overflow: auto;
}
.keypartsCard {
background: #f9fbff;
border: 1px solid #ECEDFC;
border-radius: 4px;
padding: 8px 12px 6px 12px;
box-shadow: 0 1px 4px 0 rgba(46, 76, 212, 0.03);
transition: border-color 0.2s, box-shadow 0.2s;
position: relative;
// min-height: 44px;
max-width: 300px;
width: 100%;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.keypartsCardActive {
border-color: #75A7FF;
background: #F6F7FF;
box-shadow: 0 2px 8px 0 rgba(46, 76, 212, 0.10);
}
.keypartsCardHeader {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 3px;
}
.keypartsCardTitle {
font-size: 12px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.keypartsRiskTag {
display: inline-block;
min-width: 40px;
height: 30px;
line-height: 30px;
text-align: center;
font-size: 13px;
font-weight: 500;
border-radius: 5px;
padding: 0 8px;
box-shadow: 0 1px 3px 0 rgba(46, 76, 212, 0.05);
}
.keypartsCardSub {
font-size: 12px;
color: #666;
font-weight: 400;
letter-spacing: 0.5px;
}
.Rcontainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第一个div - 高度20%
.RcontainerTop {
height: 16%;
// background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
flex-direction: column;
// padding: 15px;
.blocksContainer {
flex: 1;
display: flex;
gap: 10px;
height: 100%;
.blockItem {
flex: 1;
height: 100%;
display: flex;
background: linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
border-radius: 2px;
// border: 2px solid #FFFFFF;
&.bgBlock1 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock2 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock3 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock4 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock5 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
.blockLeft {
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 15px;
padding-left: 20px;
gap: 15px;
.blockTitle {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 1.2;
}
.blockNumber {
font-family: PingFang SC;
font-weight: 700;
font-size: 24px;
color: #333333;
line-height: 1.2;
}
.blockChange {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #1269FF;
line-height: 1.2;
display: flex;
align-items: center;
gap: 4px;
.arrow {
font-size: 14px;
font-weight: bold;
}
.checkIcon {
font-size: 16px;
color: #1269FF;
}
}
}
.blockRight {
flex: 1;
height: 100%;
background-color: transparent;
border-radius: 0 4px 4px 0;
display: flex;
align-items: center;
justify-content: center;
.blockImage {
// width: 80%;
height: 65%;
// height: 80%;
object-fit: contain;
margin-right: -5px;
}
}
}
}
}
}
// 第二个div - 高度39%
.RcontainerMiddle {
height: 100%;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.middleBlock1 {
// flex: 3;
width: 28%;
height: 100%;
background: #fff;
border: 2px solid #fff;
// border-radius: 4px;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
height: 28px;
line-height: 28px;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.block1Segmented {
padding: 0;
margin: 0;
border: 1px solid #E3E3E3;
border-radius: 4px;
height: 28px;
:global(.ant-segmented) {
padding: 0;
margin: 0;
height: 28px;
}
:global(.ant-segmented-item) {
font-size: 12px;
// padding: 2px 8px;
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: center;
}
:global(.ant-segmented-item-selected) {
background-color: #2E4CD4;
color: #fff;
}
}
}
.deviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
// .block1Chart {
// width: 100%;
// height: 100%;
// margin-top: 20px;
// .mapImage {
// margin-top: 7%;
// width: 90%;
// height: 77%;
// object-fit: cover;
// border-radius: 4px;
// display: block;
// margin-left: auto;
// margin-right: auto;
// }
// }
}
.middleBlock2 {
flex: 6;
height: 100%;
// background: linear-gradient(170.5deg, #EBEFF4 6.87%, #FFFFFF 53.01%);
// border: 2px solid #fff;
background-color: #fff;
// border-radius: 4px;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.middleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 10px;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.titleRight {
font-size: 12px;
width: 60px;
height: 20px;
line-height: 20px;
background-color: #E6E9FB;
color: #2E4CD4;
text-align: center;
border-radius: 2px;
}
}
.middleBlock2Chart {
width: 100%;
height: 100%;
padding: 5px;
// min-height: 200px;
}
}
}
}
// 第三个div - 高度不超过45%
.RcontainerBottom {
height: 45%; // 限制高度不超过45%
max-height: 45%; // 确保最大高度不超过45%
// display: flex;
// flex-direction: column;
.sectionContent {
// display: flex;
// flex-direction: row;
// gap: 10px;
padding: 0;
.tableBlock {
width: 100%;
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
.tableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 15px 5px 15px;
.tableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.tableActions {
display: flex;
gap: 8px;
margin-top: 5px;
.searchInput {
// flex: 1;
// height: 40px;
// border: 1px solid #E3E6EB;
// border-radius: 6px;
// padding: 0 16px;
// font-size: 14px;
// color: #333;
// background: #fff;
// outline: none;
// box-shadow: none;
// transition: border-color 0.2s;
}
.searchInput:focus {
border-color: #2E4CD4;
}
.selectAll {
// height: 40px;
// border: 1px solid #E3E6EB;
// border-radius: 6px;
// background: #fff;
// font-size: 14px;
// color: #333;
// padding: 0 32px 0 16px;
// margin-left: 12px;
}
.selectAll:focus {
border-color: #2E4CD4;
}
.addBtn {
background: #2E4CD4;
margin-left: 15px;
}
.addBtn:hover {
background: #1d3bb3;
}
}
}
.tableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 15px; // 上边距10px左右边距15px
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
}
}
}

@ -0,0 +1,749 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button, Segmented, Input } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './EmergencyPlanAssociation.less';
import eqicon1 from '@/assets/business_firekeynotearea/eqicon1.png';
import eqicon6 from '@/assets/business_firekeynotearea/eqicon6.png';
import eqicon7 from '@/assets/business_firekeynotearea/eqicon7.png';
import eqicon8 from '@/assets/business_firekeynotearea/eqicon8.png';
const EmergencyPlanAssociation = () => {
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: 13,
itemHeight: 4,
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: 'drillId',
key: 'drillId',
width: 140,
},
{
title: '演练时间',
dataIndex: 'drillTime',
key: 'drillTime',
width: 180,
},
{
title: '演练类型',
dataIndex: 'drillType',
key: 'drillType',
width: 120,
},
{
title: '关联部位',
dataIndex: 'relatedPart',
key: 'relatedPart',
width: 120,
},
{
title: '部位名称',
dataIndex: 'partName',
key: 'partName',
width: 160,
},
{
title: '演练次数',
dataIndex: 'drillCount',
key: 'drillCount',
width: 100,
},
{
title: '参与人数',
dataIndex: 'participantCount',
key: 'participantCount',
width: 100,
},
{
title: '效果评估',
dataIndex: 'effect',
key: 'effect',
width: 120,
render: (text) => {
let color = '#FFF3E9', fontColor = '#FF8800', label = text;
if (text === '优秀') {
color = '#D8F7DE';
fontColor = '#44BB5F';
}
return (
<span style={{
background: color,
color: fontColor,
borderRadius: 5,
padding: '3px 8px',
fontSize: 12,
fontWeight: 500
}}>{label}</span>
);
}
},
{
title: '操作',
key: 'action',
width: 120,
render: (_, record) => (
<>
<span style={{ color: '#2E4CD4', cursor: 'pointer', marginRight: 16 }}>编辑</span>
<span style={{ color: '#FF2526', cursor: 'pointer' }}>删除</span>
</>
),
},
];
// 表格数据(同步图片)
const mockData = [
{
key: '1',
drillId: 'YL202310001',
drillTime: '2025-09-10 14:23:45',
drillType: '实战演练',
relatedPart: '三楼东侧',
partName: '数据机房 A区',
drillCount: 5,
participantCount: 25,
effect: '良好',
},
{
key: '2',
drillId: 'YL202310002',
drillTime: '2025-09-10 14:23:45',
drillType: '桌面推演',
relatedPart: '二楼西侧',
partName: 'B区厨房',
drillCount: 6,
participantCount: 18,
effect: '良好',
},
{
key: '3',
drillId: 'YL202310003',
drillTime: '2025-09-10 14:23:45',
drillType: '模拟演练',
relatedPart: '地下一层',
partName: '数据中心机房',
drillCount: 3,
participantCount: 32,
effect: '优秀',
},
];
// 初始化数据
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 keypartsData = [
{
name: '配电房',
location: '1楼 - 东区',
risk: '高风险',
riskColor: '#FFE0E2',
riskTextColor: '#FF3E48',
},
{
name: '消防控制室',
location: '1楼 - 西区',
risk: '中风险',
riskColor: '#FFF8E2',
riskTextColor: '#FFC403',
},
{
name: '档案室',
location: '2楼 - 中区',
risk: '低风险',
riskColor: '#DAF3FF',
riskTextColor: '#00AAFA',
}
];
const [selectedKeypartIdx, setSelectedKeypartIdx] = useState(0);
return (
<div className={styles.Rcontainer}>
{/* 第一个div - 高度20% */}
<div className={styles.RcontainerTop}>
<div className={styles.sectionContent}>
<div className={styles.blocksContainer}>
{/* 块1 */}
<div className={styles.blockItem + ' ' + styles.bgBlock1}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>演练记录总数</div>
<div className={styles.blockNumber}>120</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon1} alt="演练记录总数" className={styles.blockImage} />
</div>
</div>
{/* 块2 */}
<div className={styles.blockItem + ' ' + styles.bgBlock2}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>重点部位数量</div>
<div className={styles.blockNumber}>32</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon6} alt="重点部位数量" className={styles.blockImage} />
</div>
</div>
{/* 块3 */}
<div className={styles.blockItem + ' ' + styles.bgBlock3}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>参与演练人员</div>
<div className={styles.blockNumber}>69</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon7} alt="参与演练人员" className={styles.blockImage} />
</div>
</div>
{/* 块4 */}
<div className={styles.blockItem + ' ' + styles.bgBlock4}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>平均演练达标率</div>
<div className={styles.blockNumber}>72%</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon8} alt="平均演练达标率" className={styles.blockImage} />
</div>
</div>
</div>
</div>
</div>
<div className={styles.RcontainerMiddle}>
<div className={styles.sectionContent}>
<div className={styles.middleBlock2}>
<div className={styles.middleBlock2Title}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>重点部位分布图</div>
</div>
<div className={styles.titleRight}>
实时定位
</div>
</div>
<div className={styles.middleBlock2Chart}>
{/* 地图图片 */}
<img
src={require('@/assets/business_firekeynotearea/map.png')}
alt="重点部位分布图"
style={{ width: '100%', height: '100%' }}
/>
</div>
</div>
<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.middleBlock1}>
<div className={styles.block1Header}>
<div className={styles.block1Title}>
<div className={styles.titleIcon}></div>
重点部位列表
</div>
<div> 3 </div>
</div>
{/* 重点部位列表 */}
<div className={styles.keypartsList}>
{keypartsData.map((item, idx) => (
<div
key={item.name}
className={styles.keypartsCard + ' ' + (selectedKeypartIdx === idx ? styles.keypartsCardActive : '')}
onClick={() => setSelectedKeypartIdx(idx)}
style={{ cursor: 'pointer' }}
>
<div>
<div className={styles.keypartsCardHeader}>
<span className={styles.keypartsCardTitle}>{item.name}</span>
</div>
<div className={styles.keypartsCardSub}>{item.location}</div>
</div>
<div
className={styles.keypartsRiskTag}
style={{ background: item.riskColor, color: item.riskTextColor }}
>
{item.risk}
</div>
</div>
))}
</div>
</div>
</div>
</div>
{/* 第三个div - 占满剩余位置 */}
<div className={styles.RcontainerBottom}>
<div className={styles.sectionContent}>
<div className={styles.tableBlock}>
{/* 表格 */}
<div className={styles.tableHeader}>
<div className={styles.tableTitle}>
<div className={styles.titleIcon}></div>
<div>演练记录录入</div>
</div>
<div className={styles.tableActions}>
<div className={styles.searchInputWrap}>
{/* 搜索输入框 */}
<Input.Search
className={styles.searchInput}
style={{ width: 200 }}
placeholder="搜索部位名称..."
allowClear
/>
</div>
<div className={styles.searchBarWrap}>
{/* 下拉选择 */}
<Select
className={styles.selectAll}
defaultValue="所属类别"
style={{ width: 100 }}
options={[{ value: '所属类别', label: '所属类别' }]}
/>
{/* 新增按钮 */}
<Button className={styles.addBtn} type="primary">
<PlusOutlined /> 新增演练记录
</Button>
</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>
</div>
</div>
</div>
</div>
);
};
export default EmergencyPlanAssociation;

@ -0,0 +1,500 @@
// 重点部位列表样式
.keypartsList {
display: flex;
flex-direction: column;
gap: 10px;
height: 200px;
margin: 35px 0 0 0;
overflow: auto;
}
.keypartsCard {
background: #f9fbff;
border: 1px solid #ECEDFC;
border-radius: 4px;
padding: 8px 12px 6px 12px;
box-shadow: 0 1px 4px 0 rgba(46, 76, 212, 0.03);
transition: border-color 0.2s, box-shadow 0.2s;
position: relative;
// min-height: 44px;
max-width: 300px;
width: 100%;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.keypartsCardActive {
border-color: #75A7FF;
background: #F6F7FF;
box-shadow: 0 2px 8px 0 rgba(46, 76, 212, 0.10);
}
.keypartsCardHeader {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 3px;
}
.keypartsCardTitle {
font-size: 12px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.keypartsRiskTag {
display: inline-block;
min-width: 40px;
height: 30px;
line-height: 30px;
text-align: center;
font-size: 13px;
font-weight: 500;
border-radius: 5px;
padding: 0 8px;
box-shadow: 0 1px 3px 0 rgba(46, 76, 212, 0.05);
}
.keypartsCardSub {
font-size: 12px;
color: #666;
font-weight: 400;
letter-spacing: 0.5px;
}
.Rcontainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第一个div - 高度20%
.RcontainerTop {
height: 16%;
// background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
flex-direction: column;
// padding: 15px;
.blocksContainer {
flex: 1;
display: flex;
gap: 10px;
height: 100%;
.blockItem {
flex: 1;
height: 100%;
display: flex;
background: linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
border-radius: 2px;
// border: 2px solid #FFFFFF;
&.bgBlock1 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock2 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock3 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock4 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock5 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
.blockLeft {
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 15px;
padding-left: 20px;
gap: 15px;
.blockTitle {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 1.2;
}
.blockNumber {
font-family: PingFang SC;
font-weight: 700;
font-size: 24px;
color: #333333;
line-height: 1.2;
}
.blockChange {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #1269FF;
line-height: 1.2;
display: flex;
align-items: center;
gap: 4px;
.arrow {
font-size: 14px;
font-weight: bold;
}
.checkIcon {
font-size: 16px;
color: #1269FF;
}
}
}
.blockRight {
flex: 1;
height: 100%;
background-color: transparent;
border-radius: 0 4px 4px 0;
display: flex;
align-items: center;
justify-content: center;
.blockImage {
// width: 80%;
height: 65%;
// height: 80%;
object-fit: contain;
margin-right: -5px;
}
}
}
}
}
}
// 第二个div - 高度39%
.RcontainerMiddle {
height: 100%;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.middleBlock1 {
// flex: 3;
width: 28%;
height: 100%;
background: #fff;
border: 2px solid #fff;
// border-radius: 4px;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
height: 28px;
line-height: 28px;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.block1Segmented {
padding: 0;
margin: 0;
border: 1px solid #E3E3E3;
border-radius: 4px;
height: 28px;
:global(.ant-segmented) {
padding: 0;
margin: 0;
height: 28px;
}
:global(.ant-segmented-item) {
font-size: 12px;
// padding: 2px 8px;
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: center;
}
:global(.ant-segmented-item-selected) {
background-color: #2E4CD4;
color: #fff;
}
}
}
.deviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
// .block1Chart {
// width: 100%;
// height: 100%;
// margin-top: 20px;
// .mapImage {
// margin-top: 7%;
// width: 90%;
// height: 77%;
// object-fit: cover;
// border-radius: 4px;
// display: block;
// margin-left: auto;
// margin-right: auto;
// }
// }
}
.middleBlock2 {
flex: 6;
height: 100%;
// background: linear-gradient(170.5deg, #EBEFF4 6.87%, #FFFFFF 53.01%);
// border: 2px solid #fff;
background-color: #fff;
// border-radius: 4px;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.middleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 10px;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.titleRight {
font-size: 12px;
width: 60px;
height: 20px;
line-height: 20px;
background-color: #E6E9FB;
color: #2E4CD4;
text-align: center;
border-radius: 2px;
}
}
.middleBlock2Chart {
width: 100%;
height: 100%;
padding: 5px;
// min-height: 200px;
}
}
}
}
// 第三个div - 高度不超过45%
.RcontainerBottom {
height: 45%; // 限制高度不超过45%
max-height: 45%; // 确保最大高度不超过45%
// display: flex;
// flex-direction: column;
.sectionContent {
// display: flex;
// flex-direction: row;
// gap: 10px;
padding: 0;
.tableBlock {
width: 100%;
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
.tableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 15px 5px 15px;
.tableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.tableActions {
display: flex;
gap: 8px;
margin-top: 5px;
.searchInput {
// flex: 1;
// height: 40px;
// border: 1px solid #E3E6EB;
// border-radius: 6px;
// padding: 0 16px;
// font-size: 14px;
// color: #333;
// background: #fff;
// outline: none;
// box-shadow: none;
// transition: border-color 0.2s;
}
.searchInput:focus {
border-color: #2E4CD4;
}
.selectAll {
// height: 40px;
// border: 1px solid #E3E6EB;
// border-radius: 6px;
// background: #fff;
// font-size: 14px;
// color: #333;
// padding: 0 32px 0 16px;
// margin-left: 12px;
}
.selectAll:focus {
border-color: #2E4CD4;
}
.addBtn {
background: #2E4CD4;
margin-left: 15px;
}
.addBtn:hover {
background: #1d3bb3;
}
}
}
.tableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 15px; // 上边距10px左右边距15px
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
}
}
}

@ -0,0 +1,922 @@
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 './EvaluationReport.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 EvaluationReport = () => {
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: 8,
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: '20%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['灭火器', '消火栓', '报警器', '疏散灯', '排烟设备'],
axisLabel: {
fontSize: 12,
color: '#333',
interval: 0,
rotate: 0
},
axisLine: {
show: false
},
axisTick: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 50,
interval: 10,
axisLabel: {
fontSize: 12,
color: '#666',
formatter: '{value}'
},
axisLine: {
show: false
},
axisTick: {
show: false
},
splitLine: {
lineStyle: {
color: '#00001A26',
type: 'dashed'
}
}
},
series: [{
name: '使用次数',
type: 'bar',
barWidth: 27,
data: [35, 28, 42, 31, 38],
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#199BFB' },
{ offset: 1, color: '#1373FA' }
]
}
},
emphasis: {
itemStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#0D7AE8' },
{ offset: 1, color: '#0F5BC7' }
]
}
}
}
}],
legend: {
show: true,
top: '5%',
left: 'center',
itemWidth: 15,
itemHeight: 3,
textStyle: {
fontSize: 12,
color: '#333'
},
data: [{
name: '使用次数',
icon: 'rect',
itemStyle: {
color: '#4B69F1'
}
}]
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params) {
return `${params[0].name}<br/>使用次数: ${params[0].value}`;
}
}
};
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: 20000,
max: 30000,
interval: 2000,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
fontSize: 12,
formatter: '¥{value}'
},
splitLine: {
lineStyle: {
color: '#00001A26',
type: 'dashed'
}
}
},
series: [{
name: '费用',
type: 'line',
data: [29000, 21000, 27500, 21900, 26000, 25000, 27000, 24000, 22300, 28000, 29000, 27000],
smooth: false,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
color: '#1269FF',
width: 1
},
itemStyle: {
color: '#FFFFFF',
borderColor: '#1269FF',
borderWidth: 1
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: 'rgba(18, 105, 255, 0.3)'
}, {
offset: 1,
color: 'rgba(18, 105, 255, 0.05)'
}]
}
}
}]
};
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: ['#3C7EFF', '#FF8800', '#FFC403', '#31BCFF'],
legend: {
orient: 'vertical',
right: '2%',
top: 'middle',
itemWidth: 14,
itemHeight: 5,
textStyle: {
fontSize: 10,
color: '#666'
}
},
tooltip: {
trigger: 'item',
formatter: '{b}<br/>{d}%'
},
series: [
{
name: '设备类型占比',
type: 'pie',
radius: '70%',
center: ['40%', '55%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 0,
borderColor: '#fff',
borderWidth: 1
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: false
}
},
labelLine: {
show: false
},
data: [
{ value: 25, name: '灭火器' },
{ value: 30, name: '消防栓' },
{ value: 20, name: '报警器' },
{ value: 25, name: '烟雾探测器' }
]
}
]
};
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 || 8;
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: 120,
},
{
title: '安装位置',
dataIndex: 'installLocation',
key: 'installLocation',
width: 100,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 100,
render: (text) => {
const statusMap = {
'报废': { color: '#FF3E48', bg: '#FFE0E2' },
'待维修': { color: '#FF8800', bg: '#FFF3E9' },
'已使用': { color: '#00AAFA', bg: '#DAF3FF' },
'正常': { 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,
color: '#FF2526',
border: '1px solid #FFE0E2',
backgroundColor: 'transparent',
borderRadius: '4px'
}}>
删除
</Button>
</div>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
deviceId: 'HQ-XF-01-001',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '1层大厅',
status: '报废',
lastMaintenance: '2025-09-10',
},
{
key: '2',
id: '002',
deviceId: 'HQ-XF-02-015',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '3层东区',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '3',
id: '003',
deviceId: 'HQ-XF-03-007',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '4',
id: '004',
deviceId: 'HQ-XF-03-008',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '待维修',
lastMaintenance: '2025-09-10',
},
{
key: '5',
id: '005',
deviceId: 'HQ-XF-01-009',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '报废',
lastMaintenance: '2025-09-10',
},
{
key: '6',
id: '006',
deviceId: 'HQ-XF-01-010',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '7',
id: '007',
deviceId: 'HQ-XF-01-011',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '待维修',
lastMaintenance: '2025-09-10',
},
{
key: '8',
id: '008',
deviceId: 'HQ-XF-01-012',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '9',
id: '009',
deviceId: 'HQ-XF-01-013',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '10',
id: '010',
deviceId: 'HQ-XF-01-014',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '地下一层',
status: '待维修',
lastMaintenance: '2025-09-10',
},
{
key: '11',
id: '011',
deviceId: 'HQ-XF-01-015',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '12',
id: '012',
deviceId: 'HQ-XF-01-016',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下一层',
status: '已使用',
lastMaintenance: '2025-09-10',
},
{
key: '13',
id: '013',
deviceId: 'HQ-XF-01-017',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '2层西区',
status: '报废',
lastMaintenance: '2024-08-15',
},
{
key: '14',
id: '014',
deviceId: 'HQ-XF-02-018',
deviceName: '室内消火栓',
modelSpec: '灭火设备',
installLocation: '4层南区',
status: '报废',
lastMaintenance: '2024-07-20',
},
{
key: '15',
id: '015',
deviceId: 'HQ-XF-03-019',
deviceName: '火警报警器',
modelSpec: '报警设备',
installLocation: '地下二层',
status: '报废',
lastMaintenance: '2024-06-10',
},
{
key: '16',
id: '016',
deviceId: 'HQ-XF-01-020',
deviceName: '干粉灭火器',
modelSpec: '灭火设备',
installLocation: '5层北区',
status: '报废',
lastMaintenance: '2024-05-05',
},
];
// 初始化数据
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.Econtainer}>
{/* 第1个div - 高度39% */}
<div className={styles.EcontainerMiddle}>
<div className={styles.sectionContent}>
<div className={styles.middleBlock1}>
<div className={styles.block1Header}>
<div className={styles.block1Title}>
<div className={styles.titleIcon}></div>
设备使用频率分析
</div>
</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>
</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>
<div className={styles.middleBlock2Chart} ref={chartRef}>
</div>
</div>
</div>
</div>
{/* 第2个div - 占满剩余位置 */}
<div className={styles.EcontainerBottom}>
<div className={styles.sectionContent}>
<div className={styles.leftBlock}>
<div className={styles.maintenanceStack}>
<div className={styles.maintenanceSection}>
<div className={styles.maintenanceTitle}>
<div className={styles.titleIcon}></div>
<div>近期维护提醒</div>
</div>
<div className={styles.maintenanceContent1}>
<div className={styles.maintenanceItem}>
<div className={styles.maintenanceLeft}>
<div className={styles.maintenanceText1}>SH-MHQ-023-C 干粉灭火器</div>
<div className={styles.maintenanceText2}>位置: 4楼办公区丨维护类型: 季度检查</div>
<div className={styles.maintenanceText3}>负责人: 张三</div>
</div>
<div className={styles.maintenanceRight}>
<div className={styles.maintenanceStatus}>3天后到期</div>
</div>
</div>
<div className={styles.maintenanceItem}>
<div className={styles.maintenanceLeft}>
<div className={styles.maintenanceText1}>SH-XHS-045-D 室内消火栓</div>
<div className={styles.maintenanceText2}>位置: 2楼东侧走廊丨维护类型: 水压测试</div>
<div className={styles.maintenanceText3}>负责人: 李四</div>
</div>
<div className={styles.maintenanceRight2}>
<div className={styles.maintenanceStatus}>8天后到期</div>
</div>
</div>
</div>
</div>
<div className={styles.maintenanceSection}>
<div className={styles.maintenanceTitle}>
<div className={styles.titleIcon}></div>
<div>维护任务进度</div>
</div>
<div className={styles.maintenanceContent2}>
{/* 进度条区域 */}
<div className={styles.progressSection}>
<div className={styles.progressLabel}>月度维护计划</div>
<Progress percent={75} status="active" />
<div className={styles.progressLabel}>季度维护计划</div>
<Progress percent={60} status="active" />
<div className={styles.progressLabel}>年度维护计划</div>
<Progress percent={85} status="active" />
{/* 警告提示框 */}
<div className={styles.warningBox}>
<ExclamationCircleOutlined className={styles.warningIcon} />
<span className={styles.warningText}>本月有5项维护任务即将到期</span>
</div>
</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>
{/* 操作按钮 */}
<div className={styles.tableActions}>
<div className={styles.leftActions}>
<Input
placeholder="搜索设备名称、编号..."
onChange={handleSearchChange}
value={searchText}
style={{ width: 250, fontSize: 12 }}
allowClear
suffix={<SearchOutlined />}
/>
</div>
<div className={styles.rightActions}>
<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}`,
}}
/>
</div>
</div>
</div>
</div>
</div>
);
};
export default EvaluationReport;

@ -0,0 +1,558 @@
.Econtainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第二个div - 高度35%
.EcontainerMiddle {
// height: 400px;
min-height: 35%;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.middleBlock1 {
width: 30%;
height: 100%;
background: #fff;
border: 2px solid #fff;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
margin-top: 5px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.deviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
z-index: 10;
min-height: 100%;
}
}
.middleBlock12 {
flex: 1;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
position: relative;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
margin-top: 5px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.deviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
// bottom: 10px;
z-index: 10;
}
}
.middleBlock12 {
width: 45%;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
position: relative;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.deviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
// bottom: 10px;
min-height: 100%;
z-index: 10;
}
}
.middleBlock2 {
// flex: 1;
width: calc(100% - 75% - 15px);
height: 100%;
// background: linear-gradient(170.5deg, #EBEFF4 6.87%, #FFFFFF 53.01%);
// border: 2px solid #fff;
background-color: #fff;
// border-radius: 4px;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.middleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 5px;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.middleBlock2Chart {
width: 100%;
height: 100%;
}
}
}
}
// 第三个div - 占满剩余位置
.EcontainerBottom {
display: flex;
flex-direction: column;
flex-shrink: 0;
.sectionContent {
display: flex;
flex-direction: row;
flex: 1;
gap: 10px;
padding: 0;
.leftBlock {
width: 30%;
flex-shrink: 0;
height: 100%;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
.leftBlockTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 10px;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.maintenanceStack {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
.maintenanceSection {
width: 100%;
height: 50%;
background: #FFF;
border-radius: 4px;
display: flex;
flex-direction: column;
padding: 12px 14px;
.maintenanceTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 8px;
}
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
.maintenanceContent {
flex: 1;
width: 100%;
}
.maintenanceContent1 {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 8px;
.maintenanceItem {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #F1F7FF;
border-radius: 4px;
padding: 16px 16px;
.maintenanceLeft {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.maintenanceText1 {
font-size: 14px;
font-weight: 500;
color: #333333;
font-family: PingFang SC;
}
.maintenanceText2 {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
}
.maintenanceText3 {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
}
}
.maintenanceRight {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
.maintenanceStatus {
font-size: 12px;
color: #FF3E48;
font-weight: 500;
font-family: PingFang SC;
background-color: #FFE0E2;
padding: 4px 8px;
border-radius: 4px;
// border: 1px solid #FFE0E2;
}
}
.maintenanceRight2 {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
.maintenanceStatus {
font-size: 12px;
color: #FF8800;
font-weight: 500;
font-family: PingFang SC;
background-color: #FFF3E9;
padding: 4px 8px;
border-radius: 4px;
// padding-right: 2px;
}
}
}
}
.maintenanceContent2 {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 15px;
padding: 8px 0;
.warningBox {
display: flex;
align-items: center;
gap: 8px;
background-color: #FFF3CD;
border: 1px solid #F4E3AE;
border-radius: 4px;
padding: 8px 12px;
// margin-bottom: 8px;
// margin-top: 10px;
.warningIcon {
color: #8C6C0B;
font-size: 14px;
}
.warningText {
color: #8C6C0B;
font-size: 12px;
font-family: PingFang SC;
font-weight: 400;
}
}
.progressSection {
width: 100%;
display: flex;
flex-direction: column;
// gap: 12px;
padding: 0px 12px 12px 12px;
.progressLabel {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
font-weight: 400;
}
// 自定义进度条样式
:global(.ant-progress) {
.ant-progress-bg {
background: linear-gradient(90deg, #2E4CD4 0%, #4B69F1 100%);
}
.ant-progress-text {
color: #2E4CD4;
font-weight: 500;
}
}
}
}
}
}
}
.rightBlock {
width: calc(100% - 28% - 10px);
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
.tableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 11px 15px 5px 15px;
.tableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
.tableActions {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-top: 5px;
padding: 0px 15px;
.leftActions {
display: flex;
align-items: center;
}
.rightActions {
display: flex;
gap: 8px;
align-items: center;
}
.actionButton {
display: flex;
align-items: center;
gap: 4px;
height: 28px;
border: 1px solid #DFE4F6;
border-radius: 4px;
color: #2E4CD4;
font-weight: 500;
font-size: 12px;
padding: 0px 8px;
background: transparent;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #f0f2ff;
border-color: #2E4CD4;
}
&:active {
background-color: #e6ebff;
}
.buttonIcon {
font-size: 14px;
font-weight: bold;
}
}
}
.tableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 15px; // 上边距10px左右边距15px
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
}
}
}

@ -0,0 +1,429 @@
import React, { useEffect, useRef, useState } from 'react';
import { Button, Input, Select, Tree } from 'antd';
import { ExportOutlined, PlusOutlined } from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './KeypartsBasicInformation.less';
import eqicon1 from '@/assets/business_firekeynotearea/eqicon1.png';
import eqicon2 from '@/assets/business_firekeynotearea/eqicon2.png';
import eqicon3 from '@/assets/business_firekeynotearea/eqicon3.png';
import eqicon4 from '@/assets/business_firekeynotearea/eqicon4.png';
import eqicon5 from '@/assets/business_firekeynotearea/eqicon5.png';
const KeypartsBasicInformation = () => {
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 columns = [
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 60,
fixed: 'left',
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: 'name',
key: 'name',
width: 120,
},
{
title: '所属建筑',
dataIndex: 'building',
key: 'building',
width: 120,
},
{
title: '类型',
dataIndex: 'type',
key: 'type',
width: 100,
},
{
title: '标签',
dataIndex: 'tags',
key: 'tags',
width: 200,
render: (tags) => (
<>
{tags.map((tag, idx) => {
const tagColorMap = {
'电气风险': { color: '#F9EBBC', fontColor: '#333333' },
'重要设备': { color: '#CDDFFF', fontColor: '#333333' },
'易燃易爆': { color: '#F8C6C6', fontColor: '#333333' },
'人员密集': { color: '#B6E1F6', fontColor: '#333333' },
'关键设施': { color: '#FDDBB5', fontColor: '#333333' },
'高风险区': { color: '#F8C6C6', fontColor: '#333333' },
};
const style = tagColorMap[tag] || { color: '#eee', fontColor: '#666' };
return (
<span key={tag + idx} style={{
background: style.color,
color: style.fontColor,
borderRadius: 4,
padding: '2px 8px',
fontSize: 12,
fontWeight: 600,
marginRight: 6,
display: 'inline-block',
minWidth: 40,
textAlign: 'center',
marginBottom: 2
}}>{tag}</span>
);
})}
</>
)
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 80,
render: (text) => {
const statusMap = {
'故障': { color: '#FF3E48', bg: '#FFE0E2' },
'预警': { color: '#FF8800', bg: '#FFF3E9' },
'正常': { color: '#44BB5F', bg: '#D8F7DE' }
};
const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' };
return (
<span style={{
color: status.color,
backgroundColor: status.bg,
padding: '4px 8px',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 500
}}>
{text}
</span>
);
}
},
{
title: '最后更新',
dataIndex: 'lastUpdate',
key: 'lastUpdate',
width: 120,
},
{
title: '操作',
key: 'action',
width: 100,
align: 'center',
fixed: 'right',
render: (_, record) => (
<>
<Button type="link" size="small" style={{ fontSize: '12px', color: '#2E4CD4', fontWeight: 500, padding: 0, marginRight: 12 }}>编辑</Button>
<Button type="link" size="small" style={{ fontSize: '12px', color: '#FF2526', fontWeight: 500, padding: 0 }}>删除</Button>
</>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
name: '1号办公楼',
building: '1号办公楼',
type: '配电室',
tags: ['电气风险', '重要设备'],
status: '故障',
lastUpdate: '2025-09-10',
},
{
key: '2',
id: '002',
name: 'B区厨房',
building: '员工餐厅',
type: '厨房',
tags: ['易燃易爆', '人员密集'],
status: '预警',
lastUpdate: '2025-09-10',
},
{
key: '3',
id: '003',
name: '数据中心机房',
building: '信息中心',
type: '数据机房',
tags: ['关键设施', '重要设备'],
status: '正常',
lastUpdate: '2025-09-10',
},
{
key: '4',
id: '004',
name: '化学品仓库',
building: '实验楼',
type: '仓库',
tags: ['易燃易爆', '高风险区'],
status: '正常',
lastUpdate: '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 + ' ' + styles.bgBlock1}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>总重点部位数</div>
<div className={styles.blockNumber}>1820</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon1} alt="总重点部位数" className={styles.blockImage} />
</div>
</div>
{/* 块2 */}
<div className={styles.blockItem + ' ' + styles.bgBlock2}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>易燃易爆场所</div>
<div className={styles.blockNumber}>386</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon2} alt="易燃易爆场所" className={styles.blockImage} />
</div>
</div>
{/* 块3 */}
<div className={styles.blockItem + ' ' + styles.bgBlock3}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>人员密集区域</div>
<div className={styles.blockNumber}>269</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon3} alt="人员密集区域" className={styles.blockImage} />
</div>
</div>
{/* 块4 */}
<div className={styles.blockItem + ' ' + styles.bgBlock4}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>重要设备房</div>
<div className={styles.blockNumber}>412</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon4} alt="重要设备房" className={styles.blockImage} />
</div>
</div>
{/* 块5 */}
<div className={styles.blockItem + ' ' + styles.bgBlock5}>
<div className={styles.blockLeft}>
<div className={styles.blockTitle}>物资储存区</div>
<div className={styles.blockNumber}>290</div>
</div>
<div className={styles.blockRight}>
<img src={eqicon5} alt="物资储存区" className={styles.blockImage} />
</div>
</div>
</div>
</div>
</div>
{/* 第二个div - 占满剩余位置 */}
<div className={styles.RcontainerBottom}>
<div className={styles.sectionContent}>
<div className={styles.leftBlock}>
{/* 表格头部 */}
<div className={styles.tableHeader}>
<div className={styles.tableTitle}>
<div className={styles.titleIcon}></div>
<div>消防设施与器材列表</div>
</div>
</div>
{/* 搜索与操作栏 */}
<div className={styles.searchWrap}>
<div className={styles.searchInputWrap}>
{/* 搜索输入框 */}
<Input.Search
className={styles.searchInput}
style={{ width: 200 }}
placeholder="搜索重点部位..."
allowClear
/>
</div>
<div className={styles.searchBarWrap}>
{/* 下拉选择 */}
<Select
className={styles.selectAll}
defaultValue="全部"
style={{ width: 100 }}
options={[{ value: '全部', label: '全部' }]}
/>
{/* 新增按钮 */}
<Button className={styles.addBtn} type="primary">
<PlusOutlined /> 新增部位
</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: "max-content" }}
/>
</div>
</div>
<div className={styles.rightBlock}>
{/* 第一行块 - 蓝色方块加标题 */}
<div className={styles.leftBlockTitle}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<div className={styles.titleIcon}></div>
<div>分类标签管理</div>
</div>
<div>
<Button className={styles.addBtn} type="primary">
<PlusOutlined /> 新增标签
</Button>
</div>
</div>
{/* 树形结构 */}
<div className={styles.treeWrap}>
<Input.Search
className={styles.treeSearchInput}
placeholder="搜索标签..."
allowClear
style={{ marginBottom: 12 }}
/>
<Tree
className={styles.customTree}
showIcon
defaultExpandAll
treeData={[
{
title: '易燃易爆场所',
key: '0-0',
icon: <span className={styles.folderIcon} />,
children: [
{
title: <span style={{ color: '#2E4CD4', fontWeight: 500 }}>危险化学品储存区</span>,
key: '0-0-0',
icon: <span className={styles.childIconSelected} />,
},
{
title: <span>危险化学品储存区</span>,
key: '0-0-1',
icon: <span className={styles.childIcon} />,
},
{
title: <span>危险化学品储存区</span>,
key: '0-0-2',
icon: <span className={styles.childIcon} />,
},
],
},
{
title: '人员密集区域',
key: '0-1',
icon: <span className={styles.folderIcon} />,
children: [],
},
{
title: '重要设备房',
key: '0-2',
icon: <span className={styles.folderIcon} />,
children: [],
},
{
title: '物资储存区',
key: '0-3',
icon: <span className={styles.folderIcon} />,
children: [],
},
]}
/>
</div>
</div>
</div>
</div>
</div>
);
};
export default KeypartsBasicInformation;

@ -0,0 +1,659 @@
/* 树形结构区域样式 */
.treeWrap {
background: #fff;
border-radius: 6px;
padding: 0 0 0 0;
}
.treeSearchInput {
width: 100%;
height: 36px;
// border-radius: 6px;
// border: 1px solid #E3E6EB;
// font-size: 14px;
// margin-bottom: 10px;
// background: #F7F8FA;
}
.customTree {
background: #fff;
border: none;
.ant-tree-switcher {
margin-right: 4px;
}
.ant-tree-node-content-wrapper {
font-size: 14px;
min-height: 32px;
padding: 2px 0 2px 0;
}
.ant-tree-treenode-selected {
background: #F0F5FF !important;
border-radius: 6px;
}
}
.folderIcon {
display: inline-block;
width: 18px;
height: 18px;
background: url('@/assets/business_firekeynotearea/tree-parent-node.svg') no-repeat center center;
background-size: contain;
margin-right: 6px;
vertical-align: text-bottom;
}
.childIconSelected {
display: inline-block;
width: 18px;
height: 18px;
background: url('@/assets/business_firekeynotearea/tree-child-node-selected.svg') no-repeat center center;
background-size: contain;
margin-right: 6px;
vertical-align: text-bottom;
}
.childIcon {
display: inline-block;
width: 18px;
height: 18px;
background: url('@/assets/business_firekeynotearea/tree-child-node.svg') no-repeat center center;
background-size: contain;
margin-right: 6px;
vertical-align: text-bottom;
}
/* 搜索与操作栏样式 */
.searchWrap {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 15px 0 15px;
height: 40px;
}
.searchInput {
// flex: 1;
// height: 40px;
// border: 1px solid #E3E6EB;
// border-radius: 6px;
// padding: 0 16px;
// font-size: 14px;
// color: #333;
// background: #fff;
// outline: none;
// box-shadow: none;
// transition: border-color 0.2s;
}
.searchInput:focus {
border-color: #2E4CD4;
}
.selectAll {
// height: 40px;
// border: 1px solid #E3E6EB;
// border-radius: 6px;
// background: #fff;
// font-size: 14px;
// color: #333;
// padding: 0 32px 0 16px;
// margin-left: 12px;
}
.selectAll:focus {
border-color: #2E4CD4;
}
.addBtn {
background: #2E4CD4;
margin-left: 15px;
}
.addBtn:hover {
background: #1d3bb3;
}
.Rcontainer {
padding: 8px 0px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第一个div - 高度20%
.RcontainerTop {
height: 16%;
// background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
flex-direction: column;
// padding: 15px;
.blocksContainer {
flex: 1;
display: flex;
gap: 10px;
height: 100%;
.blockItem {
flex: 1;
height: 100%;
display: flex;
background: linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
border-radius: 2px;
// border: 2px solid #FFFFFF;
&.bgBlock1 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock2 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock3 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock4 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
&.bgBlock5 {
background: url('@/assets/business_firekeynotearea/keyparts_bg.png') no-repeat center center, linear-gradient(170.5deg, #F5F7FF 6.87%, #FFFFFF 47.65%);
background-size: cover;
}
.blockLeft {
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 15px;
padding-left: 20px;
gap: 15px;
.blockTitle {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 1.2;
}
.blockNumber {
font-family: PingFang SC;
font-weight: 700;
font-size: 24px;
color: #333333;
line-height: 1.2;
}
.blockChange {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #1269FF;
line-height: 1.2;
display: flex;
align-items: center;
gap: 4px;
.arrow {
font-size: 14px;
font-weight: bold;
}
.checkIcon {
font-size: 16px;
color: #1269FF;
}
}
}
.blockRight {
flex: 1;
height: 100%;
background-color: transparent;
border-radius: 0 4px 4px 0;
display: flex;
align-items: center;
justify-content: center;
.blockImage {
// width: 80%;
height: 65%;
// height: 80%;
object-fit: contain;
margin-right: -5px;
}
}
}
}
}
}
// 第二个div - 高度39%
.RcontainerMiddle {
height: 33%;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.middleBlock1 {
// flex: 1;
width: 28%;
height: 100%;
background: #fff;
border: 2px solid #fff;
// border-radius: 4px;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.block1Segmented {
padding: 0;
margin: 0;
border: 1px solid #E3E3E3;
border-radius: 4px;
height: 28px;
:global(.ant-segmented) {
padding: 0;
margin: 0;
height: 28px;
}
:global(.ant-segmented-item) {
font-size: 12px;
padding: 2px 8px;
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: center;
}
:global(.ant-segmented-item-selected) {
background-color: #1890ff;
color: #fff;
}
}
}
.deviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
// .block1Chart {
// width: 100%;
// height: 100%;
// margin-top: 20px;
// .mapImage {
// margin-top: 7%;
// width: 90%;
// height: 77%;
// object-fit: cover;
// border-radius: 4px;
// display: block;
// margin-left: auto;
// margin-right: auto;
// }
// }
}
.middleBlock12 {
flex: 1;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
position: relative;
.block1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.block1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.block1Segmented {
padding: 0;
margin: 0;
border: 1px solid #E3E3E3;
border-radius: 4px;
height: 28px;
:global(.ant-segmented) {
padding: 0;
margin: 0;
height: 28px;
}
:global(.ant-segmented-item) {
font-size: 12px;
padding: 2px 8px;
height: 26px;
line-height: 26px;
display: flex;
align-items: center;
justify-content: center;
}
:global(.ant-segmented-item-selected) {
background-color: #1890ff;
color: #fff;
}
}
.customSelect {
:global(.ant-select-single:not(.ant-select-customize-input) .ant-select-selector) {
height: 26px !important;
display: flex !important;
align-items: center !important;
}
:global(.ant-select-selection-item) {
line-height: 24px !important;
// height: 24px !important;
display: flex !important;
align-items: center !important;
}
}
}
.deviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
}
.middleBlock2 {
flex: 1;
height: 100%;
// background: linear-gradient(170.5deg, #EBEFF4 6.87%, #FFFFFF 53.01%);
// border: 2px solid #fff;
background-color: #fff;
// border-radius: 4px;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.middleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 10px;
.titleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.titleRight {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: #666;
}
}
.middleBlock2Chart {
width: 100%;
height: 100%;
// min-height: 200px;
}
}
}
}
// 第三个div - 占满剩余空间
.RcontainerBottom {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: column;
.sectionContent {
flex: 1 1 0;
min-height: 0;
display: flex;
flex-direction: row;
gap: 10px;
padding: 0;
.leftBlock {
width: calc(100% - 28% - 10px);
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
flex: 1 1 0;
min-width: 0;
.tableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 15px 5px 15px;
.tableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.tableActions {
display: flex;
gap: 8px;
margin-top: 5px;
.actionButton {
display: flex;
align-items: center;
gap: 4px;
height: 28px;
border: 1px solid #DFE4F6;
border-radius: 4px;
color: #2E4CD4;
font-weight: 500;
font-size: 12px;
padding: 0px 8px;
background: transparent;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #f0f2ff;
border-color: #2E4CD4;
}
&:active {
background-color: #e6ebff;
}
.buttonIcon {
font-size: 14px;
font-weight: bold;
}
}
}
}
.tableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 15px; // 上边距10px左右边距15px
:global(.ant-table) {
font-size: 12px;
}
:global(.ant-table-thead > tr > th) {
background-color: #f5f5fa;
font-weight: 500;
font-size: 14px;
color: #333333;
border-bottom: 1px solid #f0f0f0;
padding: 8px 12px;
text-align: center;
}
:global(.ant-table-tbody > tr > td) {
padding: 8px 12px;
border-bottom: 1px solid #f0f0f0;
text-align: center;
color: #666666;
}
:global(.ant-table-tbody > tr:hover > td) {
background-color: #f5f5f5;
}
:global(.ant-pagination) {
margin-top: 16px;
text-align: right;
}
}
}
.rightBlock {
width: 28%;
flex-shrink: 0;
height: 100%;
background: #fff;
padding: 0 15px 0 15px;
display: flex;
flex-direction: column;
gap: 10px;
min-width: 260px;
.leftBlockTitle {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 10px;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
}
}
}

@ -12,6 +12,7 @@ import menuTitle from '@/assets/img/智能管控平台.svg'
import menuTitle1 from '@/assets/img/智能管控平台-1.svg' import menuTitle1 from '@/assets/img/智能管控平台-1.svg'
import fireHydrant from '@/assets/img/fireHydrant.svg' import fireHydrant from '@/assets/img/fireHydrant.svg'
import fireHydrant1 from '@/assets/img/fireHydrant1.svg' import fireHydrant1 from '@/assets/img/fireHydrant1.svg'
import fireKeynoteArea from '@/assets/img/fire_keynote_area.svg'
import trouble from '@/assets/img/trouble.svg' import trouble from '@/assets/img/trouble.svg'
import book from '@/assets/img/book.svg' import book from '@/assets/img/book.svg'
import danger from '@/assets/img/danger.svg' import danger from '@/assets/img/danger.svg'
@ -104,6 +105,19 @@ const SystemContentList = (props) => {
key: "/topnavbar00/business/basic", key: "/topnavbar00/business/basic",
"label": "基础信息管理" "label": "基础信息管理"
}, },
{
path: '/topnavbar00/business/firekeynotearea',
icon: <img
src={fireKeynoteArea}
alt="消防重点部位管理"
style={{
width: '16px',
height: '16px',
opacity: selectedKey.includes('/topnavbar00/business/firekeynotearea') ? 1 : 0.6
}} />,
key: "/topnavbar00/business/firekeynotearea",
"label": "消防重点部位管理"
},
{ {
"path": "/topnavbar00/business/basicinformation", "path": "/topnavbar00/business/basicinformation",
icon: <img src={fireHydrant1} alt="安全管理基础信息" style={{ width: '16px', height: '16px' }} />, icon: <img src={fireHydrant1} alt="安全管理基础信息" style={{ width: '16px', height: '16px' }} />,

@ -12,6 +12,10 @@ const menuItem = [
label: '基础信息管理', label: '基础信息管理',
key: '/topnavbar00/business/basic', key: '/topnavbar00/business/basic',
}, },
{
label: '消防重点部位管理',
key: '/topnavbar00/business/firekeynotearea',
},
{ {
label: '安全管理基础信息', label: '安全管理基础信息',
key: '/topnavbar00/business/basicinformation', key: '/topnavbar00/business/basicinformation',

Loading…
Cancel
Save