删除多余菜单目录

main
jiangxucong 1 month ago
parent df9086cf4d
commit 7617b1cd96

@ -1,56 +0,0 @@
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;

@ -1,66 +0,0 @@
.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; // ======== 无内边距 ========
}
}

@ -1,749 +0,0 @@
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;

@ -1,482 +0,0 @@
// 重点部位列表样式
.keypartsList {
display: flex;
flex-direction: column;
gap: 10px;
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;
}
}
.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;
line-height: 28px;
height: 28px;
// 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;
}
}
}
}
}
}

@ -1,639 +0,0 @@
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 ksdyIcon from '@/assets/business_firekeynotearea/ksdy.svg';
import deleteIcon from '@/assets/business_firekeynotearea/delete.svg';
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 = {
legend: {
data: [{
name: '调用次数',
icon: 'rect',
itemStyle: {
color: '#4B69F1'
}
}],
top: 0,
left: 'center',
itemWidth: 12,
itemHeight: 3,
textStyle: {
fontSize: 12,
color: '#333'
},
},
grid: {
left: '3%',
right: '3%',
bottom: '3%',
top: 30,
containLabel: true,
},
xAxis: {
type: 'category',
data: ['电气火灾', '防水防潮', '人员疏散', '燃气泄漏', '油锅起火'],
axisLabel: {
fontSize: 12,
color: '#666666',
margin: 12,
},
axisLine: {
lineStyle: {
color: 'rgba(0, 0, 26, 0.15)',
type: 'solid', // 改为实线
},
},
axisTick: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 25,
splitNumber: 5,
axisLabel: {
fontSize: 12,
color: '#666666',
},
axisLine: {
show: false,
},
splitLine: {
lineStyle: {
color: 'rgba(0, 0, 26, 0.15)',
type: 'dashed',
},
},
},
series: [
{
name: '调用次数',
type: 'bar',
barWidth: 32,
data: [16, 22, 16, 16, 8],
itemStyle: {
// borderRadius: [4, 4, 0, 0],
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: '#199BFB' },
{ offset: 1, color: '#1373FA' },
],
},
},
},
],
};
chart.setOption(option);
// 响应式调整
const handleResize = () => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
if (chart && !chart.isDisposed()) {
chart.dispose();
}
};
}
}, []);
// 表格列定义(同步图片)
const columns = [
{
title: '编号',
dataIndex: 'bh',
key: 'bh',
width: 140,
},
{
title: '部位名称',
dataIndex: 'bwmc',
key: 'bwmc',
width: 180,
},
{
title: '部位类型',
dataIndex: 'bwlx',
key: 'bwlx',
width: 120,
},
{
title: '关联预案数',
dataIndex: 'glyans',
key: 'glyans',
width: 120,
},
{
title: '最近更新',
dataIndex: 'zjgx',
key: 'zjgx',
width: 160,
},
{
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: '#44BB5F', cursor: 'pointer' }}>关联</span>
</>
),
},
];
// 表格数据(同步图片)
const mockData = [
{
key: '1',
bh: '01',
bwmc: '数据机房A区',
bwlx: '数据机房',
glyans: 5,
zjgx: '2025-09-10 14:23:45',
effect: '有更新',
},
{
key: '2',
bh: '02',
bwmc: 'B区厨房',
bwlx: '厨房区域',
glyans: 3,
zjgx: '2025-09-10 14:23:45',
effect: '有更新',
},
{
key: '3',
bh: '03',
bwmc: '数据中心机房',
bwlx: '配电房',
glyans: 4,
zjgx: '2025-09-10 14:23:45',
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.topBlock}>
<div className={styles.topBlockTitle}>
<div className={styles.titleLeft}>
<div className={styles.titleIcon}></div>
<div>应急预案关联管理功能组成图</div>
</div>
</div>
<div className={styles.topBlockBg}>
<img
src={require('@/assets/business_firekeynotearea/relation-group.png')}
alt="应急预案关联管理功能组成图"
style={{ width: '50%' }}
/>
</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>
<div className={styles.middleBlock2Chart} ref={chartRef}></div>
</div>
<div className={styles.middleBlock1}>
<div className={styles.block1Header}>
<div className={styles.block1Title}>
<div className={styles.titleIcon}></div>
关联应急预案
</div>
</div>
{/* 关联应急预案列表 */}
<div className={styles.planListWrap}>
{[
{
title: '电气火灾应急预案',
desc: '适用场景: 电气短路、设备过载引发的火灾',
update: '2023-05-07',
user: '张正',
},
{
title: '精密设备防水防潮应急预案',
desc: '适用场景: 电气短路、设备过载引发的火灾',
update: '2023-05-07',
user: '张正',
},
{
title: '精密设备防水防潮应急预案',
desc: '适用场景: 电气短路、设备过载引发的火灾',
update: '2023-05-07',
user: '张正',
}
].map((item, idx) => (
<div className={styles.planCard} key={item.title}>
<div style={{ flex: 1 }}>
<div className={styles.planCardTitle}>{item.title}</div>
<div className={styles.planCardDesc}>{item.desc}</div>
<div className={styles.planCardUpdate}>上次修订{item.update} | 修订人{item.user}</div>
</div>
<div className={styles.planCardFooter}>
<div className={styles.planCardBtns}>
<Button className={styles.quickBtn} type="primary" size="small">
<img src={ksdyIcon} alt="快速调用" style={{ width: 12, height: 12, marginRight: 4, verticalAlign: 'middle' }} />
快速调用
</Button>
<Button className={styles.delBtn} type="danger" size="small">
<img src={deleteIcon} alt="删除" style={{ width: 12, height: 12, marginRight: 4, verticalAlign: 'middle' }} />
删除
</Button>
</div>
</div>
</div>
))}
</div>
</div>
<div className={styles.middleBlock1}>
<div className={styles.block1Header}>
<div className={styles.block1Title}>
<div className={styles.titleIcon}></div>
预案更新通知
</div>
</div>
{/* 预案更新通知列表 */}
<div className={styles.planListWrap}>
{[
{
title: '电气火灾应急预案已更新',
desc: '该预案已修订涉及数据机房A区等3个重点部位需要同步更新',
time: '10分钟',
},
{
title: '仓库物品堆放安全管理预案已发布',
desc: '新预案适用于各类仓库区域,建议进行关联',
time: '20分钟',
},
{
title: '防汛应急预案年度修订提醒',
desc: '距离防汛应急预案下次修订时间还有30天',
time: '40分钟',
}
].map((item, idx) => (
<div className={styles.planCard} key={item.title}>
<div style={{ flex: 1 }}>
<div className={styles.planCardTitle}>{item.title}</div>
<div className={styles.planCardUpdate}>{item.desc}</div>
</div>
<div className={styles.planCardFooter}>
<div style={{color: '#FF2526', fontSize: 12}}>
{item.time}
</div>
</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;

@ -1,452 +0,0 @@
// 关联应急预案卡片列表样式
.planListWrap {
display: flex;
flex-direction: column;
gap: 8px;
margin: 35px 0 0 0;
height: 200px;
overflow: auto;
/* 隐藏滚动条但保留滚动效果 */
scrollbar-width: none;
}
.planListWrap::-webkit-scrollbar {
display: none;
/* Chrome/Safari */
}
.planCard {
background: #F1F7FF;
// border: 1px solid #ecedfc;
border-radius: 4px;
padding: 10px 12px;
box-shadow: 0 1px 4px 0 rgba(46, 76, 212, 0.03);
display: flex;
gap: 8px;
}
.planCardTitle {
font-size: 14px;
font-weight: 600;
color: #333;
margin-bottom: 6px;
}
.planCardDesc {
font-size: 12px;
color: #666;
margin-bottom: 6px;
}
.planCardFooter {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 6px;
}
.planCardUpdate {
font-size: 12px;
color: #666;
}
.planCardBtns {
display: flex;
gap: 10px;
}
.quickBtn {
background: #1169FF !important;
color: #fff;
border: none;
border-radius: 4px;
font-size: 12px !important;
font-weight: 500;
padding: 0 12px;
display: flex;
align-items: center;
}
.quickBtn:hover {
background: #1169FF;
color: #fff;
}
.delBtn {
background: #FE5F60 !important;
color: #fff;
border: none;
border-radius: 4px;
font-size: 12px !important;
font-weight: 500;
padding: 0 12px;
display: flex;
align-items: center;
}
.delBtn:hover {
background: #d9363e;
color: #fff;
}
// 重点部位列表样式
.Rcontainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
.RcontainerTop {
height: 100%;
border-radius: 4px;
display: flex;
// flex-direction: column;
background: url('@/assets/business_firekeynotearea/yj-bg.png') no-repeat center center;
background-size: cover;
.sectionContent {
width: 100%;
height: 100%;
gap: 10px;
.topBlock {
height: 100%;
display: flex;
flex-direction: column;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
padding: 5px 10px 5px 10px;
.topBlockTitle {
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;
}
}
.topBlockBg {
width: 100%;
height: 100%;
padding: 5px;
display: flex;
align-items: center;
justify-content: center;
}
}
}
}
// 第二个div - 高度39%
.RcontainerMiddle {
height: 100%;
border-radius: 4px;
display: flex;
flex-direction: column;
.sectionContent {
height: 100%;
display: flex;
gap: 10px;
.middleBlock1 {
flex: 4;
// width: 28%;
height: 100%;
background: #fff;
border: 2px solid #fff;
// border-radius: 4px;
position: relative;
padding: 0px 10px 10px 10px;
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;
}
}
.middleBlock2 {
flex: 4;
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;
line-height: 28px;
height: 28px;
// 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;
}
}
}
}
}
}

@ -1,429 +0,0 @@
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;

@ -1,395 +0,0 @@
/* 树形结构区域样式 */
.treeWrap {
background: #fff;
border-radius: 6px;
padding: 0 0 0 0;
}
.treeSearchInput {
width: 100%;
height: 36px;
}
.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: 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;
.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;
}
}
}
}
}
}
.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 0px 15px;
.tableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
line-height: 32px;
.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;
padding-top: 8px;
line-height: 32px;
.titleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
}
}
}

@ -1,48 +0,0 @@
import React, { useState } from 'react';
import { Button } from 'antd';
import styles from './FireWarning.less';
import RealtimeMonitoring from './components/RealtimeMonitoring';
import DataAnalysisWarning from './components/DataAnalysisWarning';
const Firewarning = () => {
const [activeModule, setActiveModule] = useState('realtime');
const handleModuleClick = (module) => {
setActiveModule(module);
};
const renderModule = () => {
switch (activeModule) {
case 'realtime':
return <RealtimeMonitoring />;
case 'analysis':
return <DataAnalysisWarning />;
default:
return <RealtimeMonitoring />;
}
};
return (
<div className={styles.firewarningContainer}>
<div className={styles.firewarningTopButton}>
<Button
className={`${styles.firewarningTopButtonItem} ${activeModule === "realtime" ? styles.active : ""}`}
onClick={() => handleModuleClick("realtime")}
>
实时状态监测
</Button>
<Button
className={`${styles.firewarningTopButtonItem} ${activeModule === "analysis" ? styles.active : ""}`}
onClick={() => handleModuleClick("analysis")}
>
数据分析与预警
</Button>
</div>
<div className={styles.firewarningContent}>
{renderModule()}
</div>
</div>
);
};
export default Firewarning;

@ -1,66 +0,0 @@
.firewarningContainer {
background-color: transparent;
width: 100%;
height: 89vh;
overflow: hidden;
display: flex;
flex-direction: column;
.firewarningTopButton {
background-color: white;
width: 100%;
padding: 10px 30px;
display: flex;
gap: 24px;
margin-left: 6px;
.firewarningTopButtonItem {
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;
}
}
}
}
.firewarningContent {
// ======== 内容区域样式 ========
flex: 1; // ======== 占据剩余空间 ========
overflow-y: auto; // ======== 允许垂直滚动 ========
padding: 0; // ======== 无内边距 ========
}
}

