diff --git a/README.md b/README.md index af2f4dc..203d7d2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ # dq-enms + 能源管理系统 \ No newline at end of file diff --git a/config/routes.js b/config/routes.js index b6355f1..8cf784a 100644 --- a/config/routes.js +++ b/config/routes.js @@ -24,19 +24,19 @@ export default [ name: 'business', component: './nav_system_content/SystemContentList', routes: [ - // 基础信息管理 + // 基础数据管理 { - path: '/topnavbar00/business/basic', - name: 'basic', - component: './business_basic/basic', + path: '/topnavbar00/business/data', + name: 'basic_data', + component: './business_data/basic', }, - // 消防重点部位管理 + // 数据采集 { - path: '/topnavbar00/business/firekeynotearea', - name: 'firekeynotearea', - component: './business_firekeynotearea/FireKeynoteArea', + path: '/topnavbar00/business/dataCollection', + name: 'dataCollection', + component: './business_dataCollection/basic', }, - // 消防检测报警 + // 能源监测 { path: '/topnavbar00/business/fireWarning', name: 'fireWarning', diff --git a/src/assets/basic_data/Component1.svg b/src/assets/basic_data/Component1.svg new file mode 100644 index 0000000..285a3e9 --- /dev/null +++ b/src/assets/basic_data/Component1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/basic_data/Component2.svg b/src/assets/basic_data/Component2.svg new file mode 100644 index 0000000..a26205e --- /dev/null +++ b/src/assets/basic_data/Component2.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/basic_data/Component3.svg b/src/assets/basic_data/Component3.svg new file mode 100644 index 0000000..da432d0 --- /dev/null +++ b/src/assets/basic_data/Component3.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/basic_data/Iconfont.svg b/src/assets/basic_data/Iconfont.svg new file mode 100644 index 0000000..3d42fa7 --- /dev/null +++ b/src/assets/basic_data/Iconfont.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/data_icon.svg b/src/assets/data_icon.svg new file mode 100644 index 0000000..a06d26e --- /dev/null +++ b/src/assets/data_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/menuBack.svg b/src/assets/menuBack.svg new file mode 100644 index 0000000..3545556 --- /dev/null +++ b/src/assets/menuBack.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/business_data/basic.js b/src/pages/business_data/basic.js new file mode 100644 index 0000000..20abc46 --- /dev/null +++ b/src/pages/business_data/basic.js @@ -0,0 +1,74 @@ +import React, { useState } from 'react'; +import { Card, Row, Col, Statistic, Progress, Button, Space } from 'antd'; +import styles from './basic.less'; +import Nyfl from './components/nyfl'; +import Fgpsdsz from './components/fgpsdsz'; +import Jgfx from './components/jgfx'; +import Bmzstx from './components/bmzstx'; +import Nhtjcs from './components/nhtjcs'; + + + +const SafeMajorHazardList = () => { + const [activeModule, setActiveModule] = useState('nyfl'); + + const handleModuleClick = (module) => { + setActiveModule(module) + } + + + const renderModule = () => { + switch (activeModule) { + case 'nyfl': + return ; + case 'fgpsdsz': + return ; + case 'jgtx': + return ; + case 'bmzstx': + return ; + case 'nhtjcs': + return ; + default: + return ; + } + }; + + + return ( +
+
+ + + + + +
+
+ {renderModule()} +
+
+ ); +}; + +export default SafeMajorHazardList; diff --git a/src/pages/business_data/basic.less b/src/pages/business_data/basic.less new file mode 100644 index 0000000..a06febe --- /dev/null +++ b/src/pages/business_data/basic.less @@ -0,0 +1,69 @@ +.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 { + background-color: rgba(72, 81, 255, 1) !important; + color: #fff !important; + border-radius: 20px !important; + padding: 6px 10px 10px !important; + + &::after { + content: ''; + position: absolute; + left: 50%; + transform: translateX(-50%); + bottom: 3px; + width: 16.573974609375px; + height: 2.615234375px; + background-color: rgba(255, 255, 255, 0.52); + border-radius: 15px; + opacity: 1; + } + } + } + } + + .content { + // ======== 内容区域样式 ======== + flex: 1; // ======== 占据剩余空间 ======== + overflow-y: auto; // ======== 允许垂直滚动 ======== + padding: 0; // ======== 无内边距 ======== + } +} diff --git a/src/pages/business_data/components/OnlineMonitoring.js b/src/pages/business_data/components/OnlineMonitoring.js new file mode 100644 index 0000000..3e4b6b5 --- /dev/null +++ b/src/pages/business_data/components/OnlineMonitoring.js @@ -0,0 +1,716 @@ + +import React, { useEffect, useRef, useState } from 'react'; +import { Card, Result, Select, Button } from 'antd'; +import * as echarts from 'echarts'; +import StandardTable from '@/components/StandardTable'; +import styles from './OnlineMonitoring.less'; + +import alarm0 from '@/assets/safe_majorHazard/online_monitoring/alarm0.png'; +import alarm1 from '@/assets/safe_majorHazard/online_monitoring/alarm1.png'; +import alarm2 from '@/assets/safe_majorHazard/online_monitoring/alarm2.png'; +import alarm3 from '@/assets/safe_majorHazard/online_monitoring/alarm3.png'; +import exportIcon from '@/assets/safe_majorHazard/online_monitoring/export.png'; +import deleteIcon from '@/assets/safe_majorHazard/online_monitoring/delete.png'; + +const OnlineMonitoring = () => { + const chartRef = useRef(null); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const [loading, setLoading] = useState(false); + const [dataSource, setDataSource] = useState([]); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 5, + total: 0, + }); + + useEffect(() => { + if (chartRef.current) { + const chart = echarts.init(chartRef.current); + + const option = { + color: ['#04A7F3', '#E7C42C', '#EC6941'], + + legend: { + data: ['液位', '温度', '压力'], + top: "-3px", + left: "center", + itemGap: 40, // 图例间距 + textStyle: { + fontSize: 10 + } + }, + grid: { + left: '2%', + right: '4%', + bottom: '2%', + top: '12%', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: ['0:00', '2:00', '4:00', '6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00', '24:00'], + axisLabel: { + fontSize: 10 + } + }, + yAxis: { + type: 'value', + min: 0, + max: 500, + axisLabel: { + formatter: '{value}', + fontSize: 10 + } + }, + series: [ + { + name: '液位', + type: 'line', + smooth: true, + lineStyle: { + width: 1.5, + color: '#04A7F3' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { offset: 0, color: 'rgba(4, 167, 243, 0.3)' }, + { offset: 1, color: 'rgba(4, 167, 243, 0)' } + ] + } + }, + symbol: 'none', // 不显示数据点 + data: [120, 200, 150, 300, 250, 400, 350, 280, 320, 180, 220, 160, 140] + }, + { + name: '温度', + type: 'line', + smooth: true, + lineStyle: { + width: 1.5, + color: '#E7C42C' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { offset: 0, color: 'rgba(231, 196, 44, 0.3)' }, + { offset: 1, color: 'rgba(231, 196, 44, 0)' } + ] + } + }, + symbol: 'none', + data: [80, 120, 100, 180, 160, 220, 200, 150, 170, 90, 110, 85, 75] + }, + { + name: '压力', + type: 'line', + smooth: true, + lineStyle: { + width: 1.5, + color: '#EC6941' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 1, + x2: 0, + y2: 0, + colorStops: [ + { offset: 0, color: 'rgba(236, 105, 65, 0)' }, + { offset: 1, color: 'rgba(236, 105, 65, 0.3)' } + ] + } + }, + symbol: 'none', + data: [200, 300, 250, 450, 400, 430, 480, 420, 480, 280, 320, 260, 240] + } + ] + }; + + chart.setOption(option); + + // 响应式调整 - 使用ResizeObserver监听容器尺寸变化 + let resizeTimer = null; + const handleResize = () => { + // 防抖处理,避免频繁调用resize + if (resizeTimer) { + clearTimeout(resizeTimer); + } + resizeTimer = setTimeout(() => { + chart.resize(); + }, 100); + }; + + // 监听窗口大小变化 + window.addEventListener('resize', handleResize); + + // 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题) + let resizeObserver = null; + if (window.ResizeObserver) { + resizeObserver = new ResizeObserver(() => { + // 使用setTimeout确保DOM更新完成后再调整图表 + setTimeout(() => { + handleResize(); + }, 0); + }); + resizeObserver.observe(chartRef.current); + } + + return () => { + window.removeEventListener('resize', handleResize); + if (resizeObserver) { + resizeObserver.disconnect(); + } + if (resizeTimer) { + clearTimeout(resizeTimer); + } + chart.dispose(); + }; + } + }, []); + + // 表格列定义 + const columns = [ + { + title: '编号', + dataIndex: 'id', + key: 'id', + width: 80, + render: (text, record, index) => { + const page = pagination.current || 1; + const pageSize = pagination.pageSize || 5; + const number = (page - 1) * pageSize + index + 1; + return `0${number}`.slice(-2); + } + }, + { + title: '报警时间', + dataIndex: 'alarmTime', + key: 'alarmTime', + width: 150, + }, + { + title: '报警传感器名称', + dataIndex: 'sensorName', + key: 'sensorName', + width: 150, + }, + { + title: '报警类型', + dataIndex: 'alarmType', + key: 'alarmType', + width: 120, + }, + { + title: '报警内容', + dataIndex: 'alarmContent', + key: 'alarmContent', + width: 200, + }, + { + title: '优先级', + dataIndex: 'priority', + key: 'priority', + width: 80, + render: (text) => { + const colorMap = { + '高': '#FF4D4F', + '中': '#FAAD14', + '低': '#52C41A' + }; + return {text}; + } + }, + { + title: '处理状态', + dataIndex: 'status', + key: 'status', + width: 100, + render: (text) => { + const statusMap = { + '未处理': { color: '#FF4D4F', bg: '#FFF2F0' }, + '处理中': { color: '#FAAD14', bg: '#FFFBE6' }, + '已处理': { color: '#52C41A', bg: '#F6FFED' } + }; + const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' }; + return ( + + {text} + + ); + } + }, + { + title: '处理时间', + dataIndex: 'processTime', + key: 'processTime', + width: 150, + }, + { + title: '处理人', + dataIndex: 'processor', + key: 'processor', + width: 100, + }, + { + title: '操作', + key: 'action', + width: 120, + render: (_, record) => ( +
+ +
+ ), + }, + ]; + + // 模拟数据 + const mockData = [ + { + key: '1', + id: '001', + alarmTime: '2024-01-15 08:30:25', + sensorName: 'LNG储罐', + alarmType: '温度超限', + alarmContent: '储罐温度超过安全阈值', + priority: '高', + status: '未处理', + processTime: '-', + processor: '-', + }, + { + key: '2', + id: '002', + alarmTime: '2024-01-15 09:15:10', + sensorName: 'LNG储罐', + alarmType: '压力异常', + alarmContent: '管道压力异常波动', + priority: '中', + status: '处理中', + processTime: '2024-01-15 09:20:00', + processor: '张三', + }, + { + key: '3', + id: '003', + alarmTime: '2024-01-15 10:45:30', + sensorName: 'LNG储罐', + alarmType: '液位异常', + alarmContent: '储罐液位低于警戒线', + priority: '高', + status: '已处理', + processTime: '2024-01-15 11:00:15', + processor: '李四', + }, + { + key: '4', + id: '004', + alarmTime: '2024-01-15 11:20:45', + sensorName: 'LNG储罐', + alarmType: '气体泄漏', + alarmContent: '检测到可燃气体泄漏', + priority: '高', + status: '未处理', + processTime: '-', + processor: '-', + }, + { + key: '5', + id: '005', + alarmTime: '2024-01-15 12:10:20', + sensorName: 'LNG储罐', + alarmType: '设备振动', + alarmContent: '设备异常振动', + priority: '低', + status: '已处理', + processTime: '2024-01-15 12:30:00', + processor: '王五', + }, + { + key: '6', + id: '006', + alarmTime: '2024-01-15 13:25:15', + sensorName: 'LNG管道', + alarmType: '流量异常', + alarmContent: '管道流量异常波动', + priority: '中', + status: '未处理', + processTime: '-', + processor: '-', + }, + { + key: '7', + id: '007', + alarmTime: '2024-01-15 14:10:30', + sensorName: 'LNG储罐', + alarmType: '温度异常', + alarmContent: '储罐温度异常升高', + priority: '高', + status: '处理中', + processTime: '2024-01-15 14:15:00', + processor: '赵六', + }, + { + key: '8', + id: '008', + alarmTime: '2024-01-15 15:45:20', + sensorName: 'LNG管道', + alarmType: '压力超限', + alarmContent: '管道压力超过安全阈值', + priority: '高', + status: '已处理', + processTime: '2024-01-15 16:00:00', + processor: '孙七', + }, + { + key: '9', + id: '009', + alarmTime: '2024-01-15 16:30:45', + sensorName: 'LNG储罐', + alarmType: '液位超限', + alarmContent: '储罐液位超过警戒线', + priority: '中', + status: '未处理', + processTime: '-', + processor: '-', + }, + { + key: '10', + id: '010', + alarmTime: '2024-01-15 17:15:10', + sensorName: 'LNG管道', + alarmType: '泄漏检测', + alarmContent: '检测到轻微气体泄漏', + priority: '低', + status: '已处理', + processTime: '2024-01-15 17:30:00', + processor: '周八', + }, + { + key: '11', + id: '011', + alarmTime: '2024-01-15 18:20:35', + sensorName: 'LNG储罐', + alarmType: '设备故障', + alarmContent: '储罐阀门异常关闭', + priority: '高', + status: '处理中', + processTime: '2024-01-15 18:25:00', + processor: '吴九', + }, + { + key: '12', + id: '012', + alarmTime: '2024-01-15 19:05:50', + sensorName: 'LNG管道', + alarmType: '温度异常', + alarmContent: '管道温度异常下降', + priority: '中', + status: '未处理', + processTime: '-', + processor: '-', + }, + ]; + + // 初始化数据 + useEffect(() => { + setPagination(prev => ({ ...prev, total: mockData.length })); + }, []); + + // 根据分页获取当前页数据 + const getCurrentPageData = () => { + const { current, pageSize } = pagination; + const startIndex = (current - 1) * pageSize; + const endIndex = startIndex + pageSize; + return mockData.slice(startIndex, endIndex); + }; + + // 表格选择变化 + const onSelectChange = (newSelectedRowKeys, newSelectedRows) => { + setSelectedRowKeys(newSelectedRowKeys); + setSelectedRows(newSelectedRows); + }; + + // 分页变化处理 + const handleTableChange = (pagination) => { + setPagination(prev => ({ + ...prev, + current: pagination.current, + pageSize: pagination.pageSize, + })); + }; + + // 导出功能 + const handleExport = () => { + console.log('导出数据'); + // 这里可以添加导出逻辑 + }; + + // 批量删除功能 + const handleBatchDelete = () => { + if (selectedRowKeys.length === 0) { + console.log('没有选中任何行'); + // 可以在这里添加提示用户选择行的逻辑 + return; + } + console.log('批量删除', selectedRowKeys); + // 这里可以添加批量删除逻辑 + }; + + return ( +
+
+
+
+
+
+ alarm0 +
+ +
+
总报警
+
1456
+
+
+ 未处理 6 +
+
+ 处理中 10 +
+
+
+
+
+
+ alarm1 +
+
+
一级报警
+
357
+
+
+ 未处理 6 +
+
+ 处理中 10 +
+
+
+
+
+
+ alarm2 +
+
+
二级报警
+
401
+
+
+ 未处理 6 +
+
+ 处理中 10 +
+
+
+
+
+
+ alarm3 +
+
+
三级报警
+
556
+
+
+ 未处理 6 +
+
+ 处理中 10 +
+
+
+
+
+
+
+
+
+
预警看板
+
+
+
检测对象
+ +
+
+
+ + {/* 第二行:三个小块 */} +
+
+
+
+
+ {/* 第一个块:姓名和单位 */} +
+
张明
+
东义区消防队
+
+ + {/* 第二个块:电话 */} +
+ + 132****3847 +
+ + {/* 第三个块:身份证 */} +
+ + 1304************10 +
+ + {/* 第四个块:职位标签 */} +
+
+
队长
+
消防工程师
+
+
+ + {/* 第五个块:证书状态 */} +
+
+
消防工程师
+
证书7天后到期
+
+
+ + {/* 第六个块:操作按钮 */} +
+
+ + +
+
+
+
+
+
+
+
+ {/* 第一个块:姓名和单位 */} +
+ 李小明 + 消防支队 +
+ + {/* 第二个块:电话 */} +
+ + 138****5678 +
+ + {/* 第三个块:身份证 */} +
+ + 1304************20 +
+ + {/* 第四个块:职位标签 */} +
+
+
副队长
+
安全员
+
+
+ + {/* 第五个块:证书状态 */} +
+
+
安全员证
+
证书正常
+
+
+ + {/* 第六个块:操作按钮 */} +
+
+ + +
+
+
+
+
+
+
+
+ {/* 第一个块:姓名和单位 */} +
+ 王小红 + 消防中队 +
+ + {/* 第二个块:电话 */} +
+ + 139****9012 +
+ + {/* 第三个块:身份证 */} +
+ + 1304************30 +
+ + {/* 第四个块:职位标签 */} +
+
+
队员
+
技术员
+
+
+ + {/* 第五个块:证书状态 */} +
+
+
技术员证
+
证书3天后到期
+
+
+ + {/* 第六个块:操作按钮 */} +
+
+ + +
+
+
+
+
+
+
+
+
+ +
+ {/* 第一块:标题 */} +
+
+
+
组织架构管理
+
+
+ + {/* 第二个大块:搜索和按钮 */} +
+
+ +
+
+ + + +
+
+ + {/* 第三个大块:表格 */} +
+ {}} // ======== 行选择事件处理函数 ======== + onChange={() => {}} // ======== 表格变化事件处理函数 ======== + pagination={{ + currentPage: 1, + pageSize: 5, + total: tableData.length, + showSizeChanger: false, + showQuickJumper: true, + showTotal: (total, range) => + `共 ${total} 条`, + locale: { + jump_to: '前往', + page: '页', + items_per_page: '条/页', + } + }} + /> +
+
+
+ ); +}; +export default ResponsibilityImplementation; diff --git a/src/pages/business_data/components/ResponsibilityImplementation.less b/src/pages/business_data/components/ResponsibilityImplementation.less new file mode 100644 index 0000000..d94eca3 --- /dev/null +++ b/src/pages/business_data/components/ResponsibilityImplementation.less @@ -0,0 +1,1000 @@ +.XcontainerR { + padding: 8px 6px; + height: 100%; + display: flex; + flex-direction: column; + + .warningBox { + width: 100%; + background-color: #FFF3CD; + border: 1px solid #F4E3AE; + border-radius: 4px; + padding: 8px 20px; + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 10px; + + .warningIcon { + width: 18px; + height: 18px; + } + + .warningText { + color: #8C6C0B; + font-size: 12px; + line-height: 1.4; + } + } + + .containerOne { + height: 40%; + flex-shrink: 0; + display: flex; + margin-bottom: 10px; + gap: 10px; + + .containerOneLeft { + background-color: white; + width: calc(50% - 5px); + display: flex; + flex-direction: column; + padding: 5px 15px; + border: 1px solid #f0f0f0; + border-radius: 4px; + + .leftTopSection { + display: flex; + justify-content: space-between; + align-items: center; + // margin-bottom: 15px; + + .titleLeft { + 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; + } + } + + .buttonGroup { + display: flex; + gap: 8px; + + .actionBtn { + 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; + + &:hover { + background-color: #F0F2FF; + border-color: #2E4CD4; + } + + .btnIcon { + width: 12px; + height: 12px; + } + } + } + } + + .leftBottomSection { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + + .imagePlaceholder { + display: flex; + flex-direction: column; + align-items: center; + + .imageIcon1 { + transform: scale(0.9) translateY(-5px); // 稍微向上移动 + object-fit: contain; + } + + + .imageRow { + display: flex; + justify-content: space-between; + // width: 100%; + margin-bottom: 10px; + // padding-bottom: 20px; + // gap: 12px; + + .imageIcon2 { + height: 55%; + transform: scale(0.7) translateY(-25%) translateX(20%); // 稍微向上移动 + object-fit: contain; + background-color: #EFF5FE; + // padding-bottom: 20px; + } + + .imageIcon3 { + height: 40%; + transform: scale(0.65) translateY(-32%) translateX(4%); // 向上移动10px + object-fit: contain; + padding-bottom: 20px; + // background-color: #EFF5FE; + + } + } + + .imageText { + font-size: 12px; + font-weight: 400; + } + } + } + } + + .containerOneRight { + background-color: white; + width: calc(50% - 5px); + display: flex; + flex-direction: column; + padding: 5px 15px; + border: 1px solid #f0f0f0; + border-radius: 4px; + + .rightTopSection { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + + .rightTopLeft { + display: flex; + align-items: center; + + .titleLeft { + 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; + } + } + } + + .rightTopRight { + .searchGroup { + display: flex; + gap: 8px; + align-items: center; + + .searchInput { + width: 200px; + height: 32px; + + :global(.ant-input) { + height: 32px; + border-radius: 4px; + border: 1px solid #d9d9d9; + font-size: 14px; + + &:focus { + border-color: #2E4CD4; + box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2); + } + } + + :global(.ant-input-suffix) { + color: #999999; + font-size: 14px; + } + } + + .organizationSelect { + width: 120px; + height: 32px; + + :global(.ant-select-selector) { + height: 32px !important; + border-radius: 4px !important; + border: 1px solid #d9d9d9 !important; + + &:hover { + border-color: #2E4CD4 !important; + } + + &:focus { + border-color: #2E4CD4 !important; + box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2) !important; + } + } + + :global(.ant-select-selection-item) { + line-height: 30px !important; + font-size: 14px !important; + } + } + } + } + } + + .rightBottomSection { + flex: 1; + padding: 5px 15px; + width: 100%; + height: 100%; + + .threeBlocksContainer { + display: flex; + gap: 20px; + width: 100%; + height: 100%; + + .blockItem { + width: 100%; + height: 100%; + flex: 1; + display: flex; + justify-content: center; + background: url('@/assets/business_basic/background1.png') no-repeat center center; + background-size: 100% auto; + + .blockContent { + // background-color: pink; + font-size: 12px; + color: #666666; + font-weight: 400; + width: 100%; + height: 100%; + } + + // 新的6个横向块样式 + .backgroundContainer { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 8px; + + .infoBlock { + width: 100%; + display: flex; + justify-content: flex-start; + white-space: nowrap; + + .nameText { + font-size: 12px; + font-weight: 500; + color: #333333; + margin-left: 10px; + margin-right: 10px; + margin-top: 15px; + } + + + .unitText { + font-size: 10px; + font-weight: 400; + color: #666666; + margin-top: 18px; + } + + .infoIcon { + font-size: 10px; + color: #666666; + margin-left: 10px; + margin-right: 10px; + } + + .infoText { + font-size: 10px; + font-weight: 400; + color: #666666; + + } + + } + + .tagContainer { + display: flex; + gap: 8px; + align-items: center; + } + + .tagBlue1 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + margin-left: 10px; + } + + .tagBlue2 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + + } + + .tagBlue3 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + margin-left: 10px; + + } + + .tagYellow { + background-color: #FFF8E2; + color: #FFC403; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + } + + .actionBlock { + width: 100%; + height: 50%; + background-color: #BDD6FDCC; + display: flex; + align-items: center; + justify-content: center; + } + + .buttonContainer { + display: flex; + gap: 15px; + align-items: center; + justify-content: center; + width: 100%; + + .editBtn { + height: 80%; + background-color: #1269FF; + color: #fff; + font-size: 10px; + font-weight: 400; + border: none; + border-radius: 2px; + cursor: pointer; + padding: 2px 15px; + + &:hover { + background-color: #0f5ae0; + } + } + + .deleteBtn { + height: 80%; + background-color: #FF5F60; + color: #fff; + font-size: 10px; + font-weight: 400; + border: none; + border-radius: 2px; + cursor: pointer; + padding: 2px 15px; + + &:hover { + background-color: #ff4a4b; + } + } + } + + + } + + // 第二个块的样式 + .backgroundContainer2 { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 8px; + + .infoBlock2 { + width: 100%; + display: flex; + justify-content: flex-start; + white-space: nowrap; + + .nameText2 { + font-size: 12px; + font-weight: 500; + color: #333333; + margin-left: 10px; + margin-right: 10px; + margin-top: 15px; + } + + .unitText2 { + font-size: 10px; + font-weight: 400; + color: #666666; + margin-top: 18px; + } + + .infoIcon2 { + font-size: 10px; + color: #666666; + margin-left: 10px; + margin-right: 10px; + } + + .infoText2 { + font-size: 10px; + font-weight: 400; + color: #666666; + } + } + + .tagContainer2 { + display: flex; + gap: 8px; + align-items: center; + } + + .tagBlue4 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + margin-left: 10px; + } + + .tagBlue5 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + } + + .tagBlue6 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + margin-left: 10px; + } + + .tagGreen { + background-color: #E8F5E8; + color: #52C41A; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + } + + .actionBlock2 { + width: 100%; + height: 50%; + background-color: #BDD6FDCC; + display: flex; + align-items: center; + justify-content: center; + } + + .buttonContainer2 { + display: flex; + gap: 15px; + align-items: center; + justify-content: center; + width: 100%; + + .editBtn2 { + height: 80%; + background-color: #1269FF; + color: #fff; + font-size: 10px; + font-weight: 400; + border: none; + border-radius: 2px; + cursor: pointer; + padding: 2px 15px; + + &:hover { + background-color: #0f5ae0; + } + } + + .deleteBtn2 { + height: 80%; + background-color: #FF5F60; + color: #fff; + font-size: 10px; + font-weight: 400; + border: none; + border-radius: 2px; + cursor: pointer; + padding: 2px 15px; + + &:hover { + background-color: #ff4a4b; + } + } + } + } + + // 第三个块的样式 + .backgroundContainer3 { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 8px; + + .infoBlock3 { + width: 100%; + display: flex; + justify-content: flex-start; + white-space: nowrap; + + .nameText3 { + font-size: 12px; + font-weight: 500; + color: #333333; + margin-left: 10px; + margin-right: 10px; + margin-top: 15px; + } + + .unitText3 { + font-size: 10px; + font-weight: 400; + color: #666666; + margin-top: 18px; + } + + .infoIcon3 { + font-size: 10px; + color: #666666; + margin-left: 10px; + margin-right: 10px; + } + + .infoText3 { + font-size: 10px; + font-weight: 400; + color: #666666; + } + } + + .tagContainer3 { + display: flex; + gap: 8px; + align-items: center; + } + + .tagBlue7 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + margin-left: 10px; + } + + .tagBlue8 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + } + + .tagBlue9 { + background-color: #D5E5FF; + color: #1269FF; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + margin-left: 10px; + } + + .tagOrange { + background-color: #FFF2E8; + color: #FF7A00; + font-size: 10px; + font-weight: 400; + padding: 4px 8px; + border-radius: 4px; + white-space: nowrap; + } + + .actionBlock3 { + width: 100%; + height: 50%; + background-color: #BDD6FDCC; + display: flex; + align-items: center; + justify-content: center; + } + + .buttonContainer3 { + display: flex; + gap: 15px; + align-items: center; + justify-content: center; + width: 100%; + + .editBtn3 { + height: 80%; + background-color: #1269FF; + color: #fff; + font-size: 10px; + font-weight: 400; + border: none; + border-radius: 2px; + cursor: pointer; + padding: 2px 15px; + + &:hover { + background-color: #0f5ae0; + } + } + + .deleteBtn3 { + height: 80%; + background-color: #FF5F60; + color: #fff; + font-size: 10px; + font-weight: 400; + border: none; + border-radius: 2px; + cursor: pointer; + padding: 2px 15px; + + &:hover { + background-color: #ff4a4b; + } + } + } + } + } + } + } + } + } + + .containerTwo { + flex: 1; + background-color: white; + display: flex; + flex-direction: column; + padding: 5px 15px; + border: 1px solid #f0f0f0; + border-radius: 4px; + + .containerTwoTitle { + margin-top: 5px; + margin-bottom: 15px; + + .titleLeft { + 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; + } + } + } + + .containerTwoActions { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + padding: 0px 20px; + + .searchSection { + flex: 1; + max-width: 300px; + + :global(.ant-input) { + height: 32px; + border-radius: 4px 0px 0px 4px; + border: 1px solid #d9d9d9; + + &:focus { + border-color: #2E4CD4; + box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2); + } + } + } + + .buttonSection { + display: flex; + gap: 8px; + + :global(.ant-btn) { + height: 28px; + padding: 0 16px; + border-radius: 4px; + font-size: 14px; + border: 1px solid #d9d9d9; + background-color: #fff; + color: #333; + + &:hover { + border-color: #2E4CD4; + color: #2E4CD4; + } + + &:focus { + border-color: #2E4CD4; + color: #2E4CD4; + } + } + + .addBtn { + 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; + + &:hover { + background-color: #F0F2FF; + border-color: #2E4CD4; + } + + .addIcon { + width: 12px; + height: 12px; + font-size: 12px; + } + } + + .importBtn { + 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; + + &:hover { + background-color: #F0F2FF; + border-color: #2E4CD4; + } + + .importIcon { + width: 12px; + height: 12px; + } + } + + .exportBtn { + 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; + + &:hover { + background-color: #F0F2FF; + border-color: #2E4CD4; + } + + .exportIcon { + width: 12px; + height: 12px; + } + } + } + } + + .containerTwoTable { + flex: 1; + overflow: hidden; + padding: 0px 20px; + + :global(.ant-table) { + font-size: 12px; + } + :global(.ant-pagination-options-quick-jumper input) { + text-align: center !important; + } + + :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; + // color: pink; + } + + :global(.ant-table-tbody > tr:hover > td) { + // background-color: #f5f5f5; + } + + :global(.ant-pagination) { + margin-top: 16px; + text-align: right; + } + } + } +} + + +.rightTopSelect { + + // 下拉框本身的样式 + :global(.ant-select-selector) { + background-color: #f8f9fa !important; + border: 1px solid #d9d9d9 !important; + border-radius: 6px !important; + height: 32px !important; + min-height: 32px !important; + + &:hover { + border-color: #2E4CD4 !important; + } + + &:focus { + border-color: #2E4CD4 !important; + box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2) !important; + } + } + + // 下拉框内的文字样式 + :global(.ant-select-selection-item) { + color: #333333 !important; + font-size: 14px !important; + font-weight: 500 !important; + line-height: 30px !important; + } + + // 下拉箭头样式 + :global(.ant-select-arrow) { + color: #666666 !important; + font-size: 12px !important; + } + + // 下拉菜单容器样式 + :global(.ant-select-dropdown) { + background-color: #ffffff !important; + border: 1px solid #e8e8e8 !important; + border-radius: 8px !important; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; + padding: 4px 0 !important; + } + + // 下拉选项样式 + :global(.ant-select-item) { + color: #333333 !important; + font-size: 14px !important; + padding: 8px 12px !important; + border-radius: 4px !important; + margin: 2px 8px !important; + + &:hover { + background-color: #f0f2ff !important; + color: #2E4CD4 !important; + } + + &.ant-select-item-option-selected { + background-color: #e6f7ff !important; + color: #2E4CD4 !important; + font-weight: 600 !important; + } + } + + // 选中状态的样式 + :global(.ant-select-focused .ant-select-selector) { + border-color: #2E4CD4 !important; + box-shadow: 0 0 0 2px rgba(46, 76, 212, 0.2) !important; + } +} + +// 自定义选项样式 +.customOption { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 0; + + .optionIcon { + font-size: 16px; + color: #2E4CD4; + } + + .optionText { + font-size: 14px; + color: #333333; + font-weight: 500; + } +} + diff --git a/src/pages/business_data/components/RiskAssessment.js b/src/pages/business_data/components/RiskAssessment.js new file mode 100644 index 0000000..46d5b50 --- /dev/null +++ b/src/pages/business_data/components/RiskAssessment.js @@ -0,0 +1,865 @@ + +import React, { useEffect, useRef, useState } from 'react'; +import { Card, Result, Select, Button, Segmented } from 'antd'; +import { CheckCircleOutlined, ExportOutlined } from '@ant-design/icons'; +import * as echarts from 'echarts'; +import StandardTable from '@/components/StandardTable'; +import styles from './RiskAssessment.less'; +// import './RiskAssessment.less'; + +import img1 from '@/assets/safe_majorHazard/online_monitoring/img1.png'; +import img2 from '@/assets/safe_majorHazard/online_monitoring/img2.png'; +import img3 from '@/assets/safe_majorHazard/online_monitoring/img3.png'; +import map1 from '@/assets/safe_majorHazard/online_monitoring/map.png'; +import risk1 from '@/assets/safe_majorHazard/online_monitoring/risk1.png'; +import risk2 from '@/assets/safe_majorHazard/online_monitoring/risk2.png'; +import risk3 from '@/assets/safe_majorHazard/online_monitoring/risk3.png'; +import eqicon1 from '@/assets/business_basic/eqicon1.png'; +import eqicon2 from '@/assets/business_basic/eqicon2.png'; +import eqicon3 from '@/assets/business_basic/eqicon3.png'; +import eqicon4 from '@/assets/business_basic/eqicon4.png'; + +const RiskAssessment = () => { + const chartRef = useRef(null); + const pieChartRef = useRef(null); + const faultPieChartRef = useRef(null); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [selectedRows, setSelectedRows] = useState([]); + const [loading, setLoading] = useState(false); + const [dataSource, setDataSource] = useState([]); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 5, + total: 0, + }); + + // 饼图初始化 + useEffect(() => { + if (pieChartRef.current) { + const pieChart = echarts.init(pieChartRef.current); + + const pieOption = { + color: ['#44BB5F', '#F8C541', '#A493FB', '#4B69F1', '#949FD0'], + legend: { + orient: 'vertical', + right: '10%', + top: 'center', + itemWidth: 8, + itemHeight: 8, + textStyle: { + fontSize: 12, + color: '#333' + } + }, + series: [{ + name: '设备状态', + type: 'pie', + radius: ['40%', '70%'], + center: ['35%', '50%'], + avoidLabelOverlap: false, + label: { + show: false, + position: 'center' + }, + emphasis: { + label: { + show: true, + fontSize: '14', + fontWeight: 'bold' + } + }, + labelLine: { + show: false + }, + data: [ + { value: 480, name: '正常' }, + { value: 289, name: '故障' }, + { value: 200, name: '维修中' }, + { value: 150, name: '待验收' }, + { value: 161, name: '停用' } + ] + }] + }; + + pieChart.setOption(pieOption); + + // 响应式调整 + const handlePieResize = () => { + if (pieChart && !pieChart.isDisposed()) { + pieChart.resize(); + } + }; + + window.addEventListener('resize', handlePieResize); + + return () => { + window.removeEventListener('resize', handlePieResize); + if (pieChart && !pieChart.isDisposed()) { + pieChart.dispose(); + } + }; + } + }, []); + + // 故障类型饼图初始化 + useEffect(() => { + if (faultPieChartRef.current) { + const faultPieChart = echarts.init(faultPieChartRef.current); + + const faultPieOption = { + color: ['#FF3E48', '#FF8800', '#FFC403'], + legend: { + orient: 'vertical', + right: '10%', + top: 'center', + itemWidth: 8, + itemHeight: 8, + textStyle: { + fontSize: 12, + color: '#333' + } + }, + series: [{ + name: '设备故障类型', + type: 'pie', + radius: '70%', + center: ['35%', '50%'], + avoidLabelOverlap: false, + label: { + show: true, + position: 'outside', + formatter: '{b}: {c}', + fontSize: 12 + }, + emphasis: { + label: { + show: true, + fontSize: '14', + fontWeight: 'bold' + } + }, + labelLine: { + show: true + }, + data: [ + { value: 120, name: '紧急' }, + { value: 80, name: '重要' }, + { value: 60, name: '一般' } + ] + }] + }; + + faultPieChart.setOption(faultPieOption); + + // 响应式调整 + const handleFaultPieResize = () => { + if (faultPieChart && !faultPieChart.isDisposed()) { + faultPieChart.resize(); + } + }; + + window.addEventListener('resize', handleFaultPieResize); + + return () => { + window.removeEventListener('resize', handleFaultPieResize); + if (faultPieChart && !faultPieChart.isDisposed()) { + faultPieChart.dispose(); + } + }; + } + }, []); + + useEffect(() => { + if (chartRef.current) { + const chart = echarts.init(chartRef.current); + + // 强制初始化时调整大小 + setTimeout(() => { + if (chart && !chart.isDisposed()) { + chart.resize(); + } + }, 100); + + const option = { + color: ['#8979FF', '#3CC3DF'], + + legend: { + // data: ['消防水泵1', '消防水泵2'], + top: "-3px", + // left: "center", + // itemGap: 40, + itemWidth: 20, + itemHeight: 8, + // icon: 'path://M902 472.7H747.9c-19.1-113.3-117.7-200-236.4-200s-217.3 86.7-236.4 200H119.7c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h155.5c19.1 113.3 117.7 200 236.4 200S728.9 666 748 552.7h154c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z m-390.5 200c-88.2 0-160-71.8-160-160s71.8-160 160-160 160 71.8 160 160-71.8 160-160 160z', + textStyle: { + fontSize: 10 + } + }, + grid: { + left: '2%', + right: '4%', + bottom: '2%', + top: '12%', + containLabel: true + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: ['9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00', '18:00', '19:00', '20:00'], + axisLabel: { + fontSize: 10 + } + }, + yAxis: { + type: 'value', + min: 0, + max: 30, + axisLabel: { + formatter: '{value}', + fontSize: 10 + } + }, + series: [ + { + name: '消防水泵1', + type: 'line', + smooth: false, + lineStyle: { + width: 2, + color: '#8979FF' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { offset: 0, color: 'rgba(137, 121, 255, 0.3)' }, + { offset: 1, color: 'rgba(137, 121, 255, 0.05)' } + ] + } + }, + symbol: 'circle', + symbolSize: 4, + itemStyle: { + color: '#fff', + borderColor: '#8979FF', + borderWidth: 1 + }, + data: [12, 15, 18, 14, 16, 20, 22, 19, 17, 21, 23, 25] + }, + { + name: '消防水泵2', + type: 'line', + smooth: false, + lineStyle: { + width: 2, + color: '#3CC3DF' + }, + areaStyle: { + color: { + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { offset: 0, color: 'rgba(60, 195, 223, 0.3)' }, + { offset: 1, color: 'rgba(60, 195, 223, 0.05)' } + ] + } + }, + symbol: 'circle', + symbolSize: 4, + itemStyle: { + color: '#fff', + borderColor: '#3CC3DF', + borderWidth: 1 + }, + data: [8, 11, 14, 10, 13, 17, 19, 16, 14, 18, 20, 22] + } + ] + }; + + chart.setOption(option); + + // 响应式调整 - 使用多种方式监听容器尺寸变化 + let resizeTimer = null; + const handleResize = () => { + // 防抖处理,避免频繁调用resize + if (resizeTimer) { + clearTimeout(resizeTimer); + } + resizeTimer = setTimeout(() => { + if (chart && !chart.isDisposed()) { + chart.resize(); + } + }, 50); // 减少延迟时间 + }; + + // 监听窗口大小变化 + window.addEventListener('resize', handleResize); + + // 监听容器尺寸变化(解决菜单栏伸缩时的自适应问题) + let resizeObserver = null; + if (window.ResizeObserver) { + resizeObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + // 使用requestAnimationFrame确保在下一帧执行 + requestAnimationFrame(() => { + handleResize(); + }); + } + }); + resizeObserver.observe(chartRef.current); + } + + // 额外监听父容器的尺寸变化 + const parentContainer = chartRef.current?.parentElement; + let parentObserver = null; + if (parentContainer && window.ResizeObserver) { + parentObserver = new ResizeObserver((entries) => { + for (let entry of entries) { + requestAnimationFrame(() => { + handleResize(); + }); + } + }); + parentObserver.observe(parentContainer); + } + + // 使用MutationObserver监听DOM结构变化(菜单展开收起时) + const mutationObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.type === 'attributes' && + (mutation.attributeName === 'class' || mutation.attributeName === 'style')) { + // 延迟执行,确保DOM更新完成 + setTimeout(() => { + handleResize(); + }, 200); + } + }); + }); + + // 监听整个页面的class和style变化 + mutationObserver.observe(document.body, { + attributes: true, + attributeFilter: ['class', 'style'], + subtree: true + }); + + return () => { + window.removeEventListener('resize', handleResize); + if (resizeObserver) { + resizeObserver.disconnect(); + } + if (parentObserver) { + parentObserver.disconnect(); + } + if (mutationObserver) { + mutationObserver.disconnect(); + } + if (resizeTimer) { + clearTimeout(resizeTimer); + } + if (chart && !chart.isDisposed()) { + chart.dispose(); + } + }; + } + }, []); + + // 表格列定义 + const columns = [ + { + title: '编号', + dataIndex: 'id', + key: 'id', + width: 60, + render: (text, record, index) => { + const page = pagination.current || 1; + const pageSize = pagination.pageSize || 5; + const number = (page - 1) * pageSize + index + 1; + return `0${number}`.slice(-2); + } + }, + { + title: '设备编号', + dataIndex: 'deviceId', + key: 'deviceId', + width: 140, + }, + { + title: '设备名称', + dataIndex: 'deviceName', + key: 'deviceName', + width: 110, + }, + { + title: '型号规格', + dataIndex: 'modelSpec', + key: 'modelSpec', + width: 140, + }, + { + title: '安装位置', + dataIndex: 'installLocation', + key: 'installLocation', + width: 200, + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 80, + render: (text) => { + const statusMap = { + '故障': { color: '#FF4D4F', bg: '#FFF2F0' }, + '预警': { color: '#FAAD14', bg: '#FFF3E9' }, + '正常': { color: '#44BB5F', bg: '#D8F7DE' } + }; + const status = statusMap[text] || { color: '#333', bg: '#F5F5F5' }; + return ( + + {text} + + ); + } + }, + { + title: '最后维护', + dataIndex: 'lastMaintenance', + key: 'lastMaintenance', + width: 150, + }, + { + title: '操作', + key: 'action', + width: 140, + render: (_, record) => ( +
+ + +
+ ), + }, + ]; + + // 模拟数据 + const mockData = [ + { + key: '1', + id: '001', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼1层大厅', + status: '故障', + lastMaintenance: '2025-09-10', + }, + { + key: '2', + id: '002', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼3层 东区', + status: '预警', + lastMaintenance: '2025-09-10', + }, + { + key: '3', + id: '003', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '正常', + lastMaintenance: '2025-09-10', + }, + { + key: '4', + id: '004', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '故障', + lastMaintenance: '2025-09-10', + }, + { + key: '5', + id: '005', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '正常', + lastMaintenance: '2025-09-10', + }, + { + key: '6', + id: '006', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '预警', + lastMaintenance: '2025-09-10', + }, + { + key: '7', + id: '007', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '故障', + lastMaintenance: '2025-09-10', + }, + { + key: '8', + id: '008', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '正常', + lastMaintenance: '2025-09-10', + }, + { + key: '9', + id: '009', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '预警', + lastMaintenance: '2025-09-10', + }, + { + key: '10', + id: '010', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '故障', + lastMaintenance: '2025-09-10', + }, + { + key: '11', + id: '011', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '正常', + lastMaintenance: '2025-09-10', + }, + { + key: '12', + id: '012', + deviceId: 'HQ-XF-01-001', + deviceName: '消防水泵', + modelSpec: 'XBD5.0/30-125', + installLocation: '总部大楼地下一层', + status: '预警', + lastMaintenance: '2025-09-10', + }, + ]; + + // 初始化数据 + useEffect(() => { + setPagination(prev => ({ ...prev, total: mockData.length })); + }, []); + + // 根据分页获取当前页数据 + const getCurrentPageData = () => { + const { current, pageSize } = pagination; + const startIndex = (current - 1) * pageSize; + const endIndex = startIndex + pageSize; + return mockData.slice(startIndex, endIndex); + }; + + // 表格选择变化 + const onSelectChange = (newSelectedRowKeys, newSelectedRows) => { + setSelectedRowKeys(newSelectedRowKeys); + setSelectedRows(newSelectedRows); + }; + + // 新增设备按钮点击事件 + const handleAddDevice = () => { + console.log('新增设备'); + // TODO: 实现新增设备逻辑 + }; + + // 导出数据按钮点击事件 + const handleExportData = () => { + console.log('导出数据'); + // TODO: 实现导出数据逻辑 + }; + + // 分页变化处理 + const handleTableChange = (pagination) => { + setPagination(prev => ({ + ...prev, + current: pagination.current, + pageSize: pagination.pageSize, + })); + }; + + return ( +
+ {/* 第一个div - 高度20% */} +
+
+
+ {/* 块1 */} +
+
+
设备总数
+
1280
+
+
+ 设备总数 +
+
+ + {/* 块2 */} +
+
+
正常运行
+
480
+
+
+ 高风险设备 +
+
+ + {/* 块3 */} +
+
+
需要维护
+
347
+
+
+ 今日预警次数 +
+
+ + {/* 块4 */} +
+
+
故障设备
+
289
+
+
+ 未处理预警 +
+
+ +
+
+
+ + +
+
+
+
+
+
+ 设备状态分布 +
+ { + console.log(value); + }} + /> +
+ {/* 设备状态饼图 */} +
+
+ +
+ +
+
+
+
+ 设备故障类型分布 +
+ +
+
+
+
+
+ +
+
+ + {/* 第三个div - 占满剩余位置 */} +
+
+
+ {/* 第一行块 - 蓝色方块加标题 */} +
+
+
预警信息
+
+ +
+
+
+
灭火器压力不足
+
2号楼3层 丨 15分钟前
+
+
+
重要
+
+
+
+
+
烟雾探测器电池低电量
+
1号楼5层 丨 1小时前
+
+
+
重要
+
+
+
+
+
消防栓维护到期
+
3号楼1层 丨 2小时前
+
+
+
一般
+
+
+
+
+
应急照明故障
+
地下停车场 丨 3小时前
+
+
+
一般
+
+
+
+
+ +
+ {/* 表格 */} +
+
+
+
消防设备台账
+
+ +
+ + +
+
+ + {/* 表格 */} +
+ + `共 ${total} 条`, + }} + // scroll={{ x: 1200 }} + /> +
+
+
+
+ + ); +}; + +export default RiskAssessment; \ No newline at end of file diff --git a/src/pages/business_data/components/RiskAssessment.less b/src/pages/business_data/components/RiskAssessment.less new file mode 100644 index 0000000..db5b47e --- /dev/null +++ b/src/pages/business_data/components/RiskAssessment.less @@ -0,0 +1,594 @@ +.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: 4px; + border: 2px solid #FFFFFF; + + .blockLeft { + width: 60%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + padding: 15px; + padding-left: 20px; + gap: 8px; + + .blockTitle { + font-family: PingFang SC; + font-weight: 400; + font-size: 12px; + color: #333333; + line-height: 1.2; + } + + .blockNumber { + font-family: PingFang SC; + font-weight: 700; + font-size: 24px; + color: #333333; + line-height: 1.2; + } + + .blockChange { + font-family: PingFang SC; + font-weight: 400; + font-size: 12px; + color: #1269FF; + line-height: 1.2; + display: flex; + align-items: center; + gap: 4px; + + .arrow { + font-size: 14px; + font-weight: bold; + } + + .checkIcon { + font-size: 16px; + color: #1269FF; + } + } + } + + .blockRight { + flex: 1; + height: 100%; + background-color: transparent; + border-radius: 0 4px 4px 0; + display: flex; + align-items: center; + justify-content: center; + + .blockImage { + // width: 80%; + height: 65%; + // height: 80%; + object-fit: contain; + margin-right: -5px; + } + } + } + } + } + } + + // 第二个div - 高度39% + .RcontainerMiddle { + height: 33%; + border-radius: 4px; + display: flex; + flex-direction: column; + + .sectionContent { + height: 100%; + display: flex; + display: flex; + gap: 10px; + height: 100%; + + + + .middleBlock1 { + // flex: 1; + width: 28%; + height: 100%; + background: #fff; + + border: 2px solid #fff; + // border-radius: 4px; + position: relative; + padding: 0px 10px 10px 2px; + font-family: PingFang SC; + font-size: 14px; + color: #333333; + + .block1Header { + position: absolute; + top: 5px; + left: 10px; + right: 10px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 10; + + .block1Title { + display: flex; + align-items: center; + gap: 8px; + font-weight: 500; + font-size: 14px; + color: #333333; + + .titleIcon { + width: 3px; + height: 14px; + background-color: #2E4CD4; + } + } + + .block1Segmented { + padding: 0; + margin: 0; + border: 1px solid #E3E3E3; + border-radius: 4px; + height: 28px; + + :global(.ant-segmented) { + padding: 0; + margin: 0; + height: 28px; + } + + :global(.ant-segmented-item) { + font-size: 12px; + padding: 2px 8px; + height: 26px; + line-height: 26px; + display: flex; + align-items: center; + justify-content: center; + } + + :global(.ant-segmented-item-selected) { + background-color: #1890ff; + color: #fff; + } + } + } + + .deviceStatusChart { + position: absolute; + top: 35px; + left: 10px; + right: 10px; + bottom: 10px; + z-index: 10; + } + + // .block1Chart { + // width: 100%; + // height: 100%; + // margin-top: 20px; + + // .mapImage { + // margin-top: 7%; + // width: 90%; + // height: 77%; + // object-fit: cover; + // border-radius: 4px; + // display: block; + // margin-left: auto; + // margin-right: auto; + // } + // } + } + + .middleBlock12 { + flex: 1; + height: 100%; + background-color: #fff; + display: flex; + flex-direction: column; + font-family: PingFang SC; + font-size: 14px; + color: #333333; + padding: 5px 10px 5px 10px; + position: relative; + + .block1Header { + position: absolute; + top: 5px; + left: 10px; + right: 10px; + display: flex; + justify-content: space-between; + align-items: center; + z-index: 10; + + .block1Title { + display: flex; + align-items: center; + gap: 8px; + font-weight: 500; + font-size: 14px; + color: #333333; + + .titleIcon { + width: 3px; + height: 14px; + background-color: #2E4CD4; + } + } + + .block1Segmented { + padding: 0; + margin: 0; + border: 1px solid #E3E3E3; + border-radius: 4px; + height: 28px; + + :global(.ant-segmented) { + padding: 0; + margin: 0; + height: 28px; + } + + :global(.ant-segmented-item) { + font-size: 12px; + padding: 2px 8px; + height: 26px; + line-height: 26px; + display: flex; + align-items: center; + justify-content: center; + } + + :global(.ant-segmented-item-selected) { + background-color: #1890ff; + color: #fff; + } + } + + .customSelect { + :global(.ant-select-single:not(.ant-select-customize-input) .ant-select-selector) { + height: 26px !important; + display: flex !important; + align-items: center !important; + } + + :global(.ant-select-selection-item) { + line-height: 24px !important; + // height: 24px !important; + display: flex !important; + align-items: center !important; + } + } + } + + .deviceStatusChart { + position: absolute; + top: 35px; + left: 10px; + right: 10px; + bottom: 10px; + z-index: 10; + } + } + + .middleBlock2 { + flex: 1; + height: 100%; + // background: linear-gradient(170.5deg, #EBEFF4 6.87%, #FFFFFF 53.01%); + // border: 2px solid #fff; + background-color: #fff; + // border-radius: 4px; + display: flex; + flex-direction: column; + font-family: PingFang SC; + font-size: 14px; + color: #333333; + padding: 5px 10px 5px 10px; + + .middleBlock2Title { + display: flex; + justify-content: space-between; + align-items: center; + // margin-bottom: 10px; + + .titleLeft { + display: flex; + align-items: center; + gap: 8px; + font-weight: 500; + font-size: 14px; + color: #333333; + + .titleIcon { + width: 3px; + height: 14px; + background-color: #2E4CD4; + } + } + + .titleRight { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: #666; + } + } + + .middleBlock2Chart { + width: 100%; + height: 100%; + // min-height: 200px; + } + } + + } + } + + // 第三个div - 高度不超过45% + .RcontainerBottom { + height: 45%; // 限制高度不超过45% + max-height: 45%; // 确保最大高度不超过45% + display: flex; + flex-direction: column; + + .sectionContent { + display: flex; + flex-direction: row; + gap: 10px; + padding: 0; + + .leftBlock { + width: 28%; + flex-shrink: 0; + height: 100%; + background: #fff; + // background-size: cover; + padding: 0; + display: flex; + flex-direction: column; + gap: 10px; + padding: 15px; + + .leftBlockTitle { + display: flex; + align-items: center; + gap: 8px; + font-family: PingFang SC; + font-weight: 500; + font-size: 14px; + color: #333333; + margin-bottom: 10px; + + .titleIcon { + width: 3px; + height: 16px; + background-color: #2E4CD4; + } + } + + .developmentContainer { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + gap: 8px; + + .developmentBlock1 { + flex: 1; + background-color: #F1F7FF; + border-radius: 4px; + padding: 15px 20px; + display: flex; + align-items: center; + width: 100%; + + .leftContent { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + min-width: 0; + + .mainText { + color: #333333; + font-size: 14px; + font-weight: 500; + font-family: PingFang SC; + width: 100%; + max-width: 500px; + } + + .subText { + color: #666666; + font-size: 12px; + font-weight: 400; + font-family: PingFang SC; + width: 100%; + max-width: 400px; + } + } + + .rightContent { + flex: 0 0 auto; + display: flex; + justify-content: flex-end; + align-items: center; + padding-right: 10px; + min-width: 80px; + + .importantTag { + background-color: #FFE0E2; + color: #FF3E48; + font-size: 14px; + font-weight: 500; + font-family: PingFang SC; + width: 45px; + height: 25px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + } + + .normalTag { + background-color: #DAF3FF; + color: #00AAFA; + font-size: 14px; + font-weight: 500; + font-family: PingFang SC; + width: 45px; + height: 25px; + + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + } + } + } + } + } + + .rightBlock { + width: calc(100% - 28% - 10px); + height: 100%; + background-color: #fff; + padding: 0; + display: flex; + flex-direction: column; + + .tableHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 15px 5px 15px; + + .tableTitle { + display: flex; + align-items: center; + gap: 8px; + font-family: PingFang SC; + font-weight: 500; + font-size: 14px; + color: #333333; + + .titleIcon { + width: 3px; + height: 16px; + background-color: #2E4CD4; + } + } + + .tableActions { + display: flex; + gap: 8px; + margin-top: 5px; + + .actionButton { + display: flex; + align-items: center; + gap: 4px; + height: 28px; + border: 1px solid #DFE4F6; + border-radius: 4px; + color: #2E4CD4; + font-weight: 500; + font-size: 12px; + padding: 0px 8px; + background: transparent; + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + background-color: #f0f2ff; + border-color: #2E4CD4; + } + + &:active { + background-color: #e6ebff; + } + + .buttonIcon { + font-size: 14px; + font-weight: bold; + } + } + } + } + + .tableContainer { + flex: 1; + overflow: hidden; + margin: 10px 15px 0 15px; // 上边距10px,左右边距15px + + :global(.ant-table) { + font-size: 12px; + } + + :global(.ant-table-thead > tr > th) { + background-color: #f5f5fa; + font-weight: 500; + font-size: 14px; + color: #333333; + border-bottom: 1px solid #f0f0f0; + padding: 8px 12px; + text-align: center; + } + + :global(.ant-table-tbody > tr > td) { + padding: 8px 12px; + border-bottom: 1px solid #f0f0f0; + text-align: center; + color: #666666; + } + + :global(.ant-table-tbody > tr:hover > td) { + background-color: #f5f5f5; + } + + :global(.ant-pagination) { + margin-top: 16px; + text-align: right; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/pages/business_data/components/bmzstx.js b/src/pages/business_data/components/bmzstx.js new file mode 100644 index 0000000..818a4a4 --- /dev/null +++ b/src/pages/business_data/components/bmzstx.js @@ -0,0 +1,265 @@ +import React, { useMemo, useState } from 'react'; +import { Button, message, Popconfirm, Select, Space, Table } from 'antd'; +import { DeleteOutlined, DownloadOutlined, EditOutlined, FileTextOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons'; +import styles from './jgfx.less'; + +const Bmzstx = () => { + const [filterA, setFilterA] = useState(undefined); + const [filterB, setFilterB] = useState(undefined); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [pagination, setPagination] = useState({ current: 1, pageSize: 10 }); + + const selectOptionsA = useMemo( + () => [ + { label: '千瓦时(kWh)', value: '千瓦时(kWh)' }, + { label: '立方米(m³)', value: '立方米(m³)' }, + { label: '吨(t)', value: '吨(t)' }, + { label: '千克(kg)', value: '千克(kg)' }, + ], + [], + ); + + const selectOptionsB = useMemo( + () => [ + { label: '电能', value: '电能' }, + { label: '燃气', value: '燃气' }, + { label: '水', value: '水' }, + { label: '汽油', value: '汽油' }, + ], + [], + ); + + const rows = useMemo( + () => [ + { + id: 1, + energyType: '电能', + unit: '千瓦时(kWh)', + unitPrice: 0.5, + priceUnit: 'kgce/kwh', + scope: '国标(2024 GB-282287)', + pricingUnit: '—', + latestUpdateTime: '2025-12-02 03:56:02', + }, + { + id: 2, + energyType: '燃气', + unit: '立方米(m³)', + unitPrice: 2.5, + priceUnit: 'kgce/m³', + scope: '国标(2024 GB-282287)', + pricingUnit: '—', + latestUpdateTime: '2025-11-22 11:56:50', + }, + { + id: 3, + energyType: '水', + unit: '吨(t)', + unitPrice: 1.2, + priceUnit: 'kgce/t', + scope: '行业标准(2024 GB-282287)', + pricingUnit: '—', + latestUpdateTime: '2025-12-04 21:12:20', + }, + { + id: 4, + energyType: '汽油', + unit: '千克(kg)', + unitPrice: 1.5, + priceUnit: 'kgce/kg', + scope: '行业标准(2024 GB-282287)', + pricingUnit: '—', + latestUpdateTime: '2025-11-26 10:28:20', + }, + ], + [], + ); + + const tableData = useMemo(() => { + return rows + .filter((r) => (filterA ? r.unit === filterA : true)) + .filter((r) => (filterB ? r.energyType === filterB : true)); + }, [filterA, filterB, rows]); + + const pagedData = useMemo(() => { + const start = (pagination.current - 1) * pagination.pageSize; + return tableData.slice(start, start + pagination.pageSize); + }, [pagination.current, pagination.pageSize, tableData]); + + const handleAdd = () => message.info('新增'); + const handleUpload = () => message.info('上传'); + const handleBatchDownload = () => message.info('批量下载'); + const handleQuery = () => message.info('查询'); + + const handleEdit = (record) => message.info(`编辑:${record.energyType}`); + const handleView = (record) => message.info(`详情:${record.energyType}`); + const handleDelete = (record) => message.success(`已删除:${record.energyType}`); + + const columns = useMemo( + () => [ + { + title: '序号', + dataIndex: 'id', + key: 'id', + width: 70, + align: 'center', + }, + { + title: '能源类型', + dataIndex: 'energyType', + key: 'energyType', + width: 120, + }, + { + title: '单位', + dataIndex: 'unit', + key: 'unit', + width: 160, + }, + { + title: '单价', + dataIndex: 'unitPrice', + key: 'unitPrice', + width: 110, + }, + { + title: '价格单位', + dataIndex: 'priceUnit', + key: 'priceUnit', + width: 120, + }, + { + title: '适用范围', + dataIndex: 'scope', + key: 'scope', + width: 120, + }, + { + title: '定价单位', + dataIndex: 'pricingUnit', + key: 'pricingUnit', + width: 140, + }, + { + title: '最后更新时间', + dataIndex: 'latestUpdateTime', + key: 'latestUpdateTime', + width: 210, + }, + { + title: '操作', + key: 'action', + width: 140, + align: 'center', + render: (_, record) => ( + + + + + + + +
+
筛选条件
+ + + + +
+ + +
+ `共 ${total} 条`, + position: ['bottomRight'], + onChange: (current, pageSize) => setPagination({ current, pageSize }), + }} + rowSelection={{ + selectedRowKeys, + onChange: (keys) => setSelectedRowKeys(keys), + columnWidth: 44, + }} + /> + + + ); +}; + +export default Bmzstx; diff --git a/src/pages/business_data/components/bmzstx.less b/src/pages/business_data/components/bmzstx.less new file mode 100644 index 0000000..65030ce --- /dev/null +++ b/src/pages/business_data/components/bmzstx.less @@ -0,0 +1,160 @@ +.container { + width: 100%; + height: 100%; + min-height: 560px; + background: #fff; + border-radius: 14px; + padding: 14px 16px 12px; + overflow: hidden; +} + +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 2px 0 14px; +} + +.toolbarLeft { + display: flex; + align-items: center; +} + +.toolbarRight { + display: flex; + align-items: center; +} + +.filterLabel { + font-size: 12px; + color: #8d93a3; + margin-right: 10px; +} + +.filterSelect { + width: 170px; + + :global { + .ant-select-selector { + height: 32px !important; + border-radius: 999px !important; + border-color: #e7eaf2 !important; + display: flex !important; + align-items: center !important; + box-shadow: none !important; + } + + .ant-select-selection-placeholder { + color: #b1b7c4; + } + } +} + +.primaryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + box-shadow: none !important; + background-color: rgba(72, 81, 255, 1); + + :global { + .ant-btn-icon { + margin-right: 6px; + } + } +} + +.ghostBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.queryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.tableWrap { + width: 100%; + + :global { + .ant-table { + border-radius: 10px; + } + + .ant-table-thead > tr > th { + background: #f7f8fb; + color: #6d7383; + font-weight: 500; + height: 44px; + } + + .ant-table-tbody > tr > td { + color: #2b2f3a; + height: 48px; + } + + .ant-table-tbody > tr:hover > td { + background: #fafbff; + } + + .ant-table-pagination { + margin: 14px 0 0; + } + + .ant-pagination-total-text { + color: #8d93a3; + margin-right: 10px; + } + + .ant-pagination-options { + margin-left: 10px; + } + } +} + +.actionIconBtn { + padding: 0 !important; + height: 24px !important; + color: rgba(72, 81, 255, 1) !important; + + :global { + .anticon { + font-size: 16px; + } + } +} + +.actionIconBtnInfo { + padding: 0 !important; + height: 24px !important; + color: #13c2c2 !important; + + :global { + .anticon { + font-size: 16px; + } + } +} + +.actionIconBtnDanger { + padding: 0 !important; + height: 24px !important; + color: #ff4d4f !important; + + :global { + .anticon { + font-size: 16px; + } + } +} diff --git a/src/pages/business_data/components/fgpsdsz.js b/src/pages/business_data/components/fgpsdsz.js new file mode 100644 index 0000000..bf9240e --- /dev/null +++ b/src/pages/business_data/components/fgpsdsz.js @@ -0,0 +1,290 @@ +import React, { useMemo, useState } from 'react'; +import { Button, Radio, Space, Tag } from 'antd'; +import { LeftOutlined, PlusOutlined, RightOutlined, SettingOutlined } from '@ant-design/icons'; +import styles from './fgpsdsz.less'; +import Component1 from '@/assets/basic_data/Component1.svg'; +import Component2 from '@/assets/basic_data/Component2.svg'; +import Component3 from '@/assets/basic_data/Component3.svg'; + +const TIME_LABELS = [ + '09:00', + '11:00', + '13:00', + '15:00', + '17:00', + '19:00', + '21:00', + '23:00', + '01:00', + '03:00', + '05:00', + '07:00', + '09:00', +]; + +const MONTH_LABEL = '2025年8月'; + +const Fgpsdsz = () => { + const [activeEnergyKey, setActiveEnergyKey] = useState('electric'); + const [viewMode, setViewMode] = useState('month'); + const [periodType, setPeriodType] = useState('peak'); + + const energyTypes = useMemo( + () => [ + { key: 'electric', label: '电能', icon: Component1 }, + { key: 'water', label: '水', icon: Component2 }, + { key: 'gas', label: '天然气', icon: Component3 }, + ], + [], + ); + + const cards = useMemo(() => { + const byEnergyKey = { + electric: [ + { + key: 'peak', + title: '高峰时段', + price: '1.58', + unit: '(元/度)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + { + key: 'flat', + title: '平时时段', + price: '0.58', + unit: '(元/度)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + { + key: 'valley', + title: '低谷时段', + price: '0.28', + unit: '(元/度)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + ], + water: [ + { + key: 'peak', + title: '高峰时段', + price: '2.30', + unit: '(元/m³)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + { + key: 'flat', + title: '平时时段', + price: '1.30', + unit: '(元/m³)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + { + key: 'valley', + title: '低谷时段', + price: '0.80', + unit: '(元/m³)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + ], + gas: [ + { + key: 'peak', + title: '高峰时段', + price: '3.80', + unit: '(元/m³)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + { + key: 'flat', + title: '平时时段', + price: '2.80', + unit: '(元/m³)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + { + key: 'valley', + title: '低谷时段', + price: '1.60', + unit: '(元/m³)', + tags: ['工作日', '周末', '节假日'], + periods: ['起止时刻1:09:00:00–12:00:00', '起止时刻2:09:00:00–12:00:00'], + }, + ], + }; + + return byEnergyKey[activeEnergyKey] ?? byEnergyKey.electric; + }, [activeEnergyKey]); + + const scheduleRows = useMemo( + () => [ + { day: 1, weekday: '一', bars: [{ start: 0, end: 2, type: 'peak' }, { start: 9, end: 13, type: 'valley' }] }, + { day: 2, weekday: '二', bars: [{ start: 0, end: 5, type: 'peak' }, { start: 11, end: 13, type: 'valley' }] }, + { day: 3, weekday: '三', bars: [{ start: 0, end: 4, type: 'peak' }, { start: 10, end: 13, type: 'valley' }] }, + { day: 4, weekday: '四', bars: [{ start: 0, end: 4, type: 'peak' }, { start: 9, end: 13, type: 'valley' }] }, + { day: 5, weekday: '五', bars: [{ start: 0, end: 4, type: 'peak' }, { start: 8, end: 13, type: 'valley' }] }, + { day: 6, weekday: '六', bars: [{ start: 0, end: 5, type: 'peak' }, { start: 11, end: 13, type: 'valley' }] }, + { day: 7, weekday: '日', bars: [{ start: 0, end: 4, type: 'peak' }, { start: 9, end: 13, type: 'valley' }] }, + { day: 8, weekday: '一', bars: [{ start: 0, end: 3, type: 'peak' }, { start: 8, end: 13, type: 'valley' }] }, + { day: 9, weekday: '二', bars: [{ start: 0, end: 5, type: 'peak' }, { start: 10, end: 13, type: 'valley' }] }, + { day: 10, weekday: '三', bars: [{ start: 0, end: 2, type: 'peak' }, { start: 8, end: 13, type: 'valley' }] }, + { day: 11, weekday: '四', bars: [{ start: 0, end: 2, type: 'peak' }, { start: 8, end: 13, type: 'valley' }] }, + { day: 12, weekday: '五', bars: [{ start: 0, end: 2, type: 'peak' }, { start: 8, end: 13, type: 'valley' }] }, + ], + [], + ); + + const renderBarStyle = (bar) => { + const left = (bar.start / TIME_LABELS.length) * 100; + const width = ((bar.end - bar.start) / TIME_LABELS.length) * 100; + return { left: `${left}%`, width: `${width}%` }; + }; + + return ( +
+
+
能源类型
+ +
+ {energyTypes.map((item) => { + const isActive = item.key === activeEnergyKey; + return ( + + ); + })} + + +
+
+ +
+
+ {cards.map((card) => ( +
+
+ +
+ +
+
{card.title}
+
+ {card.tags.map((tag) => ( + {tag} + ))} +
+
+ +
+
+
单价
+
{card.price}
+
{card.unit}
+
+ +
+
+ {card.periods.map((text) => ( +
+ {text} +
+ ))} +
+
+
+
+ ))} +
+ +
+
+
+ + +
+ +
+
+
+ setViewMode(e.target.value)} + > + + + +
+ +
+ {TIME_LABELS.map((t) => ( +
+ {t} +
+ ))} +
+
+ +
+ {scheduleRows.map((row) => ( +
+
{row.day}
+
{row.weekday}
+ +
+ {row.bars.map((bar, idx) => { + const isDimmed = periodType && bar.type !== periodType; + return ( +
+ ); + })} +
+
+ ))} +
+
+
+
+
+ ); +}; + +export default Fgpsdsz; diff --git a/src/pages/business_data/components/fgpsdsz.less b/src/pages/business_data/components/fgpsdsz.less new file mode 100644 index 0000000..3564a6a --- /dev/null +++ b/src/pages/business_data/components/fgpsdsz.less @@ -0,0 +1,458 @@ +.container { + display: flex; + gap: 15px; + width: 100%; + height: 100%; + min-height: 560px; + padding: 15px 10px; +} + +.leftPanel { + width: 200px; + background: #fff; + border-radius: 12px; + padding: 15px 10px; +} + +.leftTitle { + font-size: 15px; + font-weight: 500; + color: #2b2f3a; + padding: 2px 10px 14px; +} + +.leftList { + display: flex; + flex-direction: column; + gap: 12px; + padding: 0 8px; +} + +.leftItemBtn { + width: 100%; + height: 40px !important; + padding: 0 14px !important; + border-radius: 999px !important; + border: 1px solid #e8ecf3 !important; + background: #fff !important; + box-shadow: none !important; + display: flex !important; + align-items: center !important; + justify-content: flex-start !important; + color: #a5adbb !important; + + &:hover { + border-color: rgba(72, 81, 255, 0.35) !important; + color: rgba(72, 81, 255, 1) !important; + } +} + +.leftItemActive { + background: rgba(72, 81, 255, 1) !important; + border-color: rgba(72, 81, 255, 1) !important; + color: #fff !important; + box-shadow: 0 10px 20px rgba(72, 81, 255, 0.18) !important; + + &:hover { + color: #fff !important; + } + + .leftIconCircle { + background: rgba(255, 255, 255, 0.26); + color: #fff; + } +} + +.leftIconCircle { + width: 18px; + height: 18px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + background: rgba(72, 81, 255, 0.12); + color: rgba(72, 81, 255, 1); + flex: none; + + :global { + .anticon { + font-size: 12px; + line-height: 1; + } + } +} + +.leftIconImg { + width: 18px; + height: 18px; + display: block; + object-fit: contain; +} + +.leftItemText { + margin-left: 10px; + font-size: 14px; + font-weight: 500; +} + +.leftAddBtn { + width: 100%; + height: 40px !important; + border-radius: 999px !important; + border: none !important; + background: #f2f4f8 !important; + color: #b7bdc9 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + box-shadow: none !important; +} + +.rightPanel { + flex: 1; + background: #fff; + border-radius: 12px; + padding: 14px 16px 12px; + overflow: hidden; + display: flex; + flex-direction: column; + gap: 14px; +} + +.cardsRow { + display: flex; + gap: 16px; +} + +.priceCard { + flex: 1; + min-height: 118px; + padding: 14px 16px 12px; + border-radius: 14px; + color: #fff; + position: relative; + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.priceCard_peak { + background: linear-gradient(135deg, #ff5e57 0%, #ffb1ae 100%); +} + +.priceCard_flat { + background: linear-gradient(135deg, #ff9c3c 0%, #ffd8a8 100%); +} + +.priceCard_valley { + background: linear-gradient(135deg, #4cc9ff 0%, #a7e9ff 100%); +} + +.priceCardCorner { + position: absolute; + top: 12px; + right: 12px; + width: 26px; + height: 26px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.35); + display: flex; + align-items: center; + justify-content: center; + color: #fff; + + :global { + .anticon { + font-size: 14px; + } + } +} + +.priceCardTop { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.priceCardTitle { + font-size: 16px; + font-weight: 600; + line-height: 1; + padding-top: 2px; +} + +.priceCardTags { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-right: 28px; + + :global { + .ant-tag { + margin: 0; + // margin-right: 30px; + border: none; + border-radius: 999px; + background: rgba(255, 255, 255, 0.26); + color: #fff; + font-size: 12px; + padding: 0 10px; + height: 22px; + line-height: 22px; + } + } +} + +.priceCardBody { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 12px; + margin-top: 8px; +} + +.priceLeft { + min-width: 96px; +} + +.priceLabel { + font-size: 12px; + opacity: 0.92; +} + +.priceValue { + font-size: 36px; + font-weight: 700; + line-height: 1.05; + margin-top: 6px; +} + +.priceUnit { + font-size: 12px; + opacity: 0.92; + margin-top: 4px; +} + +.priceRight { + flex: 1; + display: flex; + justify-content: flex-end; +} + +.pricePeriodBox { + width: 220px; + padding: 10px 12px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.22); + backdrop-filter: blur(6px); +} + +.pricePeriodLine { + font-size: 12px; + line-height: 18px; + opacity: 0.96; +} + +.scheduleSection { + flex: 1; + background: #f7f9ff; + border-radius: 12px; + padding: 12px; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.scheduleTopBar { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 0 4px 10px; +} + +.scheduleTopLeft { + width: 88px; + flex: none; +} + +.monthSwitcher { + flex: 1; + justify-content: center; + color: #3757ff; + + :global { + .ant-btn { + padding: 0 6px; + } + } +} + +.monthText { + font-size: 14px; + font-weight: 600; +} + +.periodRadios { + :global { + .ant-radio-wrapper { + color: #6b7280; + font-size: 12px; + margin-inline-end: 10px; + } + + .ant-radio-wrapper-checked { + color: #3757ff; + } + } +} + +.scheduleGrid { + background: #fff; + border-radius: 12px; + padding: 10px 10px 12px; + overflow: auto; + min-height: 360px; +} + +.scheduleHeaderRow { + display: flex; + align-items: center; + gap: 10px; + padding: 0 0 10px; +} + +.scheduleHeaderLeft { + width: 88px; + flex: none; + display: flex; + align-items: center; + + :global { + .ant-radio-group { + background: #eef3ff; + border-radius: 999px; + padding: 2px; + } + + .ant-radio-button-wrapper { + border: none; + height: 24px; + line-height: 22px; + border-radius: 999px !important; + padding: 0 12px; + font-size: 12px; + color: #6b7280; + background: transparent; + box-shadow: none; + } + + .ant-radio-button-wrapper-checked { + background: #3a66ff; + color: #fff; + box-shadow: none; + } + } +} + +.timeHeader { + flex: 1; + min-width: 780px; + display: flex; + align-items: center; + height: 24px; + border-radius: 999px; + background: #f7f9ff; + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + inset: 0; + background-image: linear-gradient(to right, rgba(232, 236, 243, 0.9) 1px, transparent 1px); + background-size: calc(100% / 13) 100%; + pointer-events: none; + } +} + +.timeHeaderCell { + flex: 1; + text-align: center; + font-size: 12px; + color: #6b7280; + position: relative; + z-index: 1; +} + +.scheduleBody { + display: flex; + flex-direction: column; + gap: 12px; + padding-top: 6px; +} + +.scheduleRow { + display: flex; + align-items: center; + gap: 10px; +} + +.dayCell { + width: 44px; + text-align: center; + color: #2b2f3a; + font-size: 13px; +} + +.weekCell { + width: 44px; + text-align: center; + color: #8d93a3; + font-size: 12px; +} + +.timeArea { + flex: 1; + min-width: 780px; + height: 28px; + border-radius: 12px; + background: #f7f9ff; + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + inset: 0; + background-image: linear-gradient(to right, rgba(232, 236, 243, 0.9) 1px, transparent 1px); + background-size: calc(100% / 13) 100%; + pointer-events: none; + } +} + +.timeBar { + position: absolute; + top: 5px; + height: 18px; + border-radius: 999px; + z-index: 1; +} + +.timeBar_peak { + background: rgba(255, 111, 111, 0.92); +} + +.timeBar_flat { + background: rgba(255, 177, 61, 0.92); +} + +.timeBar_valley { + background: rgba(120, 205, 255, 0.92); +} + +.timeBarDim { + opacity: 0.25; +} diff --git a/src/pages/business_data/components/jgfx.js b/src/pages/business_data/components/jgfx.js new file mode 100644 index 0000000..ca0f4a9 --- /dev/null +++ b/src/pages/business_data/components/jgfx.js @@ -0,0 +1,253 @@ +import React, { useMemo, useState } from 'react'; +import { Button, message, Popconfirm, Select, Space, Table } from 'antd'; +import { DeleteOutlined, DownloadOutlined, EditOutlined, FileTextOutlined, PlusOutlined, UploadOutlined } from '@ant-design/icons'; +import styles from './jgfx.less'; + +const Jgfx = () => { + const [filterA, setFilterA] = useState(undefined); + const [filterB, setFilterB] = useState(undefined); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [pagination, setPagination] = useState({ current: 1, pageSize: 10 }); + + const selectOptionsA = useMemo( + () => [ + { label: '千瓦时(kWh)', value: '千瓦时(kWh)' }, + { label: '立方米(m³)', value: '立方米(m³)' }, + { label: '吨(t)', value: '吨(t)' }, + { label: 'kWh', value: 'kWh' }, + ], + [], + ); + + const selectOptionsB = useMemo( + () => [ + { label: '电能', value: '电能' }, + { label: '燃气', value: '燃气' }, + { label: '水', value: '水' }, + { label: '汽油', value: '汽油' }, + ], + [], + ); + + const rows = useMemo( + () => [ + { + id: 1, + energyType: '电能', + unit: '千瓦时(kWh)', + unitPrice: 0.5, + priceUnit: '元', + scope: 'xxxx', + pricingUnit: 'xxxxxx', + latestUpdateTime: '2025-12-02 03:56:02', + }, + { + id: 2, + energyType: '燃气', + unit: '立方米(m³)', + unitPrice: 2.5, + priceUnit: '元', + scope: 'xxxx', + pricingUnit: 'xxxxxx', + latestUpdateTime: '2025-11-22 11:56:50', + }, + { + id: 3, + energyType: '水', + unit: '吨(t)', + unitPrice: 1.2, + priceUnit: '元', + scope: 'xxxx', + pricingUnit: 'xxxxxx', + latestUpdateTime: '2025-12-04 21:12:20', + }, + { + id: 4, + energyType: '汽油', + unit: 'kWh', + unitPrice: 1.5, + priceUnit: '元', + scope: 'xxxx', + pricingUnit: 'xxxxxx', + latestUpdateTime: '2025-11-26 10:28:20', + }, + ], + [], + ); + + const tableData = useMemo(() => { + return rows + .filter((r) => (filterA ? r.unit === filterA : true)) + .filter((r) => (filterB ? r.energyType === filterB : true)); + }, [filterA, filterB, rows]); + + const pagedData = useMemo(() => { + const start = (pagination.current - 1) * pagination.pageSize; + return tableData.slice(start, start + pagination.pageSize); + }, [pagination.current, pagination.pageSize, tableData]); + + const handleAdd = () => message.info('新增'); + const handleUpload = () => message.info('上传'); + const handleBatchDownload = () => message.info('批量下载'); + const handleQuery = () => message.info('查询'); + + const handleEdit = (record) => message.info(`编辑:${record.energyType}`); + const handleView = (record) => message.info(`详情:${record.energyType}`); + const handleDelete = (record) => message.success(`已删除:${record.energyType}`); + + const columns = useMemo( + () => [ + { + title: '序号', + dataIndex: 'id', + key: 'id', + width: 70, + align: 'center', + }, + { + title: '能源类型', + dataIndex: 'energyType', + key: 'energyType', + width: 120, + }, + { + title: '单位', + dataIndex: 'unit', + key: 'unit', + width: 160, + }, + { + title: '单价', + dataIndex: 'unitPrice', + key: 'unitPrice', + width: 110, + }, + { + title: '价格单位', + dataIndex: 'priceUnit', + key: 'priceUnit', + width: 120, + }, + { + title: '适用范围', + dataIndex: 'scope', + key: 'scope', + width: 120, + }, + { + title: '定价单位', + dataIndex: 'pricingUnit', + key: 'pricingUnit', + width: 140, + }, + { + title: '最后更新时间', + dataIndex: 'latestUpdateTime', + key: 'latestUpdateTime', + width: 210, + }, + { + title: '操作', + key: 'action', + width: 140, + align: 'center', + render: (_, record) => ( + + + + + +
+ +
+
筛选条件
+ + + + +
+
+ +
+
`共 ${total} 条`, + position: ['bottomRight'], + onChange: (current, pageSize) => setPagination({ current, pageSize }), + }} + rowSelection={{ + selectedRowKeys, + onChange: (keys) => setSelectedRowKeys(keys), + columnWidth: 44, + }} + /> + + + ); +}; + +export default Jgfx; diff --git a/src/pages/business_data/components/jgfx.less b/src/pages/business_data/components/jgfx.less new file mode 100644 index 0000000..d233aeb --- /dev/null +++ b/src/pages/business_data/components/jgfx.less @@ -0,0 +1,161 @@ +.container { + width: 100%; + height: 100%; + min-height: 560px; + background: #fff; + border-radius: 14px; + padding: 14px 16px 12px; + margin:15px 10px; + overflow: hidden; +} + +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 2px 0 14px; +} + +.toolbarLeft { + display: flex; + align-items: center; +} + +.toolbarRight { + display: flex; + align-items: center; +} + +.filterLabel { + font-size: 12px; + color: #8d93a3; + margin-right: 10px; +} + +.filterSelect { + width: 170px; + + :global { + .ant-select-selector { + height: 32px !important; + border-radius: 999px !important; + border-color: #e7eaf2 !important; + display: flex !important; + align-items: center !important; + box-shadow: none !important; + } + + .ant-select-selection-placeholder { + color: #b1b7c4; + } + } +} + +.primaryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + box-shadow: none !important; + background-color: rgba(72, 81, 255, 1); + + :global { + .ant-btn-icon { + margin-right: 6px; + } + } +} + +.ghostBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.queryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.tableWrap { + width: 100%; + + :global { + .ant-table { + border-radius: 10px; + } + + .ant-table-thead > tr > th { + background: #f7f8fb; + color: #6d7383; + font-weight: 500; + height: 44px; + } + + .ant-table-tbody > tr > td { + color: #2b2f3a; + height: 48px; + } + + .ant-table-tbody > tr:hover > td { + background: #fafbff; + } + + .ant-table-pagination { + margin: 14px 0 0; + } + + .ant-pagination-total-text { + color: #8d93a3; + margin-right: 10px; + } + + .ant-pagination-options { + margin-left: 10px; + } + } +} + +.actionIconBtn { + padding: 0 !important; + height: 24px !important; + color: rgba(72, 81, 255, 1) !important; + + :global { + .anticon { + font-size: 16px; + } + } +} + +.actionIconBtnInfo { + padding: 0 !important; + height: 24px !important; + color: #13c2c2 !important; + + :global { + .anticon { + font-size: 16px; + } + } +} + +.actionIconBtnDanger { + padding: 0 !important; + height: 24px !important; + color: #ff4d4f !important; + + :global { + .anticon { + font-size: 16px; + } + } +} diff --git a/src/pages/business_data/components/nhtj.js b/src/pages/business_data/components/nhtj.js new file mode 100644 index 0000000..7610cbe --- /dev/null +++ b/src/pages/business_data/components/nhtj.js @@ -0,0 +1,10 @@ +import React from 'react'; +import styles from './nhtj.less'; + +const Nhtj = () => { + return ( +
待开发
+ ); +}; + +export default Nhtj; diff --git a/src/pages/business_data/components/nhtj.less b/src/pages/business_data/components/nhtj.less new file mode 100644 index 0000000..014325f --- /dev/null +++ b/src/pages/business_data/components/nhtj.less @@ -0,0 +1,4 @@ +.placeholder { + padding: 20px; + color: #999; +} diff --git a/src/pages/business_data/components/nhtjcs.js b/src/pages/business_data/components/nhtjcs.js new file mode 100644 index 0000000..3c2cd76 --- /dev/null +++ b/src/pages/business_data/components/nhtjcs.js @@ -0,0 +1,260 @@ +import React, { useMemo, useState } from 'react'; +import { Button, message, Popconfirm, Select, Space, Table } from 'antd'; +import { + DeleteOutlined, + DownloadOutlined, + EditOutlined, + PlusOutlined, + UploadOutlined, +} from '@ant-design/icons'; +import styles from './nyfl.less'; +import Component1 from '@/assets/basic_data/Component1.svg'; +import Component2 from '@/assets/basic_data/Component2.svg'; +import Component3 from '@/assets/basic_data/Component3.svg'; + +const Nhtjcs = () => { + const [activeEnergyKey, setActiveEnergyKey] = useState('electric'); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + const [filterA, setFilterA] = useState(undefined); + const [filterB, setFilterB] = useState(undefined); + + const energyTypes = useMemo( + () => [ + { key: 'electric', label: '电能', icon: Component1 }, + { key: 'water', label: '水', icon: Component2 }, + { key: 'gas', label: '天然气', icon: Component3 }, + ], + [], + ); + + const [rows, setRows] = useState(() => [ + { + id: 1, + energyKey: 'electric', + powerType: '泵类系统', + deviceCount: 28, + statisticMethod: '智能电表', + usageDesc: '用于油品输送的各类泵设备的耗电量。', + }, + { + id: 2, + energyKey: 'electric', + powerType: '加热保温系统', + deviceCount: 23, + statisticMethod: '智能电表', + usageDesc: '用于油罐加热和管道保温的电加热设备的耗电量。', + }, + { + id: 3, + energyKey: 'electric', + powerType: '照明系统', + deviceCount: 55, + statisticMethod: '手动抄表', + usageDesc: '油库区域内照明设备的耗电量,包括室内照明和室外照明。', + }, + { + id: 4, + energyKey: 'electric', + powerType: '生活用电系统', + deviceCount: 21, + statisticMethod: '手动抄表', + usageDesc: '油库工作人员生活区的用电,如宿舍、办公区等的耗电量。', + }, + ]); + + const scopedRows = useMemo(() => { + return rows.filter((row) => row.energyKey === activeEnergyKey); + }, [activeEnergyKey, rows]); + + const selectOptionsA = useMemo(() => { + const values = Array.from(new Set(scopedRows.map((r) => r.powerType).filter(Boolean))); + return values.map((value) => ({ label: value, value })); + }, [scopedRows]); + + const selectOptionsB = useMemo(() => { + const values = Array.from(new Set(scopedRows.map((r) => r.statisticMethod).filter(Boolean))); + return values.map((value) => ({ label: value, value })); + }, [scopedRows]); + + const tableData = useMemo(() => { + return scopedRows + .filter((row) => (filterA ? row.powerType === filterA : true)) + .filter((row) => (filterB ? row.statisticMethod === filterB : true)); + }, [filterA, filterB, scopedRows]); + + const handleAdd = () => message.info('新增'); + const handleUpload = () => message.info('上传'); + const handleBatchDownload = () => message.info('批量下载'); + const handleQuery = () => message.info('查询'); + + const handleEdit = (record) => message.info(`编辑:${record.powerType}`); + const handleDelete = (record) => message.success(`已删除:${record.powerType}`); + + const columns = useMemo( + () => [ + { + title: '序号', + dataIndex: 'id', + key: 'id', + width: 70, + align: 'center', + }, + { + title: '耗电类型', + dataIndex: 'powerType', + key: 'powerType', + width: 180, + }, + { + title: '设备数量', + dataIndex: 'deviceCount', + key: 'deviceCount', + width: 120, + align: 'center', + }, + { + title: '统计方式', + dataIndex: 'statisticMethod', + key: 'statisticMethod', + width: 140, + }, + { + title: '用途描述', + dataIndex: 'usageDesc', + key: 'usageDesc', + ellipsis: true, + }, + { + title: '操作', + key: 'action', + width: 120, + align: 'center', + render: (_, record) => ( + + + ); + })} + + + + + +
+
+
+ + + + + + + +
+
+
筛选条件
+ + + + +
+
+ +
+
setSelectedRowKeys(keys), + columnWidth: 44, + }} + /> + + + + ); +}; + +export default Nhtjcs; diff --git a/src/pages/business_data/components/nhtjcs.less b/src/pages/business_data/components/nhtjcs.less new file mode 100644 index 0000000..373bb7a --- /dev/null +++ b/src/pages/business_data/components/nhtjcs.less @@ -0,0 +1,245 @@ +.container { + display: flex; + gap: 15px; + width: 100%; + height: 100%; + min-height: 560px; + padding: 15px 10px +} + +.leftPanel { + width: 200px; + background: #fff; + border-radius: 12px; + padding: 15px 10px; +} + +.leftTitle { + font-size: 15px; + font-weight: 500; + color: #2b2f3a; + padding: 2px 10px 14px; +} + +.leftList { + display: flex; + flex-direction: column; + gap: 12px; + padding: 0 8px; +} + +.leftItemBtn { + width: 100%; + height: 40px !important; + padding: 0 14px !important; + border-radius: 999px !important; + border: 1px solid #e8ecf3 !important; + background: #fff !important; + box-shadow: none !important; + display: flex !important; + align-items: center !important; + justify-content: flex-start !important; + color: #a5adbb !important; + + &:hover { + border-color: rgba(72, 81, 255, 0.35) !important; + color: rgba(72, 81, 255, 1) !important; + } +} + +.leftItemActive { + background: rgba(72, 81, 255, 1) !important; + border-color: rgba(72, 81, 255, 1) !important; + color: #fff !important; + box-shadow: 0 10px 20px rgba(72, 81, 255, 0.18) !important; + + &:hover { + color: #fff !important; + } + + .leftIconCircle { + background: rgba(255, 255, 255, 0.26); + color: #fff; + } +} + +.leftIconCircle { + width: 18px; + height: 18px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + background: rgba(72, 81, 255, 0.12); + color: rgba(72, 81, 255, 1); + flex: none; + + :global { + .anticon { + font-size: 12px; + line-height: 1; + } + } +} + +.leftIconImg { + width: 18px; + height: 18px; + display: block; + object-fit: contain; +} + +.leftItemText { + margin-left: 10px; + font-size: 14px; + font-weight: 500; +} + +.leftAddBtn { + width: 100%; + height: 40px !important; + border-radius: 999px !important; + border: none !important; + background: #f2f4f8 !important; + color: #b7bdc9 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + box-shadow: none !important; +} + +.rightPanel { + flex: 1; + background: #fff; + border-radius: 12px; + padding: 14px 16px 12px; + overflow: hidden; +} + +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 2px 0 14px; +} + +.toolbarLeft { + display: flex; + align-items: center; +} + +.toolbarRight { + display: flex; + align-items: center; +} + +.filterLabel { + font-size: 12px; + color: #8d93a3; + margin-right: 10px; +} + +.filterSelect { + width: 150px; + + :global { + .ant-select-selector { + height: 32px !important; + border-radius: 999px !important; + border-color: #e7eaf2 !important; + display: flex !important; + align-items: center !important; + box-shadow: none !important; + } + + .ant-select-selection-placeholder { + color: #b1b7c4; + } + } +} + +.primaryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + box-shadow: none !important; + background-color: rgba(72, 81, 255, 1); + + :global { + .ant-btn-icon { + margin-right: 6px; + } + } +} + +.ghostBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.queryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.tableWrap { + width: 100%; + + :global { + .ant-table { + border-radius: 10px; + } + + .ant-table-thead > tr > th { + background: #f7f8fb; + color: #2b2f3a; + // font-weight: 500; + } + + .ant-table-tbody > tr > td { + color: #2b2f3a; + } + + .ant-table-tbody > tr:hover > td { + background: #fafbff; + } + + .ant-table-pagination { + margin: 12px 0 0; + } + } +} + +.actionIconBtn { + padding: 0 !important; + height: 24px !important; + color: rgba(72, 81, 255, 1) !important; + + :global { + .anticon { + font-size: 16px; + } + } +} + +.actionIconBtnDanger { + padding: 0 !important; + height: 24px !important; + color: #ff4d4f !important; + + :global { + .anticon { + font-size: 16px; + } + } +} diff --git a/src/pages/business_data/components/nyfl.js b/src/pages/business_data/components/nyfl.js new file mode 100644 index 0000000..2d782af --- /dev/null +++ b/src/pages/business_data/components/nyfl.js @@ -0,0 +1,295 @@ +import React, { useMemo, useState } from 'react'; +import { Button, message, Popconfirm, Select, Space, Switch, Table } from 'antd'; +import { + DeleteOutlined, + DownloadOutlined, + EditOutlined, + PlusOutlined, + UploadOutlined, +} from '@ant-design/icons'; +import styles from './nyfl.less'; +import Component1 from '@/assets/basic_data/Component1.svg'; +import Component2 from '@/assets/basic_data/Component2.svg'; +import Component3 from '@/assets/basic_data/Component3.svg'; + +const Nyfl = () => { + const [activeEnergyKey, setActiveEnergyKey] = useState('electric'); + const [filterUnit, setFilterUnit] = useState(undefined); + const [filterCollection, setFilterCollection] = useState(undefined); + const [selectedRowKeys, setSelectedRowKeys] = useState([]); + + const energyTypes = useMemo( + () => [ + { key: 'electric', label: '电能', icon: Component1 }, + { key: 'water', label: '水', icon: Component2 }, + { key: 'gas', label: '天然气', icon: Component3 }, + ], + [], + ); + + const unitOptions = useMemo( + () => [ + { label: 'kWh', value: 'kWh' }, + { label: 'm³', value: 'm³' }, + { label: 't', value: 't' }, + ], + [], + ); + + const collectionOptions = useMemo( + () => [ + { label: '市电', value: '市电' }, + { label: '光伏发电', value: '光伏发电' }, + { label: '自备发电', value: '自备发电' }, + ], + [], + ); + + const [rows, setRows] = useState(() => [ + { + id: 1, + energyKey: 'electric', + energyType: '电能', + unit: 'kWh', + collectionType: '市电', + enabled: true, + latestUpdateTime: '2025-12-02 03:56:02', + }, + { + id: 2, + energyKey: 'electric', + energyType: '电能', + unit: 'kWh', + collectionType: '光伏发电', + enabled: false, + latestUpdateTime: '2025-11-22 11:56:50', + }, + { + id: 3, + energyKey: 'electric', + energyType: '电能', + unit: 'kWh', + collectionType: '自备发电', + enabled: true, + latestUpdateTime: '2025-12-04 21:12:20', + }, + { + id: 4, + energyKey: 'electric', + energyType: '电能', + unit: 'kWh', + collectionType: 'xxxxxx', + enabled: false, + latestUpdateTime: '2025-11-26 10:28:20', + }, + { + id: 5, + energyKey: 'water', + energyType: '水', + unit: 'm³', + collectionType: '市政供水', + enabled: true, + latestUpdateTime: '2025-12-06 09:12:10', + }, + { + id: 6, + energyKey: 'gas', + energyType: '天然气', + unit: 'm³', + collectionType: '管网供气', + enabled: true, + latestUpdateTime: '2025-12-01 16:08:45', + }, + ]); + + const tableData = useMemo(() => { + return rows + .filter((row) => row.energyKey === activeEnergyKey) + .filter((row) => (filterUnit ? row.unit === filterUnit : true)) + .filter((row) => (filterCollection ? row.collectionType === filterCollection : true)); + }, [activeEnergyKey, filterCollection, filterUnit, rows]); + + const handleAdd = () => message.info('新增'); + const handleUpload = () => message.info('上传'); + const handleBatchDownload = () => message.info('批量下载'); + const handleQuery = () => message.info('查询'); + + const handleEdit = (record) => message.info(`编辑:${record.energyType}`); + const handleDelete = (record) => message.success(`已删除:${record.energyType}`); + + const columns = useMemo( + () => [ + { + title: '序号', + dataIndex: 'id', + key: 'id', + width: 70, + align: 'center', + }, + { + title: '能源类型', + dataIndex: 'energyType', + key: 'energyType', + width: 120, + }, + { + title: '单位', + dataIndex: 'unit', + key: 'unit', + width: 120, + }, + { + title: '采集类型', + dataIndex: 'collectionType', + key: 'collectionType', + width: 180, + }, + { + title: '状态', + dataIndex: 'enabled', + key: 'enabled', + width: 120, + align: 'center', + render: (value, record) => ( + { + setRows((prev) => + prev.map((item) => (item.id === record.id ? { ...item, enabled: checked } : item)), + ); + message.success(checked ? '已启用' : '已停用'); + }} + /> + ), + }, + { + title: '最后更新时间', + dataIndex: 'latestUpdateTime', + key: 'latestUpdateTime', + width: 200, + }, + { + title: '操作', + key: 'action', + width: 120, + align: 'center', + render: (_, record) => ( + + + ); + })} + + + + + +
+
+
+ + + + + +
+ +
+
筛选条件
+ + + + +
+
+ +
+
setSelectedRowKeys(keys), + columnWidth: 44, + }} + /> + + + + ); +}; + +export default Nyfl; diff --git a/src/pages/business_data/components/nyfl.less b/src/pages/business_data/components/nyfl.less new file mode 100644 index 0000000..7dca2b9 --- /dev/null +++ b/src/pages/business_data/components/nyfl.less @@ -0,0 +1,245 @@ +.container { + display: flex; + gap: 15px; + width: 100%; + height: 100%; + min-height: 560px; + padding: 15px 10px +} + +.leftPanel { + width: 200px; + background: #fff; + border-radius: 12px; + padding: 15px 10px; +} + +.leftTitle { + font-size: 15px; + font-weight: 500; + color: #2b2f3a; + padding: 2px 10px 14px; +} + +.leftList { + display: flex; + flex-direction: column; + gap: 12px; + padding: 0 8px; +} + +.leftItemBtn { + width: 100%; + height: 40px !important; + padding: 0 14px !important; + border-radius: 999px !important; + border: 1px solid #e8ecf3 !important; + background: #fff !important; + box-shadow: none !important; + display: flex !important; + align-items: center !important; + justify-content: flex-start !important; + color: #a5adbb !important; + + &:hover { + border-color: rgba(72, 81, 255, 0.35) !important; + color: rgba(72, 81, 255, 1) !important; + } +} + +.leftItemActive { + background: rgba(72, 81, 255, 1) !important; + border-color: rgba(72, 81, 255, 1) !important; + color: #fff !important; + box-shadow: 0 10px 20px rgba(72, 81, 255, 0.18) !important; + + &:hover { + color: #fff !important; + } + + .leftIconCircle { + background: rgba(255, 255, 255, 0.26); + color: #fff; + } +} + +.leftIconCircle { + width: 18px; + height: 18px; + border-radius: 999px; + display: inline-flex; + align-items: center; + justify-content: center; + background: rgba(72, 81, 255, 0.12); + color: rgba(72, 81, 255, 1); + flex: none; + + :global { + .anticon { + font-size: 12px; + line-height: 1; + } + } +} + +.leftIconImg { + width: 18px; + height: 18px; + display: block; + object-fit: contain; +} + +.leftItemText { + margin-left: 10px; + font-size: 14px; + font-weight: 500; +} + +.leftAddBtn { + width: 100%; + height: 40px !important; + border-radius: 999px !important; + border: none !important; + background: #f2f4f8 !important; + color: #b7bdc9 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + box-shadow: none !important; +} + +.rightPanel { + flex: 1; + background: #fff; + border-radius: 12px; + padding: 14px 16px 12px; + overflow: hidden; +} + +.toolbar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 2px 0 14px; +} + +.toolbarLeft { + display: flex; + align-items: center; +} + +.toolbarRight { + display: flex; + align-items: center; +} + +.filterLabel { + font-size: 12px; + color: #8d93a3; + margin-right: 10px; +} + +.filterSelect { + width: 150px; + + :global { + .ant-select-selector { + height: 32px !important; + border-radius: 999px !important; + border-color: #e7eaf2 !important; + display: flex !important; + align-items: center !important; + box-shadow: none !important; + } + + .ant-select-selection-placeholder { + color: #b1b7c4; + } + } +} + +.primaryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + box-shadow: none !important; + background-color: rgba(72, 81, 255, 1); + + :global { + .ant-btn-icon { + margin-right: 6px; + } + } +} + +.ghostBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.queryBtn { + height: 32px !important; + border-radius: 999px !important; + padding: 0 16px !important; + border: 1px solid #e7eaf2 !important; + background: #fff !important; + color: #5b6070 !important; + box-shadow: none !important; +} + +.tableWrap { + width: 100%; + + :global { + .ant-table { + border-radius: 10px; + } + + .ant-table-thead > tr > th { + background: #f7f8fb; + color: #6d7383; + font-weight: 500; + } + + .ant-table-tbody > tr > td { + color: #2b2f3a; + } + + .ant-table-tbody > tr:hover > td { + background: #fafbff; + } + + .ant-table-pagination { + margin: 12px 0 0; + } + } +} + +.actionIconBtn { + padding: 0 !important; + height: 24px !important; + color: rgba(72, 81, 255, 1) !important; + + :global { + .anticon { + font-size: 16px; + } + } +} + +.actionIconBtnDanger { + padding: 0 !important; + height: 24px !important; + color: #ff4d4f !important; + + :global { + .anticon { + font-size: 16px; + } + } +} diff --git a/src/pages/business_data/form/StaffSheetCreateForm.js b/src/pages/business_data/form/StaffSheetCreateForm.js new file mode 100644 index 0000000..6bdce8d --- /dev/null +++ b/src/pages/business_data/form/StaffSheetCreateForm.js @@ -0,0 +1,271 @@ +import { useState, useEffect } from 'react' +import { Col, DatePicker, Form, Input, Modal, Row, Select } from 'antd' +import SelectDeptTree from '@/components/SelectDeptTree' +import SelectOrganTree from '@/components/SelectOrganTree' +import datadictionary from '@/utils/dataDictionary' +import { formatDictOptions, verifyPhone } from '@/utils/globalCommon' +import { NumberInput } from '@/components/NumberInput' +import styles from '../StaffSheetList.less' +import style from '@/global.less' +import dayjs from 'dayjs' +import { formatDate } from '@/utils/formatUtils' + +const { Item: FormItem } = Form +const { TextArea } = Input +const dictData = datadictionary + +//新增表单 +let getDeptTreeBySelectTree +let getOrganTreeBySelectTree + +const StaffSheetCreateForm = (props => { + const [form] = Form.useForm() + const [jobStatus, setJobStatus] = useState('1') + + const { + modalVisible, + handleAdd, + handleModalVisible, + loading, + dispatch, + selectDeptTree, + selectOrganTree + } = props + + useEffect(() => { + form.setFieldsValue({ + user_type: 'employee', + job_status: '1', + mgr_type: '0' + }) + }, []) + + const selectedDeptTreeValue = (deptRecord) => { + getDeptTreeBySelectTree = deptRecord + } + + const selectedOrganTreeValue = (orgRecord) => { + getOrganTreeBySelectTree = orgRecord + } + + const parentDeptTreeMethod = { + dispatch: dispatch, + selectDeptTree: selectDeptTree, + selectedDeptTreeValue: selectedDeptTreeValue, + } + + const parentOrganTreeMethod = { + dispatch: dispatch, + selectOrganTree: selectOrganTree, + selectedOrganTreeValue: selectedOrganTreeValue + } + + const okHandle = () => { + form.validateFields() + .then(fieldsValue => { + form.resetFields() + fieldsValue.birthday = formatDate(fieldsValue.birthday, 'YYYY-MM-DD') + fieldsValue.hiredate = formatDate(fieldsValue.hiredate, 'YYYY-MM-DD') + fieldsValue.departure_time = formatDate(fieldsValue.departure_time, 'YYYY-MM-DD') + fieldsValue.posts = fieldsValue.posts ? JSON.stringify(fieldsValue.posts) : null + + // if (getDeptTreeBySelectTree) { + // fieldsValue.dept_code = getDeptTreeBySelectTree.dept_code + // fieldsValue.dept_name = getDeptTreeBySelectTree.title + // } + + if (getOrganTreeBySelectTree) { + fieldsValue.org_code = getOrganTreeBySelectTree.org_code + fieldsValue.org_name = getOrganTreeBySelectTree.title + } + + handleAdd(fieldsValue) + }) + .catch(errInfo => {}) + } + + const afterClose = () =>{ + form.resetFields(); + } + + const handleJobStatusChange = (value) => { + setJobStatus(value) + } + + return ( + handleModalVisible()} + afterClose={() => afterClose()} + confirmLoading={loading} + > +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + {/**/} + {/* */} + {/* */} + {/* */} + {/**/} + + + + + + */} + + + + + + + + + + + + + + + + + + + + + + + +