@ -1,856 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button, Segmented, Progress, Input } from 'antd';
import { CheckCircleOutlined, ExportOutlined, HeartFilled, LineHeightOutlined, ExclamationCircleOutlined, SearchOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './DataAnalysisWarning.less';
import img1 from '@/assets/safe_majorHazard/online_monitoring/img1.png';
import img2 from '@/assets/safe_majorHazard/online_monitoring/img2.png';
import img3 from '@/assets/safe_majorHazard/online_monitoring/img3.png';
import map1 from '@/assets/safe_majorHazard/online_monitoring/map.png';
import risk1 from '@/assets/safe_majorHazard/online_monitoring/risk1.png';
import risk2 from '@/assets/safe_majorHazard/online_monitoring/risk2.png';
import risk3 from '@/assets/safe_majorHazard/online_monitoring/risk3.png';
import eqicon1 from '@/assets/business_basic/eqicon1.png';
import eqicon2 from '@/assets/business_basic/eqicon2.png';
import eqicon3 from '@/assets/business_basic/eqicon3.png';
import eqicon4 from '@/assets/business_basic/eqicon4.png';
const DataAnalysisWarning = () => {
const chartRef = useRef(null);
const pieChartRef = useRef(null);
const faultPieChartRef = useRef(null);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
const [loading, setLoading] = useState(false);
const [dataSource, setDataSource] = useState([]);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 5,
total: 0,
});
const [searchText, setSearchText] = useState('');
// 柱状图初始化
useEffect(() => {
if (pieChartRef.current) {
const barChart = echarts.init(pieChartRef.current);
const barOption = {
grid: {
left: '5%',
right: '5%',
bottom: '10%',
top: '18%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
axisLabel: {
fontSize: 12,
color: '#333',
interval: 0,
rotate: 0
},
axisLine: {
show: false
},
axisTick: {
show: false
}
},
yAxis: {
type: 'value',
min: 0,
max: 35,
interval: 5,
axisLabel: {
fontSize: 12,
color: '#666',
formatter: '{value}'
},
axisLine: {
show: false
},
axisTick: {
show: false
},
splitLine: {
lineStyle: {
color: '#00001A26',
type: 'dashed'
}
}
},
series: [
{
name: '火灾报警',
type: 'bar',
stack: 'total',
barWidth: 20,
data: [12, 8, 15, 10, 18, 14, 16, 13, 11, 17, 19, 15],
itemStyle: {
color: '#8979FF'
}
},
{
name: '故障报警',
type: 'bar',
stack: 'total',
barWidth: 20,
data: [6, 9, 7, 12, 8, 11, 9, 14, 10, 7, 8, 6],
itemStyle: {
color: '#FF928A'
}
},
{
name: '误报',
type: 'bar',
stack: 'total',
barWidth: 20,
data: [3, 5, 4, 7, 6, 8, 5, 9, 7, 4, 6, 5],
itemStyle: {
color: '#3CC3DF'
}
}
],
legend: {
show: true,
top: '8%',
left: 'center',
itemWidth: 8,
itemHeight: 8,
textStyle: {
fontSize: 12,
color: '#333'
},
data: [
{
name: '火灾报警',
icon: 'rect',
itemStyle: {
color: '#8979FF'
}
},
{
name: '故障报警',
icon: 'rect',
itemStyle: {
color: '#FF928A'
}
},
{
name: '误报',
icon: 'rect',
itemStyle: {
color: '#3CC3DF'
}
}
]
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: function (params) {
let result = `${params[0].name}<br/>`;
params.forEach(param => {
result += `${param.seriesName}: ${param.value}<br/>`;
});
return result;
}
}
};
barChart.setOption(barOption);
// 响应式调整
const handleBarResize = () => {
if (barChart && !barChart.isDisposed()) {
barChart.resize();
}
};
window.addEventListener('resize', handleBarResize);
return () => {
window.removeEventListener('resize', handleBarResize);
if (barChart && !barChart.isDisposed()) {
barChart.dispose();
}
};
}
}, []);
// 设备运行状态趋势折线图初始化
useEffect(() => {
if (faultPieChartRef.current) {
const faultPieChart = echarts.init(faultPieChartRef.current);
const faultPieOption = {
legend: {
show: true,
top: '5%',
left: 'center',
itemWidth: 20,
itemHeight: 8,
textStyle: {
color: '#333',
fontSize: 12
}
},
grid: {
left: '5%',
right: '5%',
bottom: '10%',
top: '20%',
containLabel: true
},
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
axisLine: {
lineStyle: {
color: '#E5E5E5'
}
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
fontSize: 12,
interval: 0
}
},
yAxis: {
type: 'value',
min: 0,
max: 100,
interval: 20,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: '#666',
fontSize: 12,
formatter: '{value}%'
},
splitLine: {
lineStyle: {
color: '#00001A26',
type: 'dashed'
}
}
},
series: [
{
name: '正常运行率',
type: 'line',
data: [85, 78, 92, 88, 95, 90, 87, 93, 89, 91, 86, 88],
smooth: false,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
color: '#3CC3DF',
width: 1
},
itemStyle: {
color: '#FFF',
borderColor: '#3CC3DF',
borderWidth: 1
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: 'rgba(60, 195, 223, 0.3)'
}, {
offset: 1,
color: 'rgba(60, 195, 223, 0.05)'
}]
}
}
},
{
name: '故障率',
type: 'line',
data: [15, 22, 8, 12, 5, 10, 13, 7, 11, 9, 14, 12],
smooth: false,
symbol: 'circle',
symbolSize: 6,
lineStyle: {
color: '#8979FF',
width: 1
},
itemStyle: {
color: '#fff',
borderColor: '#8979FF',
borderWidth: 1
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [{
offset: 0,
color: 'rgba(137, 121, 255, 0.3)'
}, {
offset: 1,
color: 'rgba(137, 121, 255, 0.05)'
}]
}
}
}
],
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
},
formatter: function (params) {
let result = `${params[0].name}<br/>`;
params.forEach(param => {
result += `${param.seriesName}: ${param.value}%<br/>`;
});
return result;
}
}
};
faultPieChart.setOption(faultPieOption);
// 响应式调整
const handleFaultPieResize = () => {
if (faultPieChart && !faultPieChart.isDisposed()) {
faultPieChart.resize();
}
};
window.addEventListener('resize', handleFaultPieResize);
return () => {
window.removeEventListener('resize', handleFaultPieResize);
if (faultPieChart && !faultPieChart.isDisposed()) {
faultPieChart.dispose();
}
};
}
}, []);
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
// 强制初始化时调整大小
setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 100);
const option = {
tooltip: {
trigger: 'item'
},
legend: {
bottom: '4%',
left: 'center',
itemWidth: 16,
itemHeight: 5,
textStyle: {
fontSize: 12,
}
},
series: [
{
name: '设备故障原因',
type: 'pie',
radius: ['20%', '65%'],
center: ['50%', '40%'],
avoidLabelOverlap: false,
padAngle: 5,
itemStyle: {
borderRadius: 8,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
// emphasis: {
// label: {
// show: true,
// fontSize: 40,
// fontWeight: 'bold'
// }
// },
labelLine: {
show: false
},
data: [
{ value: 1048, name: '环境因素(粉尘)', itemStyle: { color: '#44BB5F' } },
{ value: 735, name: '环境因素(湿度)', itemStyle: { color: '#F8C541' } },
{ value: 580, name: '设备故障', itemStyle: { color: '#A493FB' } },
{ value: 484, name: '施工干扰', itemStyle: { color: '#4B69F1' } },
{ value: 300, name: '其他', itemStyle: { color: '#949FD0' } }
]
}
]
};
chart.setOption(option);
// 响应式调整 - 使用多种方式监听容器尺寸变化
let resizeTimer = null;
const handleResize = () => {
// 防抖处理避免频繁调用resize
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 50); // 减少延迟时间
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题)
let resizeObserver = null;
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
// 使用requestAnimationFrame确保在下一帧执行
requestAnimationFrame(() => {
handleResize();
});
}
});
resizeObserver.observe(chartRef.current);
}
// 额外监听父容器的尺寸变化
const parentContainer = chartRef.current?.parentElement;
let parentObserver = null;
if (parentContainer && window.ResizeObserver) {
parentObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
requestAnimationFrame(() => {
handleResize();
});
}
});
parentObserver.observe(parentContainer);
}
// 使用MutationObserver监听DOM结构变化菜单展开收起时
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' &&
(mutation.attributeName === 'class' || mutation.attributeName === 'style')) {
// 延迟执行确保DOM更新完成
setTimeout(() => {
handleResize();
}, 200);
}
});
});
// 监听整个页面的class和style变化
mutationObserver.observe(document.body, {
attributes: true,
attributeFilter: ['class', 'style'],
subtree: true
});
return () => {
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
}
if (parentObserver) {
parentObserver.disconnect();
}
if (mutationObserver) {
mutationObserver.disconnect();
}
if (resizeTimer) {
clearTimeout(resizeTimer);
}
if (chart && !chart.isDisposed()) {
chart.dispose();
}
};
}
}, []);
// 表格列定义
const columns = [
{
title: '序号',
dataIndex: 'id',
key: 'id',
width: 60,
render: (text, record, index) => {
const page = pagination.current || 1;
const pageSize = pagination.pageSize || 5;
const number = (page - 1) * pageSize + index + 1;
return number;
}
},
{
title: '区域',
dataIndex: 'area',
key: 'area',
width: 120,
},
{
title: '火灾报警',
dataIndex: 'fireAlarm',
key: 'fireAlarm',
width: 100,
},
{
title: '故障报警',
dataIndex: 'faultAlarm',
key: 'faultAlarm',
width: 100,
},
{
title: '误报',
dataIndex: 'falseAlarm',
key: 'falseAlarm',
width: 100,
},
{
title: '总计',
dataIndex: 'total',
key: 'total',
width: 100,
},
{
title: '同比变化',
dataIndex: 'yearOverYear',
key: 'yearOverYear',
width: 120,
render: (text) => {
const isPositive = text.includes('↑');
const color = isPositive ? '#FF3E48' : '#44BB5F';
const icon = isPositive ? '↑' : '↓';
return (
<span style={{ color }}>
{text}
</span>
);
}
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '1',
area: 'A栋',
fireAlarm: 3,
faultAlarm: 7,
falseAlarm: 10,
total: 23,
yearOverYear: '↓ 12%',
},
{
key: '2',
id: '2',
area: 'B栋',
fireAlarm: 2,
faultAlarm: 9,
falseAlarm: 6,
total: 18,
yearOverYear: '↑ 8%',
},
{
key: '3',
id: '3',
area: 'C栋',
fireAlarm: 1,
faultAlarm: 5,
falseAlarm: 8,
total: 16,
yearOverYear: '↓ 15%',
},
{
key: '4',
id: '4',
area: 'D栋',
fireAlarm: 0,
faultAlarm: 2,
falseAlarm: 7,
total: 11,
yearOverYear: '↓ 16%',
},
{
key: '5',
id: '5',
area: 'E栋',
fireAlarm: 4,
faultAlarm: 6,
falseAlarm: 5,
total: 15,
yearOverYear: '↓ 5%',
},
{
key: '6',
id: '6',
area: 'F栋',
fireAlarm: 2,
faultAlarm: 8,
falseAlarm: 9,
total: 19,
yearOverYear: '↑ 3%',
},
{
key: '7',
id: '7',
area: 'G栋',
fireAlarm: 1,
faultAlarm: 4,
falseAlarm: 6,
total: 11,
yearOverYear: '↓ 8%',
},
{
key: '8',
id: '8',
area: 'H栋',
fireAlarm: 3,
faultAlarm: 3,
falseAlarm: 4,
total: 10,
yearOverYear: '↓ 20%',
},
{
key: '9',
id: '9',
area: 'I栋',
fireAlarm: 0,
faultAlarm: 1,
falseAlarm: 3,
total: 4,
yearOverYear: '↓ 25%',
},
{
key: '10',
id: '10',
area: 'J栋',
fireAlarm: 2,
faultAlarm: 5,
falseAlarm: 7,
total: 14,
yearOverYear: '↑ 2%',
},
];
// 初始化数据
useEffect(() => {
setPagination(prev => ({ ...prev, total: mockData.length }));
}, []);
// 根据分页获取当前页数据
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const startIndex = (current - 1) * pageSize;
const endIndex = startIndex + pageSize;
return mockData.slice(startIndex, endIndex);
};
// 表格选择变化
const onSelectChange = (newSelectedRowKeys, newSelectedRows) => {
setSelectedRowKeys(newSelectedRowKeys);
setSelectedRows(newSelectedRows);
};
// 新增设备按钮点击事件
const handleAddDevice = () => {
console.log('新增设备');
// TODO: 实现新增设备逻辑
};
// 导出数据按钮点击事件
const handleExportData = () => {
console.log('导出数据');
// TODO: 实现导出数据逻辑
};
// 分页变化处理
const handleTableChange = (pagination) => {
setPagination(prev => ({
...prev,
current: pagination.current,
pageSize: pagination.pageSize,
}));
};
// 搜索处理
const handleSearchChange = (e) => {
setSearchText(e.target.value);
console.log('搜索:', e.target.value);
// TODO: 实现搜索逻辑,根据设备名称、编号等筛选数据
};
return (
<div className={styles.analysisContainer}>
<div className={styles.analysisContainerMiddle}>
<div className={styles.analysisSectionContent}>
<div className={styles.analysisMiddleBlock2}>
<div className={styles.analysisMiddleBlock2Title}>
<div className={styles.analysisTitleLeft}>
<div className={styles.analysisTitleIcon}></div>
<div>误报原因分析</div>
</div>
</div>
<div className={styles.analysisMiddleBlock2Chart} ref={chartRef}>
</div>
</div>
<div className={styles.analysisMiddleBlock1}>
<div className={styles.analysisBlock1Header}>
<div className={styles.analysisBlock1Title}>
<div className={styles.analysisTitleIcon}></div>
报表统计生成
</div>
</div>
<div className={styles.analysisDeviceStatusChart} ref={pieChartRef}>
</div>
</div>
<div className={styles.analysisMiddleBlock12}>
<div className={styles.analysisBlock1Header}>
<div className={styles.analysisBlock1Title}>
<div className={styles.analysisTitleIcon}></div>
设备运行状态趋势
</div>
</div>
<div className={styles.analysisDeviceStatusChart} ref={faultPieChartRef}>
</div>
</div>
</div>
</div>
{/* 底部区域 */}
<div className={styles.analysisBottom}>
{/* 左侧维护提醒 */}
<div className={styles.analysisMaintenanceSection}>
<div className={styles.analysisMaintenanceTitle}>
<div className={styles.analysisTitleIcon}></div>
<div>实时预警信息</div>
</div>
<div className={styles.analysisMaintenanceContent}>
<div className={styles.analysisMaintenanceItem1}>
<div className={styles.analysisMaintenanceLeft}>
<div className={styles.analysisMaintenanceText1}>电路线路过载预警</div>
<div className={styles.analysisMaintenanceText2}>B栋3层配电室丨15分钟前</div>
<div className={styles.analysisMaintenanceText3}>电流持续上升已超过正常阈值</div>
</div>
<div className={styles.analysisMaintenanceRight}>
<div className={styles.analysisMaintenanceStatus}>紧急</div>
</div>
</div>
<div className={styles.analysisMaintenanceItem2}>
<div className={styles.analysisMaintenanceLeft}>
<div className={styles.analysisMaintenanceText1}>2号防排烟风机异常</div>
<div className={styles.analysisMaintenanceText2}>地下车库丨2小时前</div>
<div className={styles.analysisMaintenanceText3}>震动频率异常, 建议尽快检修</div>
</div>
<div className={styles.analysisMaintenanceRight2}>
<div className={styles.analysisMaintenanceStatus}>警告</div>
</div>
</div>
<div className={styles.analysisMaintenanceItem3}>
<div className={styles.analysisMaintenanceLeft}>
<div className={styles.analysisMaintenanceText1}>消防水泵预测维护</div>
<div className={styles.analysisMaintenanceText2}>A栋水泵房丨今天</div>
<div className={styles.analysisMaintenanceText3}>预计15天后需要维护请提前安排</div>
</div>
<div className={styles.analysisMaintenanceRight2}>
<div className={styles.analysisMaintenanceStatus2}>提示</div>
</div>
</div>
<div className={styles.analysisMaintenanceItem4}>
<div className={styles.analysisMaintenanceLeft}>
<div className={styles.analysisMaintenanceText1}>消防水泵预测维护</div>
<div className={styles.analysisMaintenanceText2}>A栋水泵房丨今天</div>
<div className={styles.analysisMaintenanceText3}>预计15天后需要维护请提前安排</div>
</div>
<div className={styles.analysisMaintenanceRight2}>
<div className={styles.analysisMaintenanceStatus2}>提示</div>
</div>
</div>
</div>
</div>
{/* 右侧表格 */}
<div className={styles.analysisTableSection}>
<div className={styles.analysisTableHeader}>
<div className={styles.analysisTableTitle}>
<div className={styles.analysisTitleIcon}></div>
<div>月度报警统计</div>
</div>
<div className={styles.analysisTableActions}>
<button className={styles.analysisActionButton} onClick={handleExportData}>
<span className={styles.analysisButtonIcon}><ExportOutlined /></span>
<span>导出数据</span>
</button>
</div>
</div>
<div className={styles.analysisTableContainer}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: pagination
}}
loading={loading}
selectionType="checkbox"
onSelectRow={onSelectChange}
onChange={handleTableChange}
pagination={{
...pagination,
showSizeChanger: false,
showQuickJumper: true,
showTotal: (total, range) =>
`${total}`,
}}
/>
</div>
</div>
</div>
</div>
);
};
export default DataAnalysisWarning;

@ -1,508 +0,0 @@
.analysisContainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第二个div - 高度35%
.analysisContainerMiddle {
// height: 400px;
min-height: 46%;
border-radius: 4px;
display: flex;
flex-direction: column;
.analysisSectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.analysisMiddleBlock1 {
width: 39%;
height: 100%;
background: #fff;
border: 2px solid #fff;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.analysisBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.analysisBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
margin-top: 5px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisDeviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
z-index: 10;
min-height: 100%;
}
}
.analysisMiddleBlock12 {
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;
.analysisBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.analysisBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
margin-top: 5px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisDeviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
// bottom: 10px;
z-index: 10;
}
}
.analysisMiddleBlock12 {
width: 40%;
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;
.analysisBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.analysisBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisDeviceStatusChart {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
// bottom: 10px;
min-height: 100%;
z-index: 10;
}
}
.analysisMiddleBlock2 {
// flex: 1;
width: calc(100% - 76% - 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;
.analysisMiddleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 5px;
.analysisTitleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.analysisMiddleBlock2Chart {
width: 100%;
height: 100%;
}
}
}
}
// 底部区域
.analysisBottom {
display: flex;
flex-direction: row;
gap: 10px;
flex: 1;
.analysisMaintenanceSection {
width: 30%;
background: #FFF;
border-radius: 4px;
display: flex;
flex-direction: column;
padding: 12px 14px;
.analysisMaintenanceTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 8px;
.analysisTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.analysisMaintenanceContent {
flex: 1;
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
.analysisMaintenanceItem {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #FFF;
border-radius: 2px;
padding: 0px 16px;
}
.analysisMaintenanceItem1 {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #FDF2F2;
border-left: 4px solid #EB5050;
border-radius: 2px;
padding: 8px 16px;
}
.analysisMaintenanceItem2 {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #FEF8E4;
border-left: 4px solid #F6C644;
border-radius: 4px;
padding: 8px 16px;
}
.analysisMaintenanceItem3 {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #F0F9FE;
border-left: 4px solid #4CA7F3;
border-radius: 4px;
padding: 8px 16px;
}
.analysisMaintenanceItem4 {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #F0F9FE;
border-left: 4px solid #4CA7F3;
border-radius: 4px;
padding: 8px 16px;
}
.analysisMaintenanceItem,
.analysisMaintenanceItem1,
.analysisMaintenanceItem2,
.analysisMaintenanceItem3,
.analysisMaintenanceItem4 {
.analysisMaintenanceLeft {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.analysisMaintenanceText1 {
font-size: 14px;
font-weight: 500;
color: #333333;
font-family: PingFang SC;
}
.analysisMaintenanceText2 {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
}
.analysisMaintenanceText3 {
font-size: 12px;
color: #666666;
font-family: PingFang SC;
}
}
.analysisMaintenanceRight {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
.analysisMaintenanceStatus {
display: flex;
align-items: center;
font-size: 12px;
color: #FFF;
font-weight: 400;
font-family: PingFang SC;
background-color: #EB5050;
padding: 6px 8px;
align-items: center;
border-radius: 6px;
}
}
.analysisMaintenanceRight2 {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
.analysisMaintenanceStatus {
display: flex;
align-items: center;
text-align: center;
font-size: 12px;
color: #FFF;
font-weight: 400;
font-family: PingFang SC;
background-color: #F6C644;
padding: 6px 8px;
border-radius: 6px;
}
.analysisMaintenanceStatus2 {
display: flex;
align-items: center;
text-align: center;
font-size: 12px;
color: #FFF;
font-weight: 400;
font-family: PingFang SC;
background-color: #4CA7F3;
padding: 6px 8px;
border-radius: 6px;
}
}
}
}
}
.analysisTableSection {
width: calc(100% - 30% - 10px);
background-color: #fff;
display: flex;
flex-direction: column;
.analysisTableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 11px 15px 5px 15px;
.analysisTableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.analysisTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
}
.analysisTableActions {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
margin-top: 5px;
padding: 0px 15px;
.analysisLeftActions {
display: flex;
align-items: center;
}
.analysisRightActions {
display: flex;
gap: 8px;
align-items: center;
}
.analysisActionButton {
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;
}
.analysisButtonIcon {
font-size: 14px;
font-weight: bold;
}
}
}
.analysisTableContainer {
flex: 1;
overflow: hidden;
margin: 10px 15px 0 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;
}
}
}
}
}

@ -1,805 +0,0 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Result, Select, Button, Segmented ,Input} from 'antd';
import { CheckCircleOutlined, ExportOutlined, SearchOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import StandardTable from '@/components/StandardTable';
import styles from './RealtimeMonitoring.less';
import eqicon1 from '@/assets/business_basic/eqicon1.png';
import eqicon2 from '@/assets/business_basic/iconre1.svg';
import eqicon3 from '@/assets/business_basic/iconre2.svg';
import eqicon4 from '@/assets/business_basic/iconre3.svg';
import ssbj1 from '@/assets/business_basic/ssbj1.svg';
import ssbj2 from '@/assets/business_basic/ssbj2.svg';
import ssbj3 from '@/assets/business_basic/ssbj3.svg';
import ssbj4 from '@/assets/business_basic/ssbj4.svg';
const RealtimeMonitoring = () => {
const chartRef = useRef(null);
const pieChartRef = 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: ['#4B69F1', '#FFD85A', '#A493FB', '#9AA5D5'],
legend: {
orient: 'vertical',
right: '10%',
top: 'center',
itemWidth: 10,
itemHeight: 3,
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: '离线' }
]
}]
};
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 (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: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
axisLabel: {
fontSize: 10
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed'
}
}
},
yAxis: {
type: 'value',
min: 0,
max: 10,
interval: 2,
axisLabel: {
formatter: '{value}',
fontSize: 10
},
splitLine: {
show: true,
lineStyle: {
type: 'dashed'
}
}
},
series: [
{
name: '故障报警',
type: 'line',
smooth: true,
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: [3, 5, 4, 6, 7, 5, 4]
},
{
name: '火灾报警',
type: 'line',
smooth: true,
lineStyle: {
width: 2,
color: '#FF928A'
},
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(255, 146, 138, 0.3)' },
{ offset: 1, color: 'rgba(255, 146, 138, 0.05)' }
]
}
},
symbol: 'circle',
symbolSize: 4,
itemStyle: {
color: '#fff',
borderColor: '#FF928A',
borderWidth: 1
},
data: [2, 3, 4, 3, 5, 4, 3]
}
]
};
chart.setOption(option);
// 响应式调整 - 使用多种方式监听容器尺寸变化
let resizeTimer = null;
const handleResize = () => {
// 防抖处理避免频繁调用resize
if (resizeTimer) {
clearTimeout(resizeTimer);
}
resizeTimer = setTimeout(() => {
if (chart && !chart.isDisposed()) {
chart.resize();
}
}, 50); // 减少延迟时间
};
// 监听窗口大小变化
window.addEventListener('resize', handleResize);
// 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题)
let resizeObserver = null;
if (window.ResizeObserver) {
resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
// 使用requestAnimationFrame确保在下一帧执行
requestAnimationFrame(() => {
handleResize();
});
}
});
resizeObserver.observe(chartRef.current);
}
// 额外监听父容器的尺寸变化
const parentContainer = chartRef.current?.parentElement;
let parentObserver = null;
if (parentContainer && window.ResizeObserver) {
parentObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
requestAnimationFrame(() => {
handleResize();
});
}
});
parentObserver.observe(parentContainer);
}
// 使用MutationObserver监听DOM结构变化菜单展开收起时
const mutationObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' &&
(mutation.attributeName === 'class' || mutation.attributeName === 'style')) {
// 延迟执行确保DOM更新完成
setTimeout(() => {
handleResize();
}, 200);
}
});
});
// 监听整个页面的class和style变化
mutationObserver.observe(document.body, {
attributes: true,
attributeFilter: ['class', 'style'],
subtree: true
});
return () => {
window.removeEventListener('resize', handleResize);
if (resizeObserver) {
resizeObserver.disconnect();
}
if (parentObserver) {
parentObserver.disconnect();
}
if (mutationObserver) {
mutationObserver.disconnect();
}
if (resizeTimer) {
clearTimeout(resizeTimer);
}
if (chart && !chart.isDisposed()) {
chart.dispose();
}
};
}
}, []);
// 表格列定义
const columns = [
{
title: '编号',
dataIndex: 'id',
key: 'id',
width: 60,
render: (text, record, index) => {
const page = pagination.current || 1;
const pageSize = pagination.pageSize || 5;
const number = (page - 1) * pageSize + index + 1;
return `0${number}`.slice(-2);
}
},
{
title: '设备编号',
dataIndex: 'deviceId',
key: 'deviceId',
width: 140,
},
{
title: '设备名称',
dataIndex: 'deviceName',
key: 'deviceName',
width: 130,
},
{
title: '所在位置',
dataIndex: 'installLocation',
key: 'installLocation',
width: 200,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
width: 80,
render: (text) => {
const statusMap = {
'故障': { color: '#FF4D4F', bg: '#FFF2F0' },
'预警': { color: '#FAAD14', bg: '#FFF3E9' },
'正常': { color: '#44BB5F', bg: '#D8F7DE' }
};
const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' };
return (
<span style={{
color: status.color,
backgroundColor: status.bg,
padding: '2px 8px',
borderRadius: '4px',
fontSize: '12px'
}}>
{text}
</span>
);
}
},
{
title: '最后更新',
dataIndex: 'lastMaintenance',
key: 'lastMaintenance',
width: 150,
},
{
title: '操作',
key: 'action',
width: 100,
render: (_, record) => (
<div>
<Button type="link" size="small" style={{
padding: '2px 8px',
fontSize: 12,
backgroundColor: 'transparent',
borderRadius: '4px'
}}>
详情
</Button>
</div>
),
},
];
// 模拟数据
const mockData = [
{
key: '1',
id: '001',
deviceId: 'DEV2024001',
deviceName: '烟雾探测器',
installLocation: '总部大楼1层大厅',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '2',
id: '002',
deviceId: 'DEV2024002',
deviceName: '烟雾探测器',
installLocation: '总部大楼3层 东区',
status: '预警',
lastMaintenance: '2025-09-10',
},
{
key: '3',
id: '003',
deviceId: 'DEV2024003',
deviceName: '消防栓',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '4',
id: '004',
deviceId: 'DEV2024001',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '5',
id: '005',
deviceId: 'DEV2024005',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '6',
id: '006',
deviceId: 'DEV2024006',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '预警',
lastMaintenance: '2025-09-10',
},
{
key: '7',
id: '007',
deviceId: 'DEV2024007',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '8',
id: '008',
deviceId: 'DEV2024008',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '9',
id: '009',
deviceId: 'DEV2024009',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '预警',
lastMaintenance: '2025-09-10',
},
{
key: '10',
id: '010',
deviceId: 'DEV2024010',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '故障',
lastMaintenance: '2025-09-10',
},
{
key: '11',
id: '011',
deviceId: 'DEV2024011',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '正常',
lastMaintenance: '2025-09-10',
},
{
key: '12',
id: '012',
deviceId: 'DEV2024012',
deviceName: '消防水泵',
installLocation: '总部大楼地下一层',
status: '预警',
lastMaintenance: '2025-09-10',
},
];
// 初始化数据
useEffect(() => {
setPagination(prev => ({ ...prev, total: mockData.length }));
}, []);
// 根据分页获取当前页数据
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const startIndex = (current - 1) * pageSize;
const endIndex = startIndex + pageSize;
return mockData.slice(startIndex, endIndex);
};
// 表格选择变化
const onSelectChange = (newSelectedRowKeys, newSelectedRows) => {
setSelectedRowKeys(newSelectedRowKeys);
setSelectedRows(newSelectedRows);
};
// 新增设备按钮点击事件
const handleAddDevice = () => {
console.log('新增设备');
// TODO: 实现新增设备逻辑
};
// 导出数据按钮点击事件
const handleExportData = () => {
console.log('导出数据');
// TODO: 实现导出数据逻辑
};
// 分页变化处理
const handleTableChange = (pagination) => {
setPagination(prev => ({
...prev,
current: pagination.current,
pageSize: pagination.pageSize,
}));
};
return (
<div className={styles.realtimeContainer}>
{/* 第一个div - 高度20% */}
<div className={styles.realtimeContainerTop}>
<div className={styles.realtimeSectionContent}>
<div className={styles.realtimeBlocksContainer}>
{/* 块1 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>设备总数</div>
<div className={styles.realtimeBlockNumber}>1280</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon1} alt="设备总数" className={styles.realtimeBlockImage} />
</div>
</div>
{/* 块2 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>本月报警</div>
<div className={styles.realtimeBlockNumber}>32</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon2} alt="高风险设备" className={styles.realtimeBlockImage} />
</div>
</div>
{/* 块3 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>巡检任务</div>
<div className={styles.realtimeBlockNumber}>158</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon3} alt="今日预警次数" className={styles.realtimeBlockImage} />
</div>
</div>
{/* 块4 */}
<div className={styles.realtimeBlockItem}>
<div className={styles.realtimeBlockLeft}>
<div className={styles.realtimeBlockTitle}>待处理隐患</div>
<div className={styles.realtimeBlockNumber}>19</div>
</div>
<div className={styles.realtimeBlockRight}>
<img src={eqicon4} alt="未处理预警" className={styles.realtimeBlockImage} />
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeContainerMiddle}>
<div className={styles.realtimeSectionContent}>
<div className={styles.realtimeMiddleBlock2}>
<div className={styles.realtimeMiddleBlock2Title}>
<div className={styles.realtimeTitleLeft}>
<div className={styles.realtimeTitleIcon}></div>
<div>设备运行参数</div>
</div>
</div>
<div className={styles.realtimeMiddleBlock2Chart} ref={chartRef}>
</div>
</div>
<div className={styles.realtimeMiddleBlock1}>
<div className={styles.realtimeBlock1Header}>
<div className={styles.realtimeBlock1Title}>
<div className={styles.realtimeTitleIcon}></div>
设备状态分布
</div>
</div>
{/* 设备状态饼图 */}
<div className={styles.realtimeDeviceStatusChart} ref={pieChartRef}>
</div>
</div>
<div className={styles.realtimeMiddleBlock12}>
<div className={styles.parameterTitle}>
<div className={styles.realtimeTitleIcon}></div>
参数实时监测
</div>
<div className={styles.parameterGridContainer}>
<div className={styles.parameterRow}>
<div className={styles.parameterBlock1}>
<div className={styles.warningBadge}>预警</div>
<div className={styles.blockContent}>
<div className={styles.parameterName}>消防水池水位</div>
<div className={styles.parameterValue}>2.35m</div>
<div className={styles.parameterRange}>正常范围: 2.5-4.0m</div>
</div>
</div>
<div className={styles.parameterBlock2}>
<div className={styles.warningBadge}>正常</div>
<div className={styles.blockContent}>
<div className={styles.parameterName}>管网水压</div>
<div className={styles.parameterValue}>0.68MPa</div>
<div className={styles.parameterRange}>正常范围: 0.6-0.8 Mpa</div>
</div>
</div>
</div>
<div className={styles.parameterRow}>
<div className={styles.parameterBlock3}>
<div className={styles.warningBadge}>正常</div>
<div className={styles.blockContent}>
<div className={styles.parameterName}>电器线路电流</div>
<div className={styles.parameterValue}>125 A</div>
<div className={styles.parameterRange}>正常范围: 0-150 A</div>
</div>
</div>
<div className={styles.parameterBlock4}>
<div className={styles.warningBadge}>故障</div>
<div className={styles.blockContent}>
<div className={styles.parameterName}>排烟系统风速</div>
<div className={styles.parameterValue}>8,2m/s</div>
<div className={styles.parameterRange}>正常范围: 10-15 m/s</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* 第三个div - 占满剩余位置 */}
<div className={styles.realtimeContainerBottom}>
<div className={styles.realtimeSectionContent}>
<div className={styles.realtimeLeftBlock}>
{/* 第一行块 - 蓝色方块加标题 */}
<div className={styles.realtimeLeftBlockTitle}>
<div className={styles.realtimeTitleIcon}></div>
<div>实时报警信息</div>
</div>
<div className={styles.realtimeDevelopmentContainer}>
{/* 块A */}
<div className={styles.realtimeDevelopmentBlockA}>
<img src={ssbj1} alt="ssbj1" style={{ width: '30px', height: '30px', marginRight: "10px" }} />
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>灭火器压力不足</div>
<div className={styles.realtimeSubText} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>2号楼3层 15分钟前</span>
<span style={{ color: '#999', fontSize: '11px' }}>10:23:34</span>
</div>
</div>
</div>
{/* 块B */}
<div className={styles.realtimeDevelopmentBlockB}>
<img src={ssbj2} alt="ssbj1" style={{ width: '30px', height: '30px', marginRight: "10px" }} />
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>烟雾探测器电池低电量</div>
<div className={styles.realtimeSubText} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>1号楼5层 1小时前</span>
<span style={{ color: '#999', fontSize: '11px' }}>09:15:22</span>
</div>
</div>
</div>
{/* 块C */}
<div className={styles.realtimeDevelopmentBlockC}>
<img src={ssbj3} alt="ssbj1" style={{ width: '30px', height: '30px', marginRight: "10px" }} />
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>消防栓维护到期</div>
<div className={styles.realtimeSubText} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>3号楼1层 2小时前</span>
<span style={{ color: '#999', fontSize: '11px' }}>08:45:18</span>
</div>
</div>
</div>
{/* 块D */}
<div className={styles.realtimeDevelopmentBlockD}>
<img src={ssbj4} alt="ssbj1" style={{ width: '30px', height: '30px', marginRight: "10px" }} />
<div className={styles.realtimeLeftContent}>
<div className={styles.realtimeMainText}>应急照明故障</div>
<div className={styles.realtimeSubText} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>地下停车场 3小时前</span>
<span style={{ color: '#999', fontSize: '11px' }}>07:30:45</span>
</div>
</div>
</div>
</div>
</div>
<div className={styles.realtimeRightBlock}>
{/* 表格 */}
<div className={styles.realtimeTableHeader}>
<div className={styles.realtimeTableTitle}>
<div className={styles.realtimeTitleIcon}></div>
<div>实时监控数据</div>
</div>
<div className={styles.realtimeTableActions}>
<Input
placeholder="搜索设备..."
style={{ width: 180, fontSize: 12, borderRadius: '2px' }}
suffix={<SearchOutlined style={{ color: '#999' }} />}
/>
<button
className={styles.realtimeActionButton}
onClick={handleAddDevice}
style={{
backgroundColor: '#2E4CD4',
color: '#fff',
border: '1px solid #2E4CD4',
borderRadius: '2px'
}}
>
<span className={styles.realtimeButtonIcon}>+</span>
<span>添加设备</span>
</button>
<button className={styles.realtimeActionButton} onClick={handleExportData}>
<span className={styles.realtimeButtonIcon}><ExportOutlined /></span>
<span>导出数据</span>
</button>
</div>
</div>
{/* 表格 */}
<div className={styles.realtimeTableContainer}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: pagination
}}
loading={loading}
selectionType="checkbox"
onSelectRow={onSelectChange}
onChange={handleTableChange}
pagination={{
...pagination,
showSizeChanger: false,
showQuickJumper: true,
showTotal: (total, range) =>
`${total}`,
}}
// scroll={{ x: 1200 }}
/>
</div>
</div>
</div>
</div>
</div>
);
};
export default RealtimeMonitoring;

@ -1,782 +0,0 @@
.realtimeContainer {
padding: 8px 6px 0px 6px;
height: 100%;
display: flex;
flex-direction: column;
gap: 10px;
// 第一个div - 高度20%
.realtimeContainerTop {
height: 16%;
// background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
.realtimeSectionContent {
height: 100%;
display: flex;
flex-direction: column;
// padding: 15px;
.realtimeBlocksContainer {
flex: 1;
display: flex;
gap: 10px;
height: 100%;
.realtimeBlockItem {
flex: 1;
height: 100%;
display: flex;
background: url('@/assets/business_basic/background_re.svg') no-repeat center center;
border-radius: 4px;
border: 2px solid #FFFFFF;
.realtimeBlockLeft {
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
padding: 15px;
padding-left: 20px;
gap: 8px;
.realtimeBlockTitle {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #333333;
line-height: 1.2;
}
.realtimeBlockNumber {
font-family: PingFang SC;
font-weight: 700;
font-size: 24px;
color: #333333;
line-height: 1.2;
}
.realtimeBlockChange {
font-family: PingFang SC;
font-weight: 400;
font-size: 12px;
color: #1269FF;
line-height: 1.2;
display: flex;
align-items: center;
gap: 4px;
.realtimeArrow {
font-size: 14px;
font-weight: bold;
}
.realtimeCheckIcon {
font-size: 16px;
color: #1269FF;
}
}
}
.realtimeBlockRight {
flex: 1;
height: 100%;
background-color: transparent;
border-radius: 0 4px 4px 0;
display: flex;
align-items: center;
justify-content: center;
.realtimeBlockImage {
// width: 80%;
height: 65%;
// height: 80%;
object-fit: contain;
margin-right: -5px;
}
}
}
}
}
}
// 第二个div - 高度39%
.realtimeContainerMiddle {
height: 33%;
border-radius: 4px;
display: flex;
flex-direction: column;
.realtimeSectionContent {
height: 100%;
display: flex;
display: flex;
gap: 10px;
height: 100%;
.realtimeMiddleBlock1 {
width: 25%;
height: 100%;
background: #fff;
border: 2px solid #fff;
position: relative;
padding: 0px 10px 10px 2px;
font-family: PingFang SC;
font-size: 14px;
color: #333333;
.realtimeBlock1Header {
position: absolute;
top: 5px;
left: 10px;
right: 10px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 10;
.realtimeBlock1Title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
.realtimeTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
}
.realtimeDeviceStatusChart {
position: absolute;
top: 35px;
left: 10px;
right: 10px;
bottom: 10px;
z-index: 10;
}
}
.realtimeMiddleBlock12 {
width: 30%;
height: 100%;
background-color: #fff;
display: flex;
flex-direction: column;
.parameterTitle {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 10px 10px 10px;
font-size: 14px;
font-weight: 500;
color: #333;
.realtimeTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
}
.parameterGridContainer {
display: flex;
flex-direction: column;
gap: 10px;
margin: 0 10px 10px 10px;
height: 100%; // 减去title高度和margin
.parameterRow {
display: flex;
gap: 10px;
flex: 1;
.parameterBlock1 {
flex: 1 1 50%;
background-color: #FFF5E9;
border: 1px solid #ECEDFC;
border-radius: 2px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
padding: 10px;
.warningBadge {
position: absolute;
top: 0;
right: 0;
width: 35px;
height: 20px;
background-color: #FF8800;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
font-weight: 420;
}
.blockContent {
display: flex;
flex-direction: column;
gap: 5px;
margin-left: 5px;
font-family: PingFang SC;
.parameterName {
font-size: 10px;
color: #666666;
font-weight: 400;
}
.parameterValue {
font-size: 16px;
color: #333333;
font-weight: 500;
}
.parameterRange {
font-size: 10px;
color: #666666;
font-weight: 400;
}
}
}
.parameterBlock2 {
flex: 1 1 50%;
background-color: #F3FFF6;
border: 1px solid #ECEDFC;
border-radius: 2px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
padding: 10px;
.warningBadge {
position: absolute;
top: 0;
right: 0;
width: 35px;
height: 20px;
background-color: #44BB5F;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
font-weight: 420;
}
.blockContent {
display: flex;
flex-direction: column;
gap: 5px;
margin-left: 5px;
font-family: PingFang SC;
.parameterName {
font-size: 10px;
color: #666666;
font-weight: 400;
}
.parameterValue {
font-size: 16px;
color: #333333;
font-weight: 500;
}
.parameterRange {
font-size: 10px;
color: #666666;
font-weight: 400;
}
}
}
.parameterBlock3 {
flex: 1;
background-color: #F3FFF6;
border: 1px solid #ECEDFC;
border-radius: 2px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
padding: 10px;
.warningBadge {
position: absolute;
top: 0;
right: 0;
width: 35px;
height: 20px;
background-color: #44BB5F;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
font-weight: 420;
}
.blockContent {
display: flex;
flex-direction: column;
gap: 5px;
margin-left: 5px;
font-family: PingFang SC;
.parameterName {
font-size: 10px;
color: #666666;
font-weight: 400;
}
.parameterValue {
font-size: 16px;
color: #333333;
font-weight: 500;
}
.parameterRange {
font-size: 10px;
color: #666666;
font-weight: 400;
}
}
}
.parameterBlock4 {
flex: 1;
background-color: #FFF2F1;
border: 1px solid #ECEDFC;
border-radius: 2px;
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
padding: 10px;
.warningBadge {
position: absolute;
top: 0;
right: 0;
width: 35px;
height: 20px;
background-color: #FF2526;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: #fff;
font-weight: 400;
}
.blockContent {
display: flex;
flex-direction: column;
gap: 5px;
margin-left: 5px;
.parameterName {
font-size: 10px;
color: #666666;
font-weight: 400;
}
.parameterValue {
font-size: 16px;
color: #333333;
font-weight: 500;
}
.parameterRange {
font-size: 10px;
color: #666666;
font-weight: 400;
}
}
}
}
}
}
.realtimeMiddleBlock2 {
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;
.realtimeMiddleBlock2Title {
display: flex;
justify-content: space-between;
align-items: center;
// margin-bottom: 10px;
.realtimeTitleIcon {
width: 3px;
height: 14px;
background-color: #2E4CD4;
}
.realtimeTitleLeft {
display: flex;
align-items: center;
gap: 8px;
font-weight: 500;
font-size: 14px;
color: #333333;
}
}
.realtimeMiddleBlock2Chart {
width: 100%;
height: 100%;
// min-height: 200px;
}
}
}
}
// 第三个div - 高度不超过45%
.realtimeContainerBottom {
height: 45%; // 限制高度不超过45%
max-height: 45%; // 确保最大高度不超过45%
display: flex;
flex-direction: column;
.realtimeSectionContent {
display: flex;
flex-direction: row;
gap: 10px;
padding: 0;
.realtimeLeftBlock {
width: 28%;
flex-shrink: 0;
height: 100%;
background: #fff;
// background-size: cover;
padding: 0;
display: flex;
flex-direction: column;
gap: 10px;
padding: 15px;
.realtimeLeftBlockTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
margin-bottom: 10px;
.realtimeTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.realtimeDevelopmentContainer {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 8px;
.realtimeDevelopmentBlock1 {
flex: 1;
background-color: #F1F7FF;
border-radius: 4px;
padding: 15px 20px;
display: flex;
align-items: center;
width: 100%;
}
.realtimeDevelopmentBlockA {
flex: 1;
background-color: #FFF0EF;
border-radius: 4px;
padding: 15px 20px;
display: flex;
align-items: center;
width: 100%;
.realtimeLeftContent {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
.realtimeMainText {
color: #333333;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 100%;
max-width: 500px;
}
.realtimeSubText {
color: #666666;
font-size: 12px;
font-weight: 400;
font-family: PingFang SC;
width: 100%;
max-width: 400px;
}
}
}
.realtimeDevelopmentBlockB {
flex: 1;
background-color: #FFF3DA;
border-radius: 4px;
padding: 15px 20px;
display: flex;
align-items: center;
width: 100%;
.realtimeLeftContent {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
.realtimeMainText {
color: #333333;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 100%;
max-width: 500px;
}
.realtimeSubText {
color: #666666;
font-size: 12px;
font-weight: 400;
font-family: PingFang SC;
width: 100%;
max-width: 400px;
}
}
}
.realtimeDevelopmentBlockC {
flex: 1;
background-color: #FFF0EF;
border-radius: 4px;
padding: 15px 20px;
display: flex;
align-items: center;
width: 100%;
.realtimeLeftContent {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
.realtimeMainText {
color: #333333;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 100%;
max-width: 500px;
}
.realtimeSubText {
color: #666666;
font-size: 12px;
font-weight: 400;
font-family: PingFang SC;
width: 100%;
max-width: 400px;
}
}
}
.realtimeDevelopmentBlockD {
flex: 1;
background-color: #FFF3DA;
border-radius: 4px;
padding: 15px 20px;
display: flex;
align-items: center;
width: 100%;
.realtimeLeftContent {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
min-width: 0;
.realtimeMainText {
color: #333333;
font-size: 14px;
font-weight: 500;
font-family: PingFang SC;
width: 100%;
max-width: 500px;
}
.realtimeSubText {
color: #666666;
font-size: 12px;
font-weight: 400;
font-family: PingFang SC;
width: 100%;
max-width: 400px;
}
}
}
}
}
.realtimeRightBlock {
width: calc(100% - 28% - 10px);
height: 100%;
background-color: #fff;
padding: 0;
display: flex;
flex-direction: column;
.realtimeTableHeader {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 15px 5px 15px;
.realtimeTableTitle {
display: flex;
align-items: center;
gap: 8px;
font-family: PingFang SC;
font-weight: 500;
font-size: 14px;
color: #333333;
.realtimeTitleIcon {
width: 3px;
height: 16px;
background-color: #2E4CD4;
}
}
.realtimeTableActions {
display: flex;
gap: 8px;
margin-top: 5px;
.realtimeActionButton {
display: flex;
align-items: center;
gap: 4px;
height: 28px;
border: 1px solid #DFE4F6;
border-radius: 2px;
color: #2E4CD4;
font-weight: 400;
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;
}
.realtimeButtonIcon {
font-size: 14px;
font-weight: bold;
}
}
}
}
.realtimeTableContainer {
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;
}
}
}
}
}
}

@ -1,15 +0,0 @@
import React from 'react';
import { Outlet } from '@umijs/max';
import './SystemContentList.less';
const SystemContentList = () => {
return (
<div className="systemContentContainer">
<div className="systemContentMain">
<Outlet />
</div>
</div>
);
};
export default SystemContentList;

@ -1,10 +0,0 @@
.systemContentContainer {
width: 100%;
height: 100%;
}
.systemContentMain {
width: 100%;
height: 100%;
padding: 12px;
}

@ -1,408 +0,0 @@
import React from 'react';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
SearchOutlined,
RedoOutlined,
DownOutlined,
ExclamationCircleFilled,
UpOutlined,
InfoCircleFilled,
QuestionCircleFilled,
DownloadOutlined
} from '@ant-design/icons';
import {connect, history} from '@umijs/max';
import {Button, Card, Divider, Dropdown, message, Modal, Popconfirm, Space, Switch, Tag, Row, Col, Tree} from 'antd';
import StandardTable from '@/components/StandardTable';
import { MyIcon } from "@/components/Icon"
import style from "@/global.less";
import styles from './SystemMenuList.less';
import datadictionary from "@/utils/dataDictionary";
import {formatDate} from "@/utils/formatUtils";
import { formatDictText, checkButtonAuthority } from "@/utils/globalCommon";
const { confirm } = Modal;
// 菜单类型
const menu_type = datadictionary.menu_type || [];
const menu_status = datadictionary.menu_status || [];
const menu_level = datadictionary.menu_level || [];
const mockData = {
list: [
{
id: '1',
name: '效率管理',
code: 'hrefficiency',
type: '1',
level: '1',
status: '1',
path: '/topnavbar00/hrefficiency',
component: '',
icon: 'setting',
createTime: '2023-01-01 10:00:00',
createUser: 'admin',
children: [
{
id: '2',
name: '工时仪表盘',
code: 'timesheet',
type: '2',
level: '2',
status: '1',
path: '/topnavbar00/hrefficiency/timesheet',
component: './hrefficiency_timesheet/TimeSheetList',
icon: 'dashboard',
createTime: '2023-01-01 10:05:00',
createUser: 'admin'
},
{
id: '3',
name: '员工仪表盘',
code: 'staffsheet',
type: '2',
level: '2',
status: '1',
path: '/topnavbar00/hrefficiency/staffsheet',
component: './hrefficiency_staffsheet/StaffSheetList',
icon: 'user',
createTime: '2023-01-01 10:10:00',
createUser: 'admin'
}
]
},
{
id: '4',
name: '系统管理',
code: 'system',
type: '1',
level: '1',
status: '1',
path: '/topnavbar00/hrefficiency/system',
component: './nav_system_content/SystemContentList',
icon: 'control',
createTime: '2023-01-02 14:30:00',
createUser: 'admin'
}
],
pagination: {
total: 2,
pageSize: 10,
current: 1
}
};
@connect(({ systemMenu }) => ({
systemMenu
}))
export default class SystemMenuList extends React.Component {
state = {
selectedRows: [],
advancedFormVisible: false,
filterData: {},
pagination: {
pageSize: 10,
current: 1
},
tableData: mockData.list,
treeData: [],
expandedKeys: []
};
componentDidMount() {
this.getMenuList();
this.buildTreeData();
}
// 获取菜单列表
getMenuList = () => {
const { dispatch } = this.props;
const { pagination, filterData } = this.state;
try {
dispatch({
type: 'systemMenu/fetchList',
payload: {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...filterData
},
callback: (res) => {
this.setState({
tableData: res?.list || mockData.list,
pagination: {
...pagination,
total: res?.total || mockData.pagination.total
}
}, () => {
this.buildTreeData();
});
}
});
} catch (error) {
console.error('获取菜单列表失败:', error);
this.setState({
tableData: mockData.list,
pagination: mockData.pagination
}, () => {
this.buildTreeData();
});
}
};
// 构建树数据
buildTreeData = () => {
const { tableData } = this.state;
const treeData = this.transformToTree(tableData);
this.setState({ treeData });
};
// 转换为树结构
transformToTree = (data) => {
return data.map(item => {
const node = {
title: item.name,
key: item.id,
dataRef: item
};
if (item.children && item.children.length > 0) {
node.children = this.transformToTree(item.children);
}
return node;
});
};
// 表格选择变化
handleSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({ selectedRows });
};
// 分页变化
handleTableChange = (pagination) => {
this.setState({
pagination: {
...this.state.pagination,
current: pagination.current
}
}, () => {
this.getMenuList();
});
};
// 新增菜单
handleAdd = () => {
// 新增逻辑
message.success('新增菜单功能');
};
// 编辑菜单
handleEdit = (record) => {
// 编辑逻辑
message.success('编辑菜单功能');
};
// 删除菜单
handleDelete = (record) => {
confirm({
title: '确定要删除这个菜单吗?',
icon: <ExclamationCircleFilled />,
onOk: () => {
// 删除逻辑
message.success('删除菜单成功');
}
});
};
// 批量删除
handleBatchDelete = () => {
const { selectedRows } = this.state;
if (selectedRows.length === 0) {
message.warning('请选择要删除的菜单');
return;
}
confirm({
title: `确定要删除选中的${selectedRows.length}个菜单吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 批量删除逻辑
message.success('批量删除菜单成功');
}
});
};
// 启用/禁用菜单
handleStatusChange = (record) => {
const newStatus = record.status === '1' ? '0' : '1';
confirm({
title: `确定要${newStatus === '1' ? '启用' : '禁用'}这个菜单吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 状态变更逻辑
message.success(`${newStatus === '1' ? '启用' : '禁用'}菜单成功`);
}
});
};
// 展开/折叠树节点
handleExpand = (expandedKeys) => {
this.setState({ expandedKeys });
};
// 点击树节点
handleTreeSelect = (selectedKeys, info) => {
// 树节点选择逻辑
console.log('Selected', selectedKeys, info);
};
render() {
const { selectedRows, tableData, pagination, treeData, expandedKeys } = this.state;
const columns = [
{
title: '菜单名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
},
{
title: '菜单编码',
dataIndex: 'code',
key: 'code',
ellipsis: true
},
{
title: '菜单类型',
dataIndex: 'type',
key: 'type',
render: text => formatDictText(menu_type, text)
},
{
title: '菜单级别',
dataIndex: 'level',
key: 'level',
render: text => formatDictText(menu_level, text)
},
{
title: '路径',
dataIndex: 'path',
key: 'path',
ellipsis: true
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: text => (
<Tag color={text === '1' ? 'green' : 'red'}>
{text === '1' ? '启用' : '禁用'}
</Tag>
)
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true
},
{
title: '创建人',
dataIndex: 'createUser',
key: 'createUser',
ellipsis: true
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 160,
render: (_, record) => (
<Space size="middle">
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => this.handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => this.handleDelete(record)}
>
删除
</Button>
<Switch
checked={record.status === '1'}
onChange={() => this.handleStatusChange(record)}
/>
</Space>
)
}
];
return (
<div className={style.contentWrapper}>
<Card className={style.contentCard}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<h2 className={styles.title}>菜单配置</h2>
</div>
<div className={styles.headerRight}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={this.handleAdd}
>
新增菜单
</Button>
<Button
danger
style={{ marginLeft: 8 }}
onClick={this.handleBatchDelete}
disabled={selectedRows.length === 0}
>
批量删除
</Button>
</div>
</div>
<Divider />
<div className={styles.menuContent}>
<div className={styles.menuTreeSection}>
<h3 className={styles.sectionTitle}>菜单树</h3>
<div className={styles.menuTree}>
<Tree
treeData={treeData}
expandedKeys={expandedKeys}
onExpand={this.handleExpand}
onSelect={this.handleTreeSelect}
className={styles.tree}
/>
</div>
</div>
<Divider />
<StandardTable
rowKey="id"
selectedRows={selectedRows}
data={{
list: tableData,
pagination
}}
columns={columns}
onSelectRow={this.handleSelectChange}
onChange={this.handleTableChange}
/>
</div>
</Card>
</div>
);
}
}

@ -1,112 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.headerLeft {
display: flex;
align-items: center;
}
.headerRight {
display: flex;
align-items: center;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.filterSection {
margin-bottom: 16px;
}
.filterRow {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.filterItem {
margin-right: 16px;
}
.actionButtons {
display: flex;
align-items: center;
}
.batchAction {
margin-right: 12px;
}
.formContainer {
padding: 24px;
background-color: #f5f5f5;
}
.formHeader {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e8e8e8;
}
.formTitle {
margin: 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.formContent {
background-color: #fff;
padding: 24px;
border-radius: 2px;
}
.formFooter {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
.formButton {
margin-left: 12px;
}
.menuContent {
width: 100%;
}
.menuTreeSection {
margin-bottom: 20px;
}
.sectionTitle {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.menuTree {
border: 1px solid #e8e8e8;
min-height: 200px;
}
.tree {
background-color: transparent;
}
.tree .ant-tree-node-selected {
background-color: #e6f7ff;
}
.tree .ant-tree-node-selected:hover {
background-color: #bae7ff;
}

@ -1,314 +0,0 @@
import React from 'react';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
SearchOutlined,
RedoOutlined,
DownOutlined,
ExclamationCircleFilled,
UpOutlined,
InfoCircleFilled,
QuestionCircleFilled,
DownloadOutlined
} from '@ant-design/icons';
import {connect, history} from '@umijs/max';
import {Button, Card, Divider, Dropdown, message, Modal, Popconfirm, Space, Switch, Tag, Row, Col} from 'antd';
import StandardTable from '@/components/StandardTable';
import { MyIcon } from "@/components/Icon"
import style from "@/global.less";
import styles from './SystemOrganizationList.less';
import datadictionary from "@/utils/dataDictionary";
import {formatDate} from "@/utils/formatUtils";
import { formatDictText, checkButtonAuthority } from "@/utils/globalCommon";
const { confirm } = Modal;
// 组织类型
const organization_type = datadictionary.organization_type || [];
const organization_status = datadictionary.organization_status || [];
const mockData = {
list: [
{
id: '1',
name: '总部',
code: 'HQ',
type: '1',
parentId: '0',
status: '1',
createTime: '2023-01-01 10:00:00',
createUser: 'admin'
},
{
id: '2',
name: '研发部',
code: 'R&D',
type: '2',
parentId: '1',
status: '1',
createTime: '2023-01-02 14:30:00',
createUser: 'admin'
},
{
id: '3',
name: '市场部',
code: 'MKT',
type: '2',
parentId: '1',
status: '1',
createTime: '2023-01-03 09:15:00',
createUser: 'admin'
}
],
pagination: {
total: 3,
pageSize: 10,
current: 1
}
};
@connect(({ systemOrganization }) => ({
systemOrganization
}))
export default class SystemOrganizationList extends React.Component {
state = {
selectedRows: [],
advancedFormVisible: false,
filterData: {},
pagination: {
pageSize: 10,
current: 1
},
tableData: mockData.list
};
componentDidMount() {
this.getOrganizationList();
}
// 获取组织列表
getOrganizationList = () => {
const { dispatch } = this.props;
const { pagination, filterData } = this.state;
try {
dispatch({
type: 'systemOrganization/fetchList',
payload: {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...filterData
},
callback: (res) => {
this.setState({
tableData: res?.list || mockData.list,
pagination: {
...pagination,
total: res?.total || mockData.pagination.total
}
});
}
});
} catch (error) {
console.error('获取组织列表失败:', error);
this.setState({
tableData: mockData.list,
pagination: mockData.pagination
});
}
};
// 表格选择变化
handleSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({ selectedRows });
};
// 分页变化
handleTableChange = (pagination) => {
this.setState({
pagination: {
...this.state.pagination,
current: pagination.current
}
}, () => {
this.getOrganizationList();
});
};
// 新增组织
handleAdd = () => {
// 新增逻辑
message.success('新增组织功能');
};
// 编辑组织
handleEdit = (record) => {
// 编辑逻辑
message.success('编辑组织功能');
};
// 删除组织
handleDelete = (record) => {
confirm({
title: '确定要删除这个组织吗?',
icon: <ExclamationCircleFilled />,
onOk: () => {
// 删除逻辑
message.success('删除组织成功');
}
});
};
// 批量删除
handleBatchDelete = () => {
const { selectedRows } = this.state;
if (selectedRows.length === 0) {
message.warning('请选择要删除的组织');
return;
}
confirm({
title: `确定要删除选中的${selectedRows.length}个组织吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 批量删除逻辑
message.success('批量删除组织成功');
}
});
};
// 启用/禁用组织
handleStatusChange = (record) => {
const newStatus = record.status === '1' ? '0' : '1';
confirm({
title: `确定要${newStatus === '1' ? '启用' : '禁用'}这个组织吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 状态变更逻辑
message.success(`${newStatus === '1' ? '启用' : '禁用'}组织成功`);
}
});
};
render() {
const { selectedRows, tableData, pagination } = this.state;
const columns = [
{
title: '组织名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
},
{
title: '组织编码',
dataIndex: 'code',
key: 'code',
ellipsis: true
},
{
title: '组织类型',
dataIndex: 'type',
key: 'type',
render: text => formatDictText(organization_type, text)
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: text => (
<Tag color={text === '1' ? 'green' : 'red'}>
{text === '1' ? '启用' : '禁用'}
</Tag>
)
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true
},
{
title: '创建人',
dataIndex: 'createUser',
key: 'createUser',
ellipsis: true
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 160,
render: (_, record) => (
<Space size="middle">
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => this.handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => this.handleDelete(record)}
>
删除
</Button>
<Switch
checked={record.status === '1'}
onChange={() => this.handleStatusChange(record)}
/>
</Space>
)
}
];
return (
<div className={style.contentWrapper}>
<Card className={style.contentCard}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<h2 className={styles.title}>组织管理</h2>
</div>
<div className={styles.headerRight}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={this.handleAdd}
>
新增组织
</Button>
<Button
danger
style={{ marginLeft: 8 }}
onClick={this.handleBatchDelete}
disabled={selectedRows.length === 0}
>
批量删除
</Button>
</div>
</div>
<Divider />
<StandardTable
rowKey="id"
selectedRows={selectedRows}
data={{
list: tableData,
pagination
}}
columns={columns}
onSelectRow={this.handleSelectChange}
onChange={this.handleTableChange}
/>
</Card>
</div>
);
}
}

@ -1,80 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.headerLeft {
display: flex;
align-items: center;
}
.headerRight {
display: flex;
align-items: center;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.filterSection {
margin-bottom: 16px;
}
.filterRow {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.filterItem {
margin-right: 16px;
}
.actionButtons {
display: flex;
align-items: center;
}
.batchAction {
margin-right: 12px;
}
.formContainer {
padding: 24px;
background-color: #f5f5f5;
}
.formHeader {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e8e8e8;
}
.formTitle {
margin: 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.formContent {
background-color: #fff;
padding: 24px;
border-radius: 2px;
}
.formFooter {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
.formButton {
margin-left: 12px;
}

@ -1,333 +0,0 @@
import React from 'react';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
SearchOutlined,
RedoOutlined,
DownOutlined,
ExclamationCircleFilled,
UpOutlined,
InfoCircleFilled,
QuestionCircleFilled,
DownloadOutlined
} from '@ant-design/icons';
import {connect, history} from '@umijs/max';
import {Button, Card, Divider, Dropdown, message, Modal, Popconfirm, Space, Switch, Tag, Row, Col} from 'antd';
import StandardTable from '@/components/StandardTable';
import { MyIcon } from "@/components/Icon"
import style from "@/global.less";
import styles from './SystemRoleList.less';
import datadictionary from "@/utils/dataDictionary";
import {formatDate} from "@/utils/formatUtils";
import { formatDictText, checkButtonAuthority } from "@/utils/globalCommon";
const { confirm } = Modal;
// 角色类型
const role_type = datadictionary.role_type || [];
const role_status = datadictionary.role_status || [];
const mockData = {
list: [
{
id: '1',
name: '超级管理员',
code: 'SUPER_ADMIN',
type: '1',
status: '1',
createTime: '2023-01-01 10:00:00',
createUser: 'admin',
remark: '系统最高权限角色'
},
{
id: '2',
name: '部门管理员',
code: 'DEPT_ADMIN',
type: '2',
status: '1',
createTime: '2023-01-02 14:30:00',
createUser: 'admin',
remark: '部门级管理权限'
},
{
id: '3',
name: '普通用户',
code: 'NORMAL_USER',
type: '3',
status: '1',
createTime: '2023-01-03 09:15:00',
createUser: 'admin',
remark: '基础功能访问权限'
}
],
pagination: {
total: 3,
pageSize: 10,
current: 1
}
};
@connect(({ systemRole }) => ({
systemRole
}))
export default class SystemRoleList extends React.Component {
state = {
selectedRows: [],
advancedFormVisible: false,
filterData: {},
pagination: {
pageSize: 10,
current: 1
},
tableData: mockData.list
};
componentDidMount() {
this.getRoleList();
}
// 获取角色列表
getRoleList = () => {
const { dispatch } = this.props;
const { pagination, filterData } = this.state;
try {
dispatch({
type: 'systemRole/fetchList',
payload: {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...filterData
},
callback: (res) => {
this.setState({
tableData: res?.list || mockData.list,
pagination: {
...pagination,
total: res?.total || mockData.pagination.total
}
});
}
});
} catch (error) {
console.error('获取角色列表失败:', error);
this.setState({
tableData: mockData.list,
pagination: mockData.pagination
});
}
};
// 表格选择变化
handleSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({ selectedRows });
};
// 分页变化
handleTableChange = (pagination) => {
this.setState({
pagination: {
...this.state.pagination,
current: pagination.current
}
}, () => {
this.getRoleList();
});
};
// 新增角色
handleAdd = () => {
// 新增逻辑
message.success('新增角色功能');
};
// 编辑角色
handleEdit = (record) => {
// 编辑逻辑
message.success('编辑角色功能');
};
// 角色授权
handleAuth = (record) => {
// 授权逻辑
message.success('角色授权功能');
};
// 删除角色
handleDelete = (record) => {
confirm({
title: '确定要删除这个角色吗?',
icon: <ExclamationCircleFilled />,
onOk: () => {
// 删除逻辑
message.success('删除角色成功');
}
});
};
// 批量删除
handleBatchDelete = () => {
const { selectedRows } = this.state;
if (selectedRows.length === 0) {
message.warning('请选择要删除的角色');
return;
}
confirm({
title: `确定要删除选中的${selectedRows.length}个角色吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 批量删除逻辑
message.success('批量删除角色成功');
}
});
};
// 启用/禁用角色
handleStatusChange = (record) => {
const newStatus = record.status === '1' ? '0' : '1';
confirm({
title: `确定要${newStatus === '1' ? '启用' : '禁用'}这个角色吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 状态变更逻辑
message.success(`${newStatus === '1' ? '启用' : '禁用'}角色成功`);
}
});
};
render() {
const { selectedRows, tableData, pagination } = this.state;
const columns = [
{
title: '角色名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
},
{
title: '角色编码',
dataIndex: 'code',
key: 'code',
ellipsis: true
},
{
title: '角色类型',
dataIndex: 'type',
key: 'type',
render: text => formatDictText(role_type, text)
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: text => (
<Tag color={text === '1' ? 'green' : 'red'}>
{text === '1' ? '启用' : '禁用'}
</Tag>
)
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true
},
{
title: '创建人',
dataIndex: 'createUser',
key: 'createUser',
ellipsis: true
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
ellipsis: true
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 200,
render: (_, record) => (
<Space size="middle">
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => this.handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
onClick={() => this.handleAuth(record)}
>
授权
</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => this.handleDelete(record)}
>
删除
</Button>
<Switch
checked={record.status === '1'}
onChange={() => this.handleStatusChange(record)}
/>
</Space>
)
}
];
return (
<div className={style.contentWrapper}>
<Card className={style.contentCard}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<h2 className={styles.title}>角色配置</h2>
</div>
<div className={styles.headerRight}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={this.handleAdd}
>
新增角色
</Button>
<Button
danger
style={{ marginLeft: 8 }}
onClick={this.handleBatchDelete}
disabled={selectedRows.length === 0}
>
批量删除
</Button>
</div>
</div>
<Divider />
<StandardTable
rowKey="id"
selectedRows={selectedRows}
data={{
list: tableData,
pagination
}}
columns={columns}
onSelectRow={this.handleSelectChange}
onChange={this.handleTableChange}
/>
</Card>
</div>
);
}
}

@ -1,80 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.headerLeft {
display: flex;
align-items: center;
}
.headerRight {
display: flex;
align-items: center;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.filterSection {
margin-bottom: 16px;
}
.filterRow {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.filterItem {
margin-right: 16px;
}
.actionButtons {
display: flex;
align-items: center;
}
.batchAction {
margin-right: 12px;
}
.formContainer {
padding: 24px;
background-color: #f5f5f5;
}
.formHeader {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e8e8e8;
}
.formTitle {
margin: 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.formContent {
background-color: #fff;
padding: 24px;
border-radius: 2px;
}
.formFooter {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
.formButton {
margin-left: 12px;
}

@ -1,402 +0,0 @@
import React from 'react';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
SearchOutlined,
RedoOutlined,
DownOutlined,
ExclamationCircleFilled,
UpOutlined,
InfoCircleFilled,
QuestionCircleFilled,
DownloadOutlined
} from '@ant-design/icons';
import {connect, history} from '@umijs/max';
import {Button, Card, Divider, Dropdown, message, Modal, Popconfirm, Space, Switch, Tag, Row, Col, Tree} from 'antd';
import StandardTable from '@/components/StandardTable';
import { MyIcon } from "@/components/Icon"
import style from "@/global.less";
import styles from './SystemMenuList.less';
import datadictionary from "@/utils/dataDictionary";
import {formatDate} from "@/utils/formatUtils";
import { formatDictText, checkButtonAuthority } from "@/utils/globalCommon";
const { confirm } = Modal;
// 菜单类型
const menu_type = datadictionary.menu_type || [];
const menu_status = datadictionary.menu_status || [];
const menu_level = datadictionary.menu_level || [];
const mockData = {
list: [
{
id: '1',
name: '效率管理',
code: 'hrefficiency',
type: '1',
level: '1',
status: '1',
path: '/topnavbar00/hrefficiency',
component: '',
icon: 'setting',
createTime: '2023-01-01 10:00:00',
createUser: 'admin',
children: [
{
id: '2',
name: '工时仪表盘',
code: 'timesheet',
type: '2',
level: '2',
status: '1',
path: '/topnavbar00/hrefficiency/timesheet',
component: './hrefficiency_timesheet/TimeSheetList',
icon: 'dashboard',
createTime: '2023-01-01 10:05:00',
createUser: 'admin'
},
{
id: '3',
name: '员工仪表盘',
code: 'staffsheet',
type: '2',
level: '2',
status: '1',
path: '/topnavbar00/hrefficiency/staffsheet',
component: './hrefficiency_staffsheet/StaffSheetList',
icon: 'user',
createTime: '2023-01-01 10:10:00',
createUser: 'admin'
}
]
},
{
id: '4',
name: '系统管理',
code: 'system',
type: '1',
level: '1',
status: '1',
path: '/topnavbar00/hrefficiency/system',
component: './nav_system_content/SystemContentList',
icon: 'control',
createTime: '2023-01-02 14:30:00',
createUser: 'admin'
}
],
pagination: {
total: 2,
pageSize: 10,
current: 1
}
};
@connect(({ systemMenu, loading }) => ({
systemMenu,
loading: loading.models.systemMenu
}))
export default class SystemMenuList extends React.Component {
state = {
selectedRows: [],
loading: false,
advancedFormVisible: false,
filterData: {},
pagination: {
pageSize: 10,
current: 1
},
tableData: mockData.list,
treeData: [],
expandedKeys: []
};
componentDidMount() {
this.getMenuList();
this.buildTreeData();
}
// 获取菜单列表
getMenuList = () => {
const { dispatch } = this.props;
const { pagination, filterData } = this.state;
this.setState({ loading: true });
dispatch({
type: 'systemMenu/fetchList',
payload: {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...filterData
},
callback: (res) => {
this.setState({
loading: false,
tableData: res?.list || [],
pagination: {
...pagination,
total: res?.total || 0
}
}, () => {
this.buildTreeData();
});
}
});
};
// 构建树数据
buildTreeData = () => {
const { tableData } = this.state;
const treeData = this.transformToTree(tableData);
this.setState({ treeData });
};
// 转换为树结构
transformToTree = (data) => {
return data.map(item => {
const node = {
title: item.name,
key: item.id,
dataRef: item
};
if (item.children && item.children.length > 0) {
node.children = this.transformToTree(item.children);
}
return node;
});
};
// 表格选择变化
handleSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({ selectedRows });
};
// 分页变化
handleTableChange = (pagination) => {
this.setState({
pagination: {
...this.state.pagination,
current: pagination.current
}
}, () => {
this.getMenuList();
});
};
// 新增菜单
handleAdd = () => {
// 新增逻辑
message.success('新增菜单功能');
};
// 编辑菜单
handleEdit = (record) => {
// 编辑逻辑
message.success('编辑菜单功能');
};
// 删除菜单
handleDelete = (record) => {
confirm({
title: '确定要删除这个菜单吗?',
icon: <ExclamationCircleFilled />,
onOk: () => {
// 删除逻辑
message.success('删除菜单成功');
}
});
};
// 批量删除
handleBatchDelete = () => {
const { selectedRows } = this.state;
if (selectedRows.length === 0) {
message.warning('请选择要删除的菜单');
return;
}
confirm({
title: `确定要删除选中的${selectedRows.length}个菜单吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 批量删除逻辑
message.success('批量删除菜单成功');
}
});
};
// 启用/禁用菜单
handleStatusChange = (record) => {
const newStatus = record.status === '1' ? '0' : '1';
confirm({
title: `确定要${newStatus === '1' ? '启用' : '禁用'}这个菜单吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 状态变更逻辑
message.success(`${newStatus === '1' ? '启用' : '禁用'}菜单成功`);
}
});
};
// 展开/折叠树节点
handleExpand = (expandedKeys) => {
this.setState({ expandedKeys });
};
// 点击树节点
handleTreeSelect = (selectedKeys, info) => {
// 树节点选择逻辑
console.log('Selected', selectedKeys, info);
};
render() {
const { selectedRows, loading, tableData, pagination, treeData, expandedKeys } = this.state;
const columns = [
{
title: '菜单名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
},
{
title: '菜单编码',
dataIndex: 'code',
key: 'code',
ellipsis: true
},
{
title: '菜单类型',
dataIndex: 'type',
key: 'type',
render: text => formatDictText(menu_type, text)
},
{
title: '菜单级别',
dataIndex: 'level',
key: 'level',
render: text => formatDictText(menu_level, text)
},
{
title: '路径',
dataIndex: 'path',
key: 'path',
ellipsis: true
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: text => (
<Tag color={text === '1' ? 'green' : 'red'}>
{text === '1' ? '启用' : '禁用'}
</Tag>
)
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true
},
{
title: '创建人',
dataIndex: 'createUser',
key: 'createUser',
ellipsis: true
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 160,
render: (_, record) => (
<Space size="middle">
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => this.handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => this.handleDelete(record)}
>
删除
</Button>
<Switch
checked={record.status === '1'}
onChange={() => this.handleStatusChange(record)}
/>
</Space>
)
}
];
return (
<div className={style.contentWrapper}>
<Card className={style.contentCard}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<h2 className={styles.title}>菜单配置</h2>
</div>
<div className={styles.headerRight}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={this.handleAdd}
>
新增菜单
</Button>
<Button
danger
style={{ marginLeft: 8 }}
onClick={this.handleBatchDelete}
disabled={selectedRows.length === 0}
>
批量删除
</Button>
</div>
</div>
<Divider />
<div className={styles.menuContent}>
<div className={styles.menuTreeSection}>
<h3 className={styles.sectionTitle}>菜单树</h3>
<div className={styles.menuTree}>
<Tree
treeData={treeData}
expandedKeys={expandedKeys}
onExpand={this.handleExpand}
onSelect={this.handleTreeSelect}
className={styles.tree}
/>
</div>
</div>
<Divider />
<StandardTable
rowKey="id"
selectedRows={selectedRows}
loading={loading}
data={{
list: tableData,
pagination
}}
columns={columns}
onSelectRow={this.handleSelectChange}
onChange={this.handleTableChange}
/>
</div>
</Card>
</div>
);
}
}

@ -1,115 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.headerLeft {
display: flex;
align-items: center;
}
.headerRight {
display: flex;
align-items: center;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.filterSection {
margin-bottom: 16px;
}
.filterRow {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.filterItem {
margin-right: 16px;
}
.actionButtons {
display: flex;
align-items: center;
}
.batchAction {
margin-right: 12px;
}
.formContainer {
padding: 24px;
background-color: #f5f5f5;
}
.formHeader {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e8e8e8;
}
.formTitle {
margin: 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.formContent {
background-color: #fff;
padding: 24px;
border-radius: 2px;
}
.formFooter {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
.formButton {
margin-left: 12px;
}
.menuContent {
width: 100%;
}
.menuTreeSection {
margin-bottom: 24px;
}
.sectionTitle {
margin: 0 0 12px 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.menuTree {
background-color: #fafafa;
padding: 16px;
border-radius: 4px;
border: 1px solid #e8e8e8;
min-height: 200px;
}
.tree {
background-color: transparent;
}
.tree .ant-tree-node-selected {
background-color: #e6f7ff;
}
.tree .ant-tree-node-selected:hover {
background-color: #bae7ff;
}

@ -1,310 +0,0 @@
import React from 'react';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
SearchOutlined,
RedoOutlined,
DownOutlined,
ExclamationCircleFilled,
UpOutlined,
InfoCircleFilled,
QuestionCircleFilled,
DownloadOutlined
} from '@ant-design/icons';
import {connect, history} from '@umijs/max';
import {Button, Card, Divider, Dropdown, message, Modal, Popconfirm, Space, Switch, Tag, Row, Col} from 'antd';
import StandardTable from '@/components/StandardTable';
import { MyIcon } from "@/components/Icon"
import style from "@/global.less";
import styles from './SystemOrganizationList.less';
import datadictionary from "@/utils/dataDictionary";
import {formatDate} from "@/utils/formatUtils";
import { formatDictText, checkButtonAuthority } from "@/utils/globalCommon";
const { confirm } = Modal;
// 组织类型
const organization_type = datadictionary.organization_type || [];
const organization_status = datadictionary.organization_status || [];
const mockData = {
list: [
{
id: '1',
name: '总部',
code: 'HQ',
type: '1',
parentId: '0',
status: '1',
createTime: '2023-01-01 10:00:00',
createUser: 'admin'
},
{
id: '2',
name: '研发部',
code: 'R&D',
type: '2',
parentId: '1',
status: '1',
createTime: '2023-01-02 14:30:00',
createUser: 'admin'
},
{
id: '3',
name: '市场部',
code: 'MKT',
type: '2',
parentId: '1',
status: '1',
createTime: '2023-01-03 09:15:00',
createUser: 'admin'
}
],
pagination: {
total: 3,
pageSize: 10,
current: 1
}
};
@connect(({ systemOrganization, loading }) => ({
systemOrganization,
loading: loading.models.systemOrganization
}))
export default class SystemOrganizationList extends React.Component {
state = {
selectedRows: [],
loading: false,
advancedFormVisible: false,
filterData: {},
pagination: {
pageSize: 10,
current: 1
},
tableData: mockData.list
};
componentDidMount() {
this.getOrganizationList();
}
// 获取组织列表
getOrganizationList = () => {
const { dispatch } = this.props;
const { pagination, filterData } = this.state;
this.setState({ loading: true });
dispatch({
type: 'systemOrganization/fetchList',
payload: {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...filterData
},
callback: (res) => {
this.setState({
loading: false,
tableData: res?.list || [],
pagination: {
...pagination,
total: res?.total || 0
}
});
}
});
};
// 表格选择变化
handleSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({ selectedRows });
};
// 分页变化
handleTableChange = (pagination) => {
this.setState({
pagination: {
...this.state.pagination,
current: pagination.current
}
}, () => {
this.getOrganizationList();
});
};
// 新增组织
handleAdd = () => {
// 新增逻辑
message.success('新增组织功能');
};
// 编辑组织
handleEdit = (record) => {
// 编辑逻辑
message.success('编辑组织功能');
};
// 删除组织
handleDelete = (record) => {
confirm({
title: '确定要删除这个组织吗?',
icon: <ExclamationCircleFilled />,
onOk: () => {
// 删除逻辑
message.success('删除组织成功');
}
});
};
// 批量删除
handleBatchDelete = () => {
const { selectedRows } = this.state;
if (selectedRows.length === 0) {
message.warning('请选择要删除的组织');
return;
}
confirm({
title: `确定要删除选中的${selectedRows.length}个组织吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 批量删除逻辑
message.success('批量删除组织成功');
}
});
};
// 启用/禁用组织
handleStatusChange = (record) => {
const newStatus = record.status === '1' ? '0' : '1';
confirm({
title: `确定要${newStatus === '1' ? '启用' : '禁用'}这个组织吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 状态变更逻辑
message.success(`${newStatus === '1' ? '启用' : '禁用'}组织成功`);
}
});
};
render() {
const { selectedRows, loading, tableData, pagination } = this.state;
const columns = [
{
title: '组织名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
},
{
title: '组织编码',
dataIndex: 'code',
key: 'code',
ellipsis: true
},
{
title: '组织类型',
dataIndex: 'type',
key: 'type',
render: text => formatDictText(organization_type, text)
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: text => (
<Tag color={text === '1' ? 'green' : 'red'}>
{text === '1' ? '启用' : '禁用'}
</Tag>
)
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true
},
{
title: '创建人',
dataIndex: 'createUser',
key: 'createUser',
ellipsis: true
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 160,
render: (_, record) => (
<Space size="middle">
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => this.handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => this.handleDelete(record)}
>
删除
</Button>
<Switch
checked={record.status === '1'}
onChange={() => this.handleStatusChange(record)}
/>
</Space>
)
}
];
return (
<div className={style.contentWrapper}>
<Card className={style.contentCard}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<h2 className={styles.title}>组织管理</h2>
</div>
<div className={styles.headerRight}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={this.handleAdd}
>
新增组织
</Button>
<Button
danger
style={{ marginLeft: 8 }}
onClick={this.handleBatchDelete}
disabled={selectedRows.length === 0}
>
批量删除
</Button>
</div>
</div>
<Divider />
<StandardTable
rowKey="id"
selectedRows={selectedRows}
loading={loading}
data={{
list: tableData,
pagination
}}
columns={columns}
onSelectRow={this.handleSelectChange}
onChange={this.handleTableChange}
/>
</Card>
</div>
);
}
}

@ -1,80 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.headerLeft {
display: flex;
align-items: center;
}
.headerRight {
display: flex;
align-items: center;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.filterSection {
margin-bottom: 16px;
}
.filterRow {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.filterItem {
margin-right: 16px;
}
.actionButtons {
display: flex;
align-items: center;
}
.batchAction {
margin-right: 12px;
}
.formContainer {
padding: 24px;
background-color: #f5f5f5;
}
.formHeader {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e8e8e8;
}
.formTitle {
margin: 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.formContent {
background-color: #fff;
padding: 24px;
border-radius: 2px;
}
.formFooter {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
.formButton {
margin-left: 12px;
}

@ -1,329 +0,0 @@
import React from 'react';
import {
DeleteOutlined,
EditOutlined,
PlusOutlined,
SearchOutlined,
RedoOutlined,
DownOutlined,
ExclamationCircleFilled,
UpOutlined,
InfoCircleFilled,
QuestionCircleFilled,
DownloadOutlined
} from '@ant-design/icons';
import {connect, history} from '@umijs/max';
import {Button, Card, Divider, Dropdown, message, Modal, Popconfirm, Space, Switch, Tag, Row, Col} from 'antd';
import StandardTable from '@/components/StandardTable';
import { MyIcon } from "@/components/Icon"
import style from "@/global.less";
import styles from './SystemRoleList.less';
import datadictionary from "@/utils/dataDictionary";
import {formatDate} from "@/utils/formatUtils";
import { formatDictText, checkButtonAuthority } from "@/utils/globalCommon";
const { confirm } = Modal;
// 角色类型
const role_type = datadictionary.role_type || [];
const role_status = datadictionary.role_status || [];
const mockData = {
list: [
{
id: '1',
name: '超级管理员',
code: 'SUPER_ADMIN',
type: '1',
status: '1',
createTime: '2023-01-01 10:00:00',
createUser: 'admin',
remark: '系统最高权限角色'
},
{
id: '2',
name: '部门管理员',
code: 'DEPT_ADMIN',
type: '2',
status: '1',
createTime: '2023-01-02 14:30:00',
createUser: 'admin',
remark: '部门级管理权限'
},
{
id: '3',
name: '普通用户',
code: 'NORMAL_USER',
type: '3',
status: '1',
createTime: '2023-01-03 09:15:00',
createUser: 'admin',
remark: '基础功能访问权限'
}
],
pagination: {
total: 3,
pageSize: 10,
current: 1
}
};
@connect(({ systemRole, loading }) => ({
systemRole,
loading: loading.models.systemRole
}))
export default class SystemRoleList extends React.Component {
state = {
selectedRows: [],
loading: false,
advancedFormVisible: false,
filterData: {},
pagination: {
pageSize: 10,
current: 1
},
tableData: mockData.list
};
componentDidMount() {
this.getRoleList();
}
// 获取角色列表
getRoleList = () => {
const { dispatch } = this.props;
const { pagination, filterData } = this.state;
this.setState({ loading: true });
dispatch({
type: 'systemRole/fetchList',
payload: {
pageNum: pagination.current,
pageSize: pagination.pageSize,
...filterData
},
callback: (res) => {
this.setState({
loading: false,
tableData: res?.list || [],
pagination: {
...pagination,
total: res?.total || 0
}
});
}
});
};
// 表格选择变化
handleSelectChange = (selectedRowKeys, selectedRows) => {
this.setState({ selectedRows });
};
// 分页变化
handleTableChange = (pagination) => {
this.setState({
pagination: {
...this.state.pagination,
current: pagination.current
}
}, () => {
this.getRoleList();
});
};
// 新增角色
handleAdd = () => {
// 新增逻辑
message.success('新增角色功能');
};
// 编辑角色
handleEdit = (record) => {
// 编辑逻辑
message.success('编辑角色功能');
};
// 角色授权
handleAuth = (record) => {
// 授权逻辑
message.success('角色授权功能');
};
// 删除角色
handleDelete = (record) => {
confirm({
title: '确定要删除这个角色吗?',
icon: <ExclamationCircleFilled />,
onOk: () => {
// 删除逻辑
message.success('删除角色成功');
}
});
};
// 批量删除
handleBatchDelete = () => {
const { selectedRows } = this.state;
if (selectedRows.length === 0) {
message.warning('请选择要删除的角色');
return;
}
confirm({
title: `确定要删除选中的${selectedRows.length}个角色吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 批量删除逻辑
message.success('批量删除角色成功');
}
});
};
// 启用/禁用角色
handleStatusChange = (record) => {
const newStatus = record.status === '1' ? '0' : '1';
confirm({
title: `确定要${newStatus === '1' ? '启用' : '禁用'}这个角色吗?`,
icon: <ExclamationCircleFilled />,
onOk: () => {
// 状态变更逻辑
message.success(`${newStatus === '1' ? '启用' : '禁用'}角色成功`);
}
});
};
render() {
const { selectedRows, loading, tableData, pagination } = this.state;
const columns = [
{
title: '角色名称',
dataIndex: 'name',
key: 'name',
ellipsis: true
},
{
title: '角色编码',
dataIndex: 'code',
key: 'code',
ellipsis: true
},
{
title: '角色类型',
dataIndex: 'type',
key: 'type',
render: text => formatDictText(role_type, text)
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: text => (
<Tag color={text === '1' ? 'green' : 'red'}>
{text === '1' ? '启用' : '禁用'}
</Tag>
)
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
ellipsis: true
},
{
title: '创建人',
dataIndex: 'createUser',
key: 'createUser',
ellipsis: true
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
ellipsis: true
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 200,
render: (_, record) => (
<Space size="middle">
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => this.handleEdit(record)}
>
编辑
</Button>
<Button
type="link"
size="small"
onClick={() => this.handleAuth(record)}
>
授权
</Button>
<Button
type="link"
size="small"
danger
icon={<DeleteOutlined />}
onClick={() => this.handleDelete(record)}
>
删除
</Button>
<Switch
checked={record.status === '1'}
onChange={() => this.handleStatusChange(record)}
/>
</Space>
)
}
];
return (
<div className={style.contentWrapper}>
<Card className={style.contentCard}>
<div className={styles.header}>
<div className={styles.headerLeft}>
<h2 className={styles.title}>角色配置</h2>
</div>
<div className={styles.headerRight}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={this.handleAdd}
>
新增角色
</Button>
<Button
danger
style={{ marginLeft: 8 }}
onClick={this.handleBatchDelete}
disabled={selectedRows.length === 0}
>
批量删除
</Button>
</div>
</div>
<Divider />
<StandardTable
rowKey="id"
selectedRows={selectedRows}
loading={loading}
data={{
list: tableData,
pagination
}}
columns={columns}
onSelectRow={this.handleSelectChange}
onChange={this.handleTableChange}
/>
</Card>
</div>
);
}
}

@ -1,80 +0,0 @@
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.headerLeft {
display: flex;
align-items: center;
}
.headerRight {
display: flex;
align-items: center;
}
.title {
margin: 0;
font-size: 18px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.filterSection {
margin-bottom: 16px;
}
.filterRow {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.filterItem {
margin-right: 16px;
}
.actionButtons {
display: flex;
align-items: center;
}
.batchAction {
margin-right: 12px;
}
.formContainer {
padding: 24px;
background-color: #f5f5f5;
}
.formHeader {
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e8e8e8;
}
.formTitle {
margin: 0;
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
.formContent {
background-color: #fff;
padding: 24px;
border-radius: 2px;
}
.formFooter {
display: flex;
justify-content: flex-end;
margin-top: 24px;
}
.formButton {
margin-left: 12px;
}
Loading…
Cancel
Save