Resolve merge conflict and keep atmosphere pollutant library and env monitoring menu items

main
wangyunfei 2 weeks ago
commit d45da29ee4

@ -36,6 +36,12 @@ export default [
name: 'atmospherePollutantLibrary',
component: './business_envInformation/components/AtmospherePollutantLibrary',
},
// 环保监测管理
{
path: '/topnavbar00/business/envmonitoring',
name: 'envInformation',
component: './business_envmonitoring/EnvMonitoring',
},
// 安全管理基础信息
{
path: '/topnavbar00/business/basicinformation',

@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.04453 3.0682C8.33094 3.0682 8.56312 2.83602 8.56312 2.54961V1.51273C8.56312 1.22633 8.33094 0.994141 8.04453 0.994141C7.75813 0.994141 7.52594 1.22633 7.52594 1.51273V2.54977C7.52609 2.83617 7.75813 3.0682 8.04453 3.0682ZM4.88203 3.57086C5.02516 3.81883 5.34234 3.90383 5.59031 3.7607C5.83828 3.61758 5.92328 3.30039 5.78016 3.05242L5.26156 2.1543C5.11844 1.90633 4.80125 1.82133 4.55328 1.96445C4.30531 2.10758 4.22031 2.42477 4.36344 2.67273L4.88203 3.57086ZM2.20453 5.25867L3.10266 5.77727C3.35063 5.92039 3.66781 5.83555 3.81094 5.58742C3.95406 5.33945 3.86922 5.02227 3.62109 4.87914L2.72297 4.36055C2.475 4.21742 2.15781 4.30227 2.01469 4.55039C1.87156 4.79836 1.95656 5.11539 2.20453 5.25867ZM3.18344 8.05898C3.18344 7.77258 2.95125 7.54039 2.66484 7.54039H1.62797C1.34156 7.54039 1.10938 7.77258 1.10938 8.05898C1.10938 8.34539 1.34156 8.57758 1.62797 8.57758H2.665C2.95125 8.57758 3.18344 8.34539 3.18344 8.05898ZM13.0513 5.66492L13.9494 5.14633C14.1973 5.0032 14.2823 4.68602 14.1392 4.43805C13.9961 4.19008 13.6789 4.10508 13.4309 4.2482L12.5328 4.7668C12.2848 4.90992 12.1998 5.22711 12.343 5.47508C12.4861 5.72305 12.8033 5.80805 13.0513 5.66492ZM10.5162 3.69586C10.7642 3.83898 11.0814 3.75414 11.2245 3.50602L11.7431 2.60789C11.8862 2.35992 11.8014 2.04273 11.5533 1.89961C11.3053 1.75648 10.9881 1.84133 10.845 2.08945L10.3264 2.98758C10.1833 3.23555 10.2681 3.55258 10.5162 3.69586ZM10.8281 11.4888C10.8281 11.696 10.9104 11.8947 11.0569 12.0413C11.2035 12.1878 11.4022 12.2701 11.6094 12.2701C11.8166 12.2701 12.0153 12.1878 12.1618 12.0413C12.3083 11.8947 12.3906 11.696 12.3906 11.4888C12.3906 11.2816 12.3083 11.0829 12.1618 10.9364C12.0153 10.7899 11.8166 10.7076 11.6094 10.7076C11.4022 10.7076 11.2035 10.7899 11.0569 10.9364C10.9104 11.0829 10.8281 11.2816 10.8281 11.4888ZM11.6094 7.98883C9.67641 7.98883 8.10938 9.55586 8.10938 11.4888C8.10938 13.4218 9.67641 14.9888 11.6094 14.9888C13.5423 14.9888 15.1094 13.4218 15.1094 11.4888C15.1094 9.55586 13.5423 7.98883 11.6094 7.98883ZM11.6094 12.8013C10.2286 12.8013 9.10938 11.4888 9.10938 11.4888C9.10938 11.4888 10.2286 10.1763 11.6094 10.1763C12.9902 10.1763 14.1094 11.4888 14.1094 11.4888C14.1094 11.4888 12.9902 12.8013 11.6094 12.8013ZM7.45922 12.542C7.42891 12.3815 7.38125 12.0577 7.36891 11.8949C7.30984 11.1123 7.44781 10.6059 7.60969 10.1488L7.63953 9.93305H6.42422L8.85469 6.3882L8.63375 8.52336C9.42609 7.72758 10.5227 7.23492 11.7344 7.23492C11.8472 7.23492 11.9589 7.23914 12.0697 7.24758C11.62 5.44273 9.98859 4.10523 8.04453 4.10523C5.75359 4.10523 3.89641 5.96242 3.89641 8.25336V13.9385H2.34078C2.05437 13.9385 1.82219 14.1707 1.82219 14.4571C1.82219 14.7435 2.05437 14.9757 2.34078 14.9757H8.93937C8.20219 14.3629 7.64359 13.5177 7.45922 12.542Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

@ -0,0 +1,63 @@
import React, { useState } from 'react';
import { Card, Row, Col, Statistic, Progress, Button, Space } from 'antd';
import styles from './EnvMonitoring.less';
import EnvMonitoringOneMap from './components/EnvMonitoringOneMap'; //环保监测一张图
import WastewaterMonitoring from './components/WastewaterMonitoring'; //废水监测管理
import ExhaustMonitoring from './components/ExhaustMonitoring'; //废气监测管理
import WasteMonitoring from './components/WasteMonitoring'; //废物监测管理
const EnvMonitoring = () => {
const [activeModule, setActiveModule] = useState('1');
const handleModuleClick = (module) => {
setActiveModule(module)
}
const renderModule = () => {
switch (activeModule) {
case '1':
return <EnvMonitoringOneMap />;
case '2':
return <WastewaterMonitoring />;
case '3':
return <ExhaustMonitoring />;
case '4':
return <WasteMonitoring />;
default:
return <EnvMonitoringOneMap />;
}
};
return (
<div className={styles.container}>
<div className={styles.TopButton}>
<Button
className={`${styles.TopButtonItem} ${activeModule === "1" ? styles.active : ""}`}
onClick={() => handleModuleClick("1")}
>环保检测一张图
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "2" ? styles.active : ""}`}
onClick={() => handleModuleClick("2")}
>废水监测管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "3" ? styles.active : ""}`}
onClick={() => handleModuleClick("3")}
>废气监测管理
</Button>
<Button
className={`${styles.TopButtonItem} ${activeModule === "4" ? styles.active : ""}`}
onClick={() => handleModuleClick("4")}
>废物监测管理
</Button>
</div>
<div className={styles.content}>
{renderModule()}
</div>
</div>
);
};
export default EnvMonitoring;

@ -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: 8px 15px 5px;
display: flex;
gap: 24px;
// margin-left: 6px;
.TopButtonItem {
background-color: #FFFFFF !important;
color: #999999 !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: 4px 16px !important;
height: auto !important;
border: none !important;
box-shadow: none !important;
position: relative !important;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
gap: 5px !important;
transition: all 0.3s ease !important;
&:hover {
color: #999999 !important;
border: none !important;
}
&:focus {
color: #999999 !important;
border: none !important;
}
&.active {
background: linear-gradient(98.03deg, #00E49C 0.68%, #00D2D2 98.3%) !important;
box-shadow: 0px 2px 2px 0px #AEEDDE !important;
color: #FFFFFF !important;
&::after {
content: '';
width: 28px;
height: 5px;
background-color: #FFFFFF;
border-radius: 6px;
opacity: 1;
}
}
}
}
.content {
// ======== 内容区域样式 ========
flex: 1; // ======== 占据剩余空间 ========
overflow-y: auto; // ======== 允许垂直滚动 ========
padding: 0; // ======== 无内边距 ========
}
}

@ -0,0 +1,13 @@
import React from 'react';
import styles from './EnvMonitoringOneMap.less';
const EnvMonitoringOneMap = () => {
return (
<div className={styles.container}>
开发中
</div>
);
};
export default EnvMonitoringOneMap;

@ -0,0 +1,41 @@
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
border-radius: 4px;
padding: 20px;
.developingBox {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 80px;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(240, 147, 251, 0.3);
.developingText {
font-size: 32px;
font-weight: 600;
color: #ffffff;
letter-spacing: 4px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
animation: pulse 2s ease-in-out infinite;
}
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(1.05);
}
}

@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { Tabs, Button } from 'antd';
import styles from './ExhaustMonitoring.less';
import { SettingOutlined } from '@ant-design/icons';
// 导入子页面组件
import ExhaustEmissionStandard from './secondary_menu/ExhaustEmissionStandard';
import AlarmInformation from './secondary_menu/AlarmInformation';
import AbnormalAlarmInformation from './secondary_menu/AbnormalAlarmInformation';
const ExhaustMonitoring = () => {
const [activeTab, setActiveTab] = useState('exhaust-emission-standard');
// 标签页配置
const tabItems = [
{
key: 'exhaust-emission-standard',
label: '废气排放达标情况管理',
children: <ExhaustEmissionStandard />
},
{
key: 'abnormal-alarm-information',
label: '异常报警信息管理',
children: <AbnormalAlarmInformation />
},
{
key: 'alarm-information',
label: '告警信息管理',
children: <AlarmInformation />
}
];
// 标签页切换处理
const handleTabChange = (key) => {
setActiveTab(key);
};
return (
<div className={styles.container}>
<Tabs
activeKey={activeTab}
onChange={handleTabChange}
items={tabItems}
className={styles.tabs}
style={{
'--ant-tabs-tab-color': '#AFAFAF',
'--ant-tabs-tab-active-color': '#009D6F',
'--ant-tabs-tab-active-bg': '#fff'
}}
tabBarStyle={{
'--ant-tabs-tab-active-color': '#009D6F'
}}
tabBarExtraContent={
<Button
icon={<SettingOutlined />}
className={styles.settingButton}
onClick={() => {/* 你的操作 */ }}>
自动报警设置
</Button>
}
/>
</div>
);
};
export default ExhaustMonitoring;

@ -0,0 +1,132 @@
.container {
width: 100%;
height: 100%;
padding: 10px 0;
// background-color: #f5f5f5;
.tabs {
height: 100%;
background-color: transparent;
border-radius: 8px;
// box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
:global(.ant-tabs-nav) {
margin: 0;
padding: 0 0px;
background-color: transparent;
border-bottom: none;
}
:global(.ant-tabs-tab) {
padding: 16px 24px !important;
font-size: 14px !important;
font-weight: 400 !important;
color: #AFAFAF !important;
background: transparent !important;
border: none !important;
position: relative;
&:hover {
color: #AFAFAF !important;
background-color: transparent !important;
}
&.ant-tabs-tab-active {
color: #009D6F !important;
background-color: #fff !important;
border-bottom: none !important;
font-weight: 700;
}
}
// 额外的全局样式覆盖,确保优先级足够高
:global(.ant-tabs-tab.ant-tabs-tab-active) {
color: #009D6F !important;
background-color: #fff !important;
border-bottom: none !important;
}
// 更强的选择器优先级
:global(.ant-tabs .ant-tabs-tab.ant-tabs-tab-active) {
color: #009D6F !important;
background-color: #fff !important;
border-bottom: none !important;
}
// 针对可能的嵌套结构
:global(.ant-tabs-nav .ant-tabs-tab.ant-tabs-tab-active) {
color: #009D6F !important;
background-color: #fff !important;
border-bottom: none !important;
}
// // 覆盖 Ant Design 5.x 的高优先级样式
// :global(.ant-tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
// color: #009D6F !important;
// text-shadow: none !important;
// }
// 使用更高优先级的选择器
:global(.ant-tabs.ant-tabs-top .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
color: #009D6F !important;
text-shadow: none !important;
}
// 针对 CSS 模块的覆盖
:global(.ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
color: #009D6F !important;
text-shadow: none !important;
}
// 最高优先级覆盖
:global(.ant-tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
color: #009D6F !important;
text-shadow: none !important;
}
// 覆盖Ant Design的默认下划线
:global(.ant-tabs-ink-bar) {
background: none !important;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%) rotate(180deg);
width: 38.36132812500014px;
height: 3.3613271713256965px;
background-color: #009D6F;
opacity: 1;
border-radius: 2px;
z-index: 1;
}
}
:global(.ant-tabs-content-holder) {
height: calc(100% - 60px);
// padding: 20px;
.ant-tabs-content {
height: 100%;
.ant-tabs-tabpane {
height: 100%;
}
}
}
}
.settingButton {
border-color: #d9d9d9;
color: #333;
height: 32px;
border-radius: 4px;
&:hover {
border-color: #40a9ff;
color: #40a9ff;
}
}
}

@ -0,0 +1,13 @@
import React from 'react';
import styles from './WasteMonitoring.less';
const WasteMonitoring = () => {
return (
<div className={styles.container}>
开发中
</div>
);
};
export default WasteMonitoring;

@ -0,0 +1,41 @@
.container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
border-radius: 4px;
padding: 20px;
.developingBox {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 60px 80px;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(240, 147, 251, 0.3);
.developingText {
font-size: 32px;
font-weight: 600;
color: #ffffff;
letter-spacing: 4px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
animation: pulse 2s ease-in-out infinite;
}
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.8;
transform: scale(1.05);
}
}

@ -0,0 +1,349 @@
import React, { useEffect, useRef, useState } from 'react';
import { Card, Select, Button, Table, Tag, Input, Form, Row, Col, Pagination, Space } from 'antd';
import { EyeOutlined, SettingOutlined, PlusOutlined } from '@ant-design/icons';
import * as echarts from 'echarts';
import styles from './WastewaterMonitoring.less';
import viewIcon from '@/assets/business_envinformation/viewicon.svg';
const pollutantOptions = [
{ value: '全部', label: '全部污染物' },
{ value: 'COD', label: 'COD' },
{ value: '氨氮', label: '氨氮' },
];
const monitorPointOptions = [
{ value: '全部', label: '全部监测点' },
{ value: '点1', label: '点1' },
{ value: '点2', label: '点2' },
];
const dateOptions = [
{ value: '本月', label: '本月' },
{ value: '本周', label: '本周' },
{ value: '今日', label: '今日' },
];
const statData = [
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '超标' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '报警' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '正常' },
{ w: 'COD', s: '山顶点', v: '490mg/L', d: '2025-09-11', st: '超标' },
];
const alarmTableColumns = [
{ title: '序号', dataIndex: 'index', width: 60, render: (_, __, i) => i + 1 },
{ title: '报警时间', dataIndex: 'time' },
{ title: '监测站点', dataIndex: 'point' },
{ title: '污染物名称', dataIndex: 'name' },
{ title: '超标情况(%)', dataIndex: 'percent' },
{ title: '负责人', dataIndex: 'person' },
{ title: '报警短信', dataIndex: 'sms', render: v => v === '已发送' ? <span style={{ background: '#FAFAFA', color: '#333333', border: '1px solid #D9D9D9', borderRadius: 4, padding: '2px 12px', fontSize: 13 }}>已发送</span> : <span style={{ background: '#FFEAE9', color: '#FF0000', border: '1px solid #FFBEBE', borderRadius: 4, padding: '2px 12px', fontSize: 13 }}></span> },
{ title: '处理情况', dataIndex: 'status', render: v => v === '已处理' ? <span style={{ background: '#F6FFED', color: '#52C41A', border: '1px solid #B7EB8F', borderRadius: 4, padding: '2px 12px', fontSize: 13 }}>已处理</span> : v === '' ? <span style={{ background: '#FFF2E8', color: '#FA541C', border: '1px solid #FFBB96', borderRadius: 4, padding: '2px 12px', fontSize: 13 }}></span> : <span style={{ background: '#FFFBE6', color: '#FAAD14', border: '1px solid #FFE58F', borderRadius: 4, padding: '2px 12px', fontSize: 13 }}></span> },
{
title: '操作', dataIndex: 'action', align: 'center', render: (_, record) => (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' }}>
<img
src={viewIcon}
alt="查看"
style={{ width: 16, height: 16, cursor: 'pointer' }}
/>
</div>
),
},
];
const alarmTableData = [
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'hto', percent: 26, person: '负责人', sms: '已发送', status: '已处理' },
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'dfadf', percent: 58, person: '负责人', sms: '未发送', status: '未处理' },
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'dfadf', percent: 52, person: '王华俊', sms: '已发送', status: '已处理' },
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'dfadf', percent: 43, person: '赵荣堂', sms: '已发送', status: '已处理' },
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'dfadf', percent: 43, person: '赵荣堂', sms: '已发送', status: '已处理' },
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'dfadf', percent: 43, person: '赵荣堂', sms: '已发送', status: '已处理' },
{ time: '2025-08-27 07:12', point: 'xxxxx', name: 'dfadf', percent: 43, person: '赵荣堂', sms: '已发送', status: '已处理' },
];
const OnlineMonitoring = () => {
const chartRef = useRef(null);
const [monitorPoint, setMonitorPoint] = useState('全部');
const [pollutant, setPollutant] = useState('全部');
const [dateRange, setDateRange] = useState('本月');
const [pagination, setPagination] = useState({
current: 1,
pageSize: 5,
total: 0,
});
const [alarmPage, setAlarmPage] = useState(1);
const ALARM_PAGE_SIZE = 7;
const ALARM_TOTAL = 85; // 真实总数
useEffect(() => {
if (chartRef.current) {
const chart = echarts.init(chartRef.current);
chart.setOption({
grid: { left: 40, right: 20, top: 20, bottom: 40 },
xAxis: {
type: 'category',
data: ['05-01', '05-05', '05-09', '05-13', '05-17', '05-21', '05-25', '05-29', '05-31'],
boundaryGap: false,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { color: '#A3ACB7', fontSize: 13, margin: 16 },
splitLine: { show: false }, // 竖线不显示
},
yAxis: {
type: 'value',
min: 0,
max: 900,
splitNumber: 9,
axisLine: { show: false },
axisTick: { show: false },
axisLabel: { color: '#A3ACB7', fontSize: 13, margin: 16 },
splitLine: { show: true, lineStyle: { color: '#E9F0F5', type: 'dashed' } }, // 横线为虚线
},
series: [{
data: [400, 500, 700, 800, 600, 700, 500, 400, 450],
type: 'line',
smooth: true,
symbol: 'none',
lineStyle: { color: '#00C48F', width: 2 },
areaStyle: {
color: {
type: 'linear',
x: 0, y: 0, x2: 0, y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(0,196,143,0.15)' },
{ offset: 1, color: 'rgba(0,196,143,0.00)' }
]
}
},
}],
tooltip: {
trigger: 'axis',
backgroundColor: '#fff',
borderColor: '#E9F0F5',
borderWidth: 1,
textStyle: { color: '#222', fontSize: 13 },
axisPointer: { type: 'line', lineStyle: { color: '#00C48F', width: 1 } },
},
markLine: {
symbol: 'none',
data: [
{
yAxis: 700,
lineStyle: { color: '#FF8A00', type: 'dashed', width: 2 },
label: {
show: true,
position: 'start',
color: '#fff',
backgroundColor: '#FF8A00',
borderRadius: 4,
padding: [2, 8],
fontSize: 13,
fontWeight: 500,
formatter: '700',
distance: 10
}
},
{
yAxis: 700,
lineStyle: { color: 'transparent' },
label: {
show: true,
position: [0, 18],
color: '#FF8A00',
backgroundColor: 'transparent',
fontSize: 12,
formatter: '严重超标',
distance: 0
}
},
{
yAxis: 500,
lineStyle: { color: '#FFB800', type: 'dashed', width: 2 },
label: {
show: true,
position: 'start',
color: '#fff',
backgroundColor: '#FFB800',
borderRadius: 4,
padding: [2, 8],
fontSize: 13,
fontWeight: 500,
formatter: '500',
distance: 10
}
},
{
yAxis: 500,
lineStyle: { color: 'transparent' },
label: {
show: true,
position: [0, 18],
color: '#FFB800',
backgroundColor: 'transparent',
fontSize: 12,
formatter: '报警线',
distance: 0
}
}
]
},
});
}
}, [monitorPoint, pollutant, dateRange]);
return (
<div className={styles.Ocontainer}>
<div className={styles.pageMainRow}>
{/* 左侧主区 */}
<div className={styles.pageMainColLeft}>
<div className={styles.trendCard}>
<div className={styles.trendHeader}>
<div className={styles.trendTitle}>
<div className={styles.trendTitleBar}></div>
污染物排放趋势
</div>
<div className={styles.trendSelects}>
<Select value={monitorPoint} onChange={setMonitorPoint} options={monitorPointOptions} className={styles.trendSelect} />
<Select value={pollutant} onChange={setPollutant} options={pollutantOptions} className={styles.trendSelect} />
<Select value={dateRange} onChange={setDateRange} options={dateOptions} className={styles.trendSelect} />
</div>
</div>
<div ref={chartRef} className={styles.trendChart} />
</div>
<div className={styles.alarmTableCard}>
<div className={styles.alarmTableHeader}>
<div className={styles.alarmTableTitle}>
<div className={styles.alarmTableTitleBar}></div>
报警信息
</div>
<div className={styles.alarmTableSelects}>
<Select defaultValue="全部" options={monitorPointOptions} className={styles.alarmTableSelect} dropdownStyle={{ minWidth: 100 }} />
<Select defaultValue="全部" options={pollutantOptions} className={styles.alarmTableSelect} dropdownStyle={{ minWidth: 100 }} />
<Select defaultValue="本月" options={dateOptions} className={styles.alarmTableSelect} dropdownStyle={{ minWidth: 100 }} />
<Button className={styles.alarmTableAddBtn}><PlusOutlined /></Button>
</div>
</div>
<div className={styles.alarmTableWrap}>
<Table
columns={alarmTableColumns}
dataSource={alarmTableData.slice((alarmPage - 1) * ALARM_PAGE_SIZE, alarmPage * ALARM_PAGE_SIZE)}
size="small"
pagination={{
pageSize: ALARM_PAGE_SIZE,
total: ALARM_TOTAL,
current: alarmPage,
showTotal: (total) => `${total}`,
onChange: (page) => setAlarmPage(page),
showSizeChanger: false,
showQuickJumper: true,
position: ['bottomRight'],
}}
rowKey={(_, i) => i + (alarmPage - 1) * ALARM_PAGE_SIZE}
scroll={{ x: 'max-content' }}
bordered={false}
className={styles.alarmTable}
tableLayout="fixed"
/>
</div>
</div>
</div>
<div style={{ width: 420, display: 'flex', flexDirection: 'column', gap: 10 }}>
<div className={styles.statCard}>
<div className={styles.statHeader}>
<div className={styles.trendTitle}>
<div className={styles.trendTitleBar}></div>
污染物排放统计
</div>
<Button className={styles.statAddBtn}>手动添加</Button>
</div>
<div className={styles.statTableWrap}>
<Table
className={styles.statTable}
columns={[
{
title: '污染物',
dataIndex: 'w',
width: 100,
},
{ title: '站点', dataIndex: 's', width: 120 },
{ title: '排放量', dataIndex: 'v', width: 120 },
{ title: '日期', dataIndex: 'd', width: 140 },
{
title: '状态',
dataIndex: 'st',
width: 120,
},
]}
dataSource={statData.slice((alarmPage - 1) * ALARM_PAGE_SIZE, alarmPage * ALARM_PAGE_SIZE)}
size="small"
pagination={{
pageSize: 10,
total: 10,
current: 1,
showTotal: () => `共 10 条`,
onChange: (page) => setAlarmPage(page),
showSizeChanger: false,
showQuickJumper: true,
position: ['bottomRight'],
}}
rowKey={(r, i) => i + (alarmPage - 1) * ALARM_PAGE_SIZE}
scroll={{ x: 'max-content' }}
bordered={false}
tableLayout="fixed"
rowClassName={(_, idx) => idx % 2 === 0 ? '' : 'table-row-alt'}
onRow={() => ({ style: { height: 20 } })}
components={{
body: {
wrapper: (props) => {
// 过滤掉第一个tr
const { children, ...rest } = props;
return (
<tbody {...rest}>
{React.Children.toArray(children).slice(1)}
</tbody>
);
}
}
}}
/>
</div>
</div>
{/* 短信区块 */}
<div style={{ background: '#fff', boxShadow: 'none', padding: 0 }}>
<div className={styles.smsCard}>
<div className={styles.smsHeader}>
<div className={styles.trendTitle}>
<div className={styles.trendTitleBar}></div>
报警短信
</div>
<div className={styles.smsHeaderBtns}>
<Button className={styles.smsTplBtn}>设置模板</Button>
<Button type="primary" className={styles.smsSendBtn}>发送</Button>
</div>
</div>
<Form layout="vertical" className={styles.smsForm}>
<div className={styles.smsFormRow} style={{ display: 'flex', alignItems: 'center', marginTop: 20 }}>
<span className={styles.smsFormLabel} style={{ minWidth: 70 }}> </span>
<Select className={styles.smsFormInput} value="xxx" options={[{ value: 'xxx', label: 'xxx' }]} style={{ width: 320 }} />
</div>
<div className={styles.smsFormRow} style={{ display: 'flex', alignItems: 'center', marginTop: 20 }}>
<span className={styles.smsFormLabel} style={{ minWidth: 70 }}>手机号码</span>
<Input className={styles.smsFormInput} placeholder="请输入" style={{ width: 320 }} />
</div>
<div className={styles.smsFormRow} style={{ display: 'flex', alignItems: 'flex-start', marginTop: 20 }}>
<span className={styles.smsFormLabel} style={{ minWidth: 70, marginTop: 8 }}>短信内容</span>
<div style={{ width: 320 }}>
<Input.TextArea className={styles.smsFormTextarea} value="xxx" rows={5} style={{ resize: 'none' }} />
</div>
</div>
</Form>
</div>
</div>
</div>
</div>
</div>
);
};
export default OnlineMonitoring;

@ -0,0 +1,196 @@
import React, { useState } from 'react';
import { Button, Input, Select, Switch } from 'antd';
import { SearchOutlined, RedoOutlined, UploadOutlined, DownloadOutlined, DeleteOutlined } from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './AbnormalAlarmInformation.less';
const AbnormalAlarmInformation = () => {
const [activeTab, setActiveTab] = useState('actual');
// 表格数据与图片一致
const [tableData, setTableData] = useState([
{ key: 1, siteName: '2025-08-29', monitorFactor: '赵若行', alarmCount: '郑祥飞', autoHandle: false },
{ key: 2, siteName: '2025-09-02', monitorFactor: '王翠琪', alarmCount: '赵广峰', autoHandle: true },
{ key: 3, siteName: '2025-09-06', monitorFactor: '王翠琪', alarmCount: '郑嘉亨', autoHandle: true },
{ key: 4, siteName: '2025-08-22', monitorFactor: '王锦瑞', alarmCount: '赵玉', autoHandle: true },
{ key: 5, siteName: '2025-08-31', monitorFactor: '吴子鑫', alarmCount: '钱如珩', autoHandle: true },
{ key: 6, siteName: '2025-08-29', monitorFactor: '何凤彬', alarmCount: '赵润琪', autoHandle: false },
{ key: 7, siteName: '2025-08-28', monitorFactor: '李树烈', alarmCount: '李建刚', autoHandle: false },
{ key: 8, siteName: '2025-08-22', monitorFactor: '赵午光', alarmCount: '钱瑞西', autoHandle: false },
{ key: 9, siteName: '2025-08-21', monitorFactor: '王凤妍', alarmCount: '李金涛', autoHandle: false },
{ key: 10, siteName: '2025-09-09', monitorFactor: '赵涛', alarmCount: '何倩', autoHandle: false },
]);
// 表格列定义
const handleSwitchChange = (checked, record) => {
setTableData(prevData => prevData.map(item =>
item.key === record.key ? { ...item, autoHandle: checked } : item
));
};
const normalColumns = [
{
title: '序号',
dataIndex: 'key',
key: 'key',
align: 'center',
width: 60,
},
{
title: '站点名称',
dataIndex: 'siteName',
key: 'siteName',
align: 'left',
},
{
title: '监测因子',
dataIndex: 'monitorFactor',
key: 'monitorFactor',
align: 'left',
},
{
title: '报警次数',
dataIndex: 'alarmCount',
key: 'alarmCount',
align: 'left',
},
{
title: '异常自动报警',
dataIndex: 'autoHandle',
key: 'autoHandle',
align: 'left',
render: (text, record) => (
<Switch checked={!!text} size="small" onChange={checked => handleSwitchChange(checked, record)} />
),
},
{
title: '详情',
key: 'detail',
align: 'left',
render: () => <a href="#">查看</a>,
},
];
// 获取当前标签页的数据
const getCurrentData = () => {
switch (activeTab) {
case 'actual':
case 'excessive':
case 'equipment':
return tableData;
default:
return tableData;
}
};
// 获取当前标签页的列定义
const getCurrentColumns = () => {
switch (activeTab) {
case 'actual':
case 'excessive':
case 'equipment':
return normalColumns;
default:
return normalColumns;
}
};
// 分页配置
const pagination = {
current: 1,
pageSize: 10,
total: 12,
showTotal: (total) => `${total}`,
showSizeChanger: true,
showQuickJumper: true,
};
return (
<div className={styles.dischargePermitManagement}>
<div className={styles.tabContainer}>
<div className={styles.tabButtons}>
<Button
className={`${styles.tabButton} ${activeTab === 'actual' ? styles.active : ''}`}
onClick={() => setActiveTab('actual')}
>
异常报警
</Button>
<Button
className={`${styles.tabButton} ${activeTab === 'excessive' ? styles.active : ''}`}
onClick={() => setActiveTab('excessive')}
>
超标报警
</Button>
<Button
className={`${styles.tabButton} ${activeTab === 'equipment' ? styles.active : ''}`}
onClick={() => setActiveTab('equipment')}
>
离线报警
</Button>
</div>
<div className={styles.filterSection}>
<Select
defaultValue={"1"}
className={styles.filterSelect}
placeholder="请选择"
style={{ width: 120 }}
options={
[
{ value: '1', label: '全部站点类型' },
]
}
/>
<Select
defaultValue={"1"}
className={styles.filterSelect}
placeholder="请选择"
style={{ width: 120 }}
options={
[
{ value: '1', label: '全部站点' },
]
}
/>
<Select
defaultValue={"1"}
className={styles.filterSelect}
placeholder="请选择"
style={{ width: 120 }}
options={
[
{ value: '1', label: '全部监测因子' },
]
}
/>
<Button
type="primary"
// icon={<SearchOutlined />}
className={styles.queryBtn}
>
查询
</Button>
<Button
// icon={<RedoOutlined />}
className={styles.resetBtn}
>
重置
</Button>
</div>
</div>
<div className={styles.tableContent}>
<StandardTable
columns={getCurrentColumns()}
data={{
list: getCurrentData(),
pagination: pagination
}}
rowKey="key"
/>
</div>
</div>
);
};
export default AbnormalAlarmInformation;

@ -0,0 +1,160 @@
.dischargePermitManagement {
width: 100%;
height: 100%;
background-color: #fff;
padding: 20px;
.tabContainer {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding: 0 ;
// border-bottom: 1px solid #e8e8e8;
.tabButtons {
display: flex;
gap: 0;
background-color: #F4F4F4;
border-radius: 4px;
padding: 4px 8px;
align-items: center;
.tabButton {
padding: 8px 20px;
border: none;
background-color: transparent;
color: #666;
font-size: 14px;
font-weight: 500;
cursor: pointer;
border-radius: 4px;
transition: all 0.3s ease;
height: auto;
line-height: 1.4;
&:hover {
color: #1890ff;
background-color: rgba(24, 144, 255, 0.1);
}
&.active {
background-color: #03C598;
color: #fff;
}
}
}
.filterSection {
display: flex;
align-items: center;
gap: 12px;
border-radius: 4px;
.filterLabel {
font-size: 14px;
color: #333;
}
.filterSelect {
min-width: 120px;
}
.filterInput {
min-width: 200px;
border-radius: 4px;
}
.queryBtn {
background-color: #00D48A;
border-color: #00D48A;
border-radius: 4px;
&:hover {
background-color: #389e0d;
border-color: #389e0d;
}
}
.resetBtn, .uploadBtn {
background-color: #fff;
color: #666;
border-color: #d9d9d9;
border-radius: 4px;
&:hover {
border-color: #1890ff;
color: #1890ff;
}
}
}
}
.tableContent {
width: 100%;
// 覆盖表头样式
:global {
.ant-table-thead > tr > th {
font-weight: 400 !important;
font-size: 12px !important;
color: #333 !important;
// text-align: center !important;
background-color: #FAFAFA !important;
}
.ant-table-tbody > tr > td {
font-size: 12px;
color: #333;
text-align: center;
}
.ant-table-wrapper {
width: 100%;
max-width: 100%;
}
}
.attachmentLink {
color: #1890ff;
text-decoration: none;
font-size: 12px;
&:hover {
text-decoration: underline;
}
}
.actionButtons {
display: flex;
gap: 20px;
justify-content: center;
align-items: center;
.downloadIcon, .deleteIcon {
width: 20px;
height: 20px;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
transform: scale(1.1);
opacity: 0.8;
}
}
.downloadIcon {
&:hover {
filter: brightness(1.2);
}
}
.deleteIcon {
&:hover {
filter: brightness(1.2) saturate(1.5);
}
}
}
}
}

@ -0,0 +1,235 @@
import React, { useState } from 'react';
import { Form, Input, Button, DatePicker, Space, Modal, Select } from 'antd';
import { SearchOutlined, RedoOutlined, CloseOutlined, EyeOutlined, DeleteOutlined, PlusOutlined, UploadOutlined, EditOutlined, DownloadOutlined } from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './AlarmInformation.less';
import viewIcon from '@/assets/business_envinformation/viewicon.svg';
import deleteIcon from '@/assets/business_envinformation/deleteicon.svg';
const AlarmInformation = () => {
const [form] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false);
const [currentImage, setCurrentImage] = useState(null);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 12,
});
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [dataSource, setDataSource] = useState([
{ key: 1, type: 'xxxx', level: '二级', company: '北京市-聚能和京东有限公司', site: 'xxxx站点', time: '2025-08-31', method: 'xxxx', content: 'xxxx' },
{ key: 2, type: 'xxxx', level: '二级', company: '万和化企业服务有限公司北京分公司', site: 'xxxx站点', time: '2025-08-29', method: 'xxxx', content: 'xxxx' },
{ key: 3, type: 'xxxx', level: '二级', company: '佳园国际集团有限公司', site: 'xxxx站点', time: '2025-09-14', method: 'xxxx', content: 'xxxx' },
{ key: 4, type: 'xxxx', level: '二级', company: '飞骑摩车(中国)有限公司北京分公司', site: 'xxxx站点', time: '2025-09-13', method: 'xxxx', content: 'xxxx' },
{ key: 5, type: 'xxxx', level: '二级', company: '深圳亿和企业管理有限公司', site: 'xxxx站点', time: '2025-08-20', method: 'xxxx', content: 'xxxx' },
{ key: 6, type: 'xxxx', level: '二级', company: '深圳亿和企业管理有限公司', site: 'xxxx站点', time: '2025-08-20', method: 'xxxx', content: 'xxxx' },
{ key: 7, type: 'xxxx', level: '二级', company: '深圳亿和企业管理有限公司', site: 'xxxx站点', time: '2025-08-20', method: 'xxxx', content: 'xxxx' },
{ key: 8, type: 'xxxx', level: '二级', company: '深圳亿和企业管理有限公司', site: 'xxxx站点', time: '2025-08-20', method: 'xxxx', content: 'xxxx' },
{ key: 9, type: 'xxxx', level: '二级', company: '深圳亿和企业管理有限公司', site: 'xxxx站点', time: '2025-08-20', method: 'xxxx', content: 'xxxx' },
]);
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const start = (current - 1) * pageSize;
const end = start + pageSize;
return dataSource.slice(start, end);
};
const columns = [
{ title: '序号', dataIndex: 'key', key: 'key', align: 'left' },
{ title: '告警类型', dataIndex: 'type', key: 'type', align: 'left' },
{ title: '告警等级', dataIndex: 'level', key: 'level', align: 'left' },
{ title: '所属企业', dataIndex: 'company', key: 'company', align: 'left' },
{ title: '告警站点', dataIndex: 'site', key: 'site', align: 'left' },
{ title: '告警时间', dataIndex: 'time', key: 'time', align: 'left' },
{ title: '通知方式', dataIndex: 'method', key: 'method', align: 'left' },
{ title: '告警内容', dataIndex: 'content', key: 'content', align: 'left' },
{
title: '操作',
key: 'action',
align: 'center',
render: (_, record) => (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '14px' }}>
<img
src={viewIcon}
alt="查看"
style={{ width: 16, height: 16, cursor: 'pointer' }}
onClick={() => handleView(record)}
/>
<img src={deleteIcon} alt="删除" style={{ width: 16, height: 16, cursor: 'pointer' }} />
</div>
),
},
];
const handleSearch = (values) => {
console.log('搜索参数:', values);
// TODO: 实现搜索功能
};
const handleReset = () => {
form.resetFields();
// TODO: 重置搜索
};
const handleView = (record) => {
console.log('查看:', record);
// TODO: 实现查看功能
};
const handleEdit = (record) => {
console.log('编辑:', record);
// TODO: 实现编辑功能
};
const handleDownload = (record) => {
console.log('下载:', record);
// TODO: 实现下载功能
};
const handleDelete = (record) => {
console.log('删除:', record);
// TODO: 实现删除功能
};
const handleAdd = () => {
console.log('新增');
// TODO: 实现新增功能
};
const handleImport = () => {
console.log('导入');
// TODO: 实现导入功能
};
const handleQuery = () => {
console.log('查询');
// TODO: 实现查询功能
};
const handleTableChange = (pagination) => {
setPagination(pagination);
};
// 全选功能
const handleSelectAll = (checked) => {
if (checked) {
const allKeys = getCurrentPageData().map(item => item.key);
setSelectedRowKeys(allKeys);
} else {
setSelectedRowKeys([]);
}
};
// 批量操作
const handleBatchOperation = (operation) => {
console.log(`批量${operation}:`, selectedRowKeys);
// TODO: 实现批量操作功能
};
return (
<div className={styles.wasteGasContainer}>
{/* 第一块:操作按钮和筛选条件 */}
<div className={styles.searchSection}>
<Form
form={form}
layout="inline"
className={styles.rightControls}
onFinish={handleQuery}
>
<Form.Item label="告警类型" name="type" style={{ marginRight: 12 }}>
<Select
placeholder="请选择"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
options={[
{ value: '1', label: '类型1' },
{ value: '2', label: '类型2' },
{ value: '3', label: '类型3' },
]}
/>
</Form.Item>
<Form.Item label="告警等级" name="level" style={{ marginRight: 12 }}>
<Select
placeholder="全部监测因子"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
options={[
{ value: '1', label: '一级' },
{ value: '2', label: '二级' },
{ value: '3', label: '三级' },
]}
/>
</Form.Item>
<Form.Item label="告警站点" name="site" style={{ marginRight: 12 }}>
<Select
placeholder="请选择"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
options={[
{ value: '1', label: '站点1' },
{ value: '2', label: '站点2' },
{ value: '3', label: '站点3' },
]}
/>
</Form.Item>
<Form.Item style={{ marginInlineEnd: 0 }}>
<Button
type="primary"
htmlType="submit"
className={styles.queryButton}
>
查询
</Button>
</Form.Item>
<Form.Item>
<Button
onClick={handleReset}
className={styles.resetButton}
>
重置
</Button>
</Form.Item>
</Form>
</div>
{/* 第二块:表格 */}
<div className={styles.tableSection}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: {
...pagination,
total: dataSource.length,
showTotal: (total) => `${total}`,
showSizeChanger: false,
}
}}
onChange={handleTableChange}
scroll={{ x: 'max-content' }}
/>
</div>
{/* 图片弹窗 */}
<Modal
open={isModalVisible}
onCancel={() => setIsModalVisible(false)}
footer={null}
closeIcon={<CloseOutlined style={{ color: '#fff', fontSize: 20 }} />}
width="auto"
centered
styles={{
mask: { backgroundColor: '#10101080' },
content: { padding: 0, background: 'transparent', boxShadow: 'none' }
}}
>
{currentImage && <img src={currentImage} alt="许可证" style={{ width: '100%', display: 'block' }} />}
</Modal>
</div>
);
};
export default AlarmInformation;

@ -0,0 +1,213 @@
.wasteGasContainer {
width: 100%;
height: 100%;
// padding: 20px;
background-color: #fff;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
.switchContainer {
width: 200px;
display: flex;
justify-content: space-between;
align-items: center;
height: 30px;
line-height: 30px;
background-color: #F4F4F4;
border-radius: 6px;
padding: 0 15px;
}
.searchSection {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.leftButtons {
display: flex;
gap: 12px;
.addButton {
background-color: #00D48A;
border-color: #00D48A;
color: white;
height: 32px;
border-radius: 4px;
display: flex;
align-items: center;
&:hover {
background-color: #00D48A;
border-color: #00D48A;
}
}
.importButton {
background-color: white;
border-color: #d9d9d9;
color: #333;
height: 32px;
border-radius: 4px;
&:hover {
border-color: #40a9ff;
color: #40a9ff;
}
}
}
.rightControls {
display: flex;
align-items: center;
gap: 12px;
.filterLabel {
color: #333;
font-size: 14px;
white-space: nowrap;
}
.queryButton {
background-color: #00D48A;
border-color: #00D48A;
color: white;
height: 28px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #00D48A;
border-color: #00D48A;
}
}
.selectInput {
:global {
.ant-select-selector {
display: flex;
align-items: center;
height: 28px !important;
}
.ant-select-selection-item {
display: flex;
align-items: center;
line-height: 1;
}
.ant-select-selection-placeholder {
display: flex;
align-items: center;
line-height: 1;
}
}
}
}
}
.tableSection {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 20px 0;
overflow: hidden; // 只设置垂直隐藏
min-width: 0; // 确保可以收缩
:global {
.ant-spin-nested-loading {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
.ant-spin-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
.ant-table-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
}
}
}
.ant-table {
font-size: 12px;
.ant-table-thead > tr > th {
background-color: #FAFAFA;
font-weight: 400;
color: #000000D9;
border-right: none;
text-align: center;
white-space: nowrap; // 防止换行
overflow: hidden;
text-overflow: ellipsis;
}
.ant-table-tbody > tr > td {
border-right: none;
color: #000000D9;
font-weight: 400;
text-align: center;
white-space: nowrap; // 防止换行
overflow: hidden;
text-overflow: ellipsis;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f5f5;
}
// 固定列样式
.ant-table-thead > tr > th.ant-table-cell-fix-left,
.ant-table-tbody > tr > td.ant-table-cell-fix-left {
// background-color: #FAFAFA;
z-index: 1;
}
.ant-table-thead > tr > th.ant-table-cell-fix-right,
.ant-table-tbody > tr > td.ant-table-cell-fix-right {
// background-color: #FAFAFA;
z-index: 1;
}
// 固定列阴影效果
.ant-table-cell-fix-left {
// box-shadow: 0px 0 4px 0px #00000040;
}
.ant-table-cell-fix-right {
// box-shadow: 0px 0 4px 0px #00000040;
}
a {
color: #1890ff;
text-decoration: none;
&:hover {
color: #40a9ff;
}
}
}
.ant-pagination {
text-align: right;
margin-top: 25px !important;
}
}
}
}

@ -0,0 +1,617 @@
import React, { useState } from 'react';
import { Form, Input, Button, DatePicker, Space, Modal, Select, Switch } from 'antd';
import { SearchOutlined, RedoOutlined, CloseOutlined, EyeOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './ExhaustEmissionStandard.less';
const ExhaustEmissionStandard = () => {
const [form] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false);
const [currentImage, setCurrentImage] = useState(null);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 12,
});
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [dataSource, setDataSource] = useState([
// 图片表格数据
{
key: 1,
region: '2025-08-29',
source: '赵喜行',
outletName: '郑叶飞',
outletNumber: '塞隆风湿酒(同仁堂)',
monitorTime: '35.236.217.212',
smokeHumidity: '6Dd888D-3d',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 2,
region: '2025-09-02',
source: '王嘉琪',
outletName: '赵子峰',
outletNumber: '复方水杨酸甲酯乳膏(曼秀雷敦)',
monitorTime: '65.177.48.116',
smokeHumidity: '3D8d4ffa-bD',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 3,
region: '2025-09-06',
source: '王嘉琪',
outletName: '郑清予',
outletNumber: '口炎清颗粒(大神)',
monitorTime: '111.161.135.125',
smokeHumidity: 'f9b89EB2-ae9',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 4,
region: '2025-09-06',
source: '王思琪',
outletName: '赵玉',
outletNumber: '烧结砖(茂祥)',
monitorTime: '162.208.135.147',
smokeHumidity: '0ca9DB56-DA',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 5,
region: '2025-08-31',
source: '吴子富',
outletName: '钱茂莉',
outletNumber: '云南白药(云南白药)',
monitorTime: '138.6.246.181',
smokeHumidity: 'bf0173D6-3cf',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 6,
region: '2025-08-30',
source: '何凤景',
outletName: '赵海洲',
outletNumber: '鹏程书局(修正)',
monitorTime: '134.80.173.128',
smokeHumidity: '55aACD54-69',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 7,
region: '2025-08-29',
source: '李计颖',
outletName: '李海洲',
outletNumber: '复方醋酸甲羟孕酮颗粒(妇复春)',
monitorTime: '218.169.25.43',
smokeHumidity: 'b0a849eD-9e',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 8,
region: '2025-08-26',
source: '赵年光',
outletName: '钱海莉',
outletNumber: '杜龙药酒',
monitorTime: '54.218.80.84',
smokeHumidity: 'b1D5cFbD-EA',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 9,
region: '2025-08-21',
source: '王凤妍',
outletName: '李金泽',
outletNumber: '和兴白花油(和兴白花油)',
monitorTime: '222.1.18.173',
smokeHumidity: 'fbc9cb69-F8B',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 10,
region: '2025-09-09',
source: '赵芳',
outletName: '何能',
outletNumber: '柔来期明含片(哈喽舒)',
monitorTime: '136.200.182.22',
smokeHumidity: 'e4b6adE6-8C1',
smokeFlow: '重量',
smokePressure: '2',
o2Content: 't',
o2Content2: 't',
},
{
key: 5,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 6,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 7,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 8,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 9,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 10,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 11,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 12,
recordTime: '2025-08-29',
recorder: '赵喜行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
equipmentModel: '6Ddb888D-3d7A-305E-3372-109F7154Ad3A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantFlueGasVolume: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
exhaustStackHeight: '2',
exhaustTemperature: '50.48',
pressure: '100',
emissionTime: '115',
powerConsumption: '39',
byproductName: '氮气',
byproductProduction: '2',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
]);
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const start = (current - 1) * pageSize;
const end = start + pageSize;
return dataSource.slice(start, end);
};
const columns = [
{
title: (
<input
type="checkbox"
checked={selectedRowKeys.length === getCurrentPageData().length && getCurrentPageData().length > 0}
onChange={(e) => handleSelectAll(e.target.checked)}
/>
),
key: 'selection',
width: 60,
align: 'center',
fixed: 'left',
render: (_, record) => (
<input
type="checkbox"
checked={selectedRowKeys.includes(record.key)}
onChange={(e) => {
if (e.target.checked) {
setSelectedRowKeys([...selectedRowKeys, record.key]);
} else {
setSelectedRowKeys(selectedRowKeys.filter(key => key !== record.key));
}
}}
/>
),
},
{ title: '所属区县', dataIndex: 'region', key: 'region', align: 'left' },
{ title: '污染源', dataIndex: 'source', key: 'source', align: 'left' },
{ title: '排口名称', dataIndex: 'outletName', key: 'outletName', align: 'left' },
{ title: '排口编号', dataIndex: 'outletNumber', key: 'outletNumber', align: 'left' },
{ title: '监测时间', dataIndex: 'monitorTime', key: 'monitorTime', align: 'left' },
{ title: '烟气湿度', dataIndex: 'smokeHumidity', key: 'smokeHumidity', align: 'left' },
{ title: '烟气流速', dataIndex: 'smokeFlow', key: 'smokeFlow', align: 'left' },
{ title: '烟气压力', dataIndex: 'smokePressure', key: 'smokePressure', align: 'left' },
{ title: 'O₂含量', dataIndex: 'o2Content', key: 'o2Content', align: 'left' },
{ title: 'O₂含量', dataIndex: 'o2Content2', key: 'o2Content2', align: 'left' },
];
const handleSearch = (values) => {
console.log('搜索参数:', values);
// TODO: 实现搜索功能
};
const handleReset = () => {
form.resetFields();
// TODO: 重置搜索
};
const handleView = (record) => {
console.log('查看:', record);
// TODO: 实现查看功能
};
const handleEdit = (record) => {
console.log('编辑:', record);
// TODO: 实现编辑功能
};
const handleDownload = (record) => {
console.log('下载:', record);
// TODO: 实现下载功能
};
const handleDelete = (record) => {
console.log('删除:', record);
// TODO: 实现删除功能
};
const handleAdd = () => {
console.log('新增');
// TODO: 实现新增功能
};
const handleImport = () => {
console.log('导入');
// TODO: 实现导入功能
};
const handleQuery = () => {
console.log('查询');
// TODO: 实现查询功能
};
const handleTableChange = (pagination) => {
setPagination(pagination);
};
// 全选功能
const handleSelectAll = (checked) => {
if (checked) {
const allKeys = getCurrentPageData().map(item => item.key);
setSelectedRowKeys(allKeys);
} else {
setSelectedRowKeys([]);
}
};
// 批量操作
const handleBatchOperation = (operation) => {
console.log(`批量${operation}:`, selectedRowKeys);
// TODO: 实现批量操作功能
};
return (
<div className={styles.wasteGasContainer}>
{/* 第一块:操作按钮和筛选条件 */}
<div className={styles.searchSection}>
<div className={styles.leftButtons}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={handleAdd}
className={styles.addButton}
>
新增
</Button>
<Button
// icon={<UploadOutlined />}
onClick={handleImport}
className={styles.importButton}
>
上传
</Button>
<Button
// icon={<UploadOutlined />}
onClick={() => handleBatchOperation('下载')}
className={styles.importButton}
disabled={selectedRowKeys.length === 0}
>
批量下载
</Button>
<div className={styles.switchContainer}>
<span>仅显示异常信息</span>
<Switch />
</div>
</div>
<div className={styles.rightControls}>
<Select
defaultValue={'1'}
placeholder="请选择"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
options={
[
{ value: '1', label: 'xx监测点' },
]
}
/>
<Select
defaultValue={'1'}
placeholder="请选择"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
options={
[
{ value: '1', label: 'xx污染物' },
]
}
/>
<Select
defaultValue={'1'}
placeholder="请选择"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
options={
[
{ value: '1', label: '本月' },
]
}
/>
<Button
onClick={handleQuery}
className={styles.queryButton}
>
查询
</Button>
<Button
onClick={handleReset}
className={styles.resetButton}
>
重置
</Button>
</div>
</div>
{/* 第二块:表格 */}
<div className={styles.tableSection}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: {
...pagination,
total: dataSource.length,
showTotal: (total) => `${total}`,
showSizeChanger: false,
}
}}
onChange={handleTableChange}
scroll={{ x: 'max-content' }}
/>
</div>
{/* 图片弹窗 */}
<Modal
open={isModalVisible}
onCancel={() => setIsModalVisible(false)}
footer={null}
closeIcon={<CloseOutlined style={{ color: '#fff', fontSize: 20 }} />}
width="auto"
centered
styles={{
mask: { backgroundColor: '#10101080' },
content: { padding: 0, background: 'transparent', boxShadow: 'none' }
}}
>
{currentImage && <img src={currentImage} alt="许可证" style={{ width: '100%', display: 'block' }} />}
</Modal>
</div>
);
};
export default ExhaustEmissionStandard;

@ -0,0 +1,213 @@
.wasteGasContainer {
width: 100%;
height: 100%;
// padding: 20px;
background-color: #fff;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
.switchContainer {
width: 200px;
display: flex;
justify-content: space-between;
align-items: center;
height: 30px;
line-height: 30px;
background-color: #F4F4F4;
border-radius: 6px;
padding: 0 15px;
}
.searchSection {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.leftButtons {
display: flex;
gap: 12px;
.addButton {
background-color: #00D48A;
border-color: #00D48A;
color: white;
height: 32px;
border-radius: 4px;
display: flex;
align-items: center;
&:hover {
background-color: #00D48A;
border-color: #00D48A;
}
}
.importButton {
background-color: white;
border-color: #d9d9d9;
color: #333;
height: 32px;
border-radius: 4px;
&:hover {
border-color: #40a9ff;
color: #40a9ff;
}
}
}
.rightControls {
display: flex;
align-items: center;
gap: 12px;
.filterLabel {
color: #333;
font-size: 14px;
white-space: nowrap;
}
.queryButton {
background-color: #00D48A;
border-color: #00D48A;
color: white;
height: 28px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #00D48A;
border-color: #00D48A;
}
}
.selectInput {
:global {
.ant-select-selector {
display: flex;
align-items: center;
height: 28px !important;
}
.ant-select-selection-item {
display: flex;
align-items: center;
line-height: 1;
}
.ant-select-selection-placeholder {
display: flex;
align-items: center;
line-height: 1;
}
}
}
}
}
.tableSection {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 20px 0;
overflow: hidden; // 只设置垂直隐藏
min-width: 0; // 确保可以收缩
:global {
.ant-spin-nested-loading {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
.ant-spin-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
.ant-table-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
}
}
}
.ant-table {
font-size: 12px;
.ant-table-thead > tr > th {
background-color: #FAFAFA;
font-weight: 400;
color: #000000D9;
border-right: none;
text-align: center;
white-space: nowrap; // 防止换行
overflow: hidden;
text-overflow: ellipsis;
}
.ant-table-tbody > tr > td {
border-right: none;
color: #000000D9;
font-weight: 400;
text-align: center;
white-space: nowrap; // 防止换行
overflow: hidden;
text-overflow: ellipsis;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f5f5;
}
// 固定列样式
.ant-table-thead > tr > th.ant-table-cell-fix-left,
.ant-table-tbody > tr > td.ant-table-cell-fix-left {
// background-color: #FAFAFA;
z-index: 1;
}
.ant-table-thead > tr > th.ant-table-cell-fix-right,
.ant-table-tbody > tr > td.ant-table-cell-fix-right {
// background-color: #FAFAFA;
z-index: 1;
}
// 固定列阴影效果
.ant-table-cell-fix-left {
// box-shadow: 0px 0 4px 0px #00000040;
}
.ant-table-cell-fix-right {
// box-shadow: 0px 0 4px 0px #00000040;
}
a {
color: #1890ff;
text-decoration: none;
&:hover {
color: #40a9ff;
}
}
}
.ant-pagination {
text-align: right;
margin-top: 25px !important;
}
}
}
}

@ -0,0 +1,746 @@
import React, { useState } from 'react';
import { Form, Input, Button, DatePicker, Space, Modal, Select } from 'antd';
import { SearchOutlined, RedoOutlined, CloseOutlined, EyeOutlined, DeleteOutlined, PlusOutlined, UploadOutlined, EditOutlined, DownloadOutlined } from '@ant-design/icons';
import StandardTable from '@/components/StandardTable';
import styles from './WastewaterFacilityManagement.less';
import licence1 from '@/assets/business_envinformation/image1.svg';
import licence2 from '@/assets/business_envinformation/image2.svg';
import viewicon from '@/assets/business_envinformation/viewicon.svg';
import editicon from '@/assets/business_envinformation/editicon.svg';
import downloadicon from '@/assets/business_envinformation/downloadicon.svg';
import deleteicon from '@/assets/business_envinformation/deleteicon.svg';
const WastewaterFacilityManagement = () => {
const [form] = Form.useForm();
const [isModalVisible, setIsModalVisible] = useState(false);
const [currentImage, setCurrentImage] = useState(null);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 12,
});
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [dataSource, setDataSource] = useState([
{
key: 1,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 2,
recordTime: '2025-09-02',
recorder: '王嘉祺',
reviewer: '赵子峰',
facilityName: '复方水杨酸甲酯乳膏(曼秀雷敦)',
code: '65.177.48.116',
facilityModel: '3D8d4ffa-bD7e-AffF-ED68-839DAFe74c27',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '14:10',
endRunTime: '03:06',
isNormal: '异常',
pollutantOutletFlow: '2',
pollutantFactor: '烟尘2',
treatmentEfficiency: '93.81',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '59.12',
treatmentMethod: '95',
powerConsumption: '34',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-08-24 05:16',
reagentAdditionAmount: '2',
},
{
key: 3,
recordTime: '2025-09-06',
recorder: '王嘉琪',
reviewer: '郑清予',
facilityName: '口炎清颗粒(大神)',
code: '111.161.135.125',
facilityModel: '19b89EB2-ae94-6bF7-2355-6DBC2d6a6009',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '12:26',
endRunTime: '03:26',
isNormal: '异常',
pollutantOutletFlow: '2',
pollutantFactor: '烟尘',
treatmentEfficiency: '91.40',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '59.95',
treatmentMethod: '90',
powerConsumption: '56',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-08-30 18:34',
reagentAdditionAmount: '2',
},
{
key: 4,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 5,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 6,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 7,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 8,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 9,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 10,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 11,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
{
key: 12,
recordTime: '2025-08-29',
recorder: '赵吾行',
reviewer: '郑叶飞',
facilityName: '塞隆风湿酒(同仁堂)',
code: '35.236.217.212',
facilityModel: '6Ddb888D-3d7A-3e5E-3372-109F7154A03A',
parameterName: '重量',
designValue: '2',
unit: 't',
startRunTime: '00:46',
endRunTime: '03:55',
isNormal: '正常',
pollutantOutletFlow: '1',
pollutantFactor: '烟尘',
treatmentEfficiency: '95.32',
dataSource: '实时采集',
dischargeDestination: '2',
sludgeProduction: '50.48',
treatmentMethod: '100',
powerConsumption: '39',
reagentName: '乳酸钠葡萄糖',
reagentAdditionTime: '2025-09-01 07:53',
reagentAdditionAmount: '2',
},
]);
const getCurrentPageData = () => {
const { current, pageSize } = pagination;
const start = (current - 1) * pageSize;
const end = start + pageSize;
return dataSource.slice(start, end);
};
const columns = [
{
title: (
<input
type="checkbox"
checked={selectedRowKeys.length === getCurrentPageData().length && getCurrentPageData().length > 0}
onChange={(e) => handleSelectAll(e.target.checked)}
/>
),
key: 'selection',
width: 60,
align: 'center',
fixed: 'left',
render: (_, record) => (
<input
type="checkbox"
checked={selectedRowKeys.includes(record.key)}
onChange={(e) => {
if (e.target.checked) {
setSelectedRowKeys([...selectedRowKeys, record.key]);
} else {
setSelectedRowKeys(selectedRowKeys.filter(key => key !== record.key));
}
}}
/>
),
},
{
title: '记录时间',
dataIndex: 'recordTime',
key: 'recordTime',
width: 120,
align: 'center',
},
{
title: '记录人',
dataIndex: 'recorder',
key: 'recorder',
width: 100,
align: 'center',
},
{
title: '审核人',
dataIndex: 'reviewer',
key: 'reviewer',
width: 100,
align: 'center',
},
{
title: '设施名称',
dataIndex: 'facilityName',
key: 'facilityName',
width: 200,
},
{
title: '编码',
dataIndex: 'code',
key: 'code',
width: 150,
},
{
title: '设施型号',
dataIndex: 'facilityModel',
key: 'facilityModel',
width: 200,
},
{
title: '参数名称',
dataIndex: 'parameterName',
key: 'parameterName',
width: 120,
align: 'center',
},
{
title: '设计值',
dataIndex: 'designValue',
key: 'designValue',
width: 100,
align: 'center',
},
{
title: '单位',
dataIndex: 'unit',
key: 'unit',
width: 80,
align: 'center',
},
{
title: '开始运行时间',
dataIndex: 'startRunTime',
key: 'startRunTime',
width: 120,
align: 'center',
},
{
title: '结束运行时间',
dataIndex: 'endRunTime',
key: 'endRunTime',
width: 120,
align: 'center',
},
{
title: '是否正常',
dataIndex: 'isNormal',
key: 'isNormal',
width: 100,
align: 'center',
render: (text) => (
<span style={{
color: text === '异常' ? '#ff4d4f' : '#52c41a',
backgroundColor: text === '异常' ? '#FFF1F0' : '#F6FFED',
border: text === '异常' ? '1px solid #FFA39E' : '1px solid #B7EB8F',
padding: '2px 8px',
borderRadius: '4px',
display: 'inline-block'
}}>
{text}
</span>
),
},
{
title: '污染物出口流量(m³/h)',
dataIndex: 'pollutantOutletFlow',
key: 'pollutantOutletFlow',
width: 150,
align: 'center',
},
{
title: '污染因子',
dataIndex: 'pollutantFactor',
key: 'pollutantFactor',
width: 120,
align: 'center',
},
{
title: '治理效率(%)',
dataIndex: 'treatmentEfficiency',
key: 'treatmentEfficiency',
width: 120,
align: 'center',
},
{
title: '数据来源',
dataIndex: 'dataSource',
key: 'dataSource',
width: 120,
align: 'center',
},
{
title: '排放去向',
dataIndex: 'dischargeDestination',
key: 'dischargeDestination',
width: 120,
align: 'center',
},
{
title: '污泥产生量(t)',
dataIndex: 'sludgeProduction',
key: 'sludgeProduction',
width: 130,
align: 'center',
},
{
title: '处理方式',
dataIndex: 'treatmentMethod',
key: 'treatmentMethod',
width: 120,
align: 'center',
},
{
title: '耗电量(kWh)',
dataIndex: 'powerConsumption',
key: 'powerConsumption',
width: 120,
align: 'center',
},
{
title: '药剂名称',
dataIndex: 'reagentName',
key: 'reagentName',
width: 120,
align: 'center',
},
{
title: '药剂添加时间',
dataIndex: 'reagentAdditionTime',
key: 'reagentAdditionTime',
width: 150,
align: 'center',
},
{
title: '药剂添加量(t)',
dataIndex: 'reagentAdditionAmount',
key: 'reagentAdditionAmount',
width: 140,
align: 'center',
},
{
title: '操作',
key: 'action',
width: 140,
align: 'center',
fixed: 'right',
render: (_, record) => (
<Space size={18}>
{/* <img
src={viewicon}
alt="查看"
style={{ width: 16, height: 16, cursor: 'pointer' }}
onClick={() => handleView(record)}
/> */}
<img
src={editicon}
alt="编辑"
style={{ width: 16, height: 16, cursor: 'pointer' }}
onClick={() => handleEdit(record)}
/>
<img
src={downloadicon}
alt="下载"
style={{ width: 16, height: 16, cursor: 'pointer' }}
onClick={() => handleDownload(record)}
/>
<img
src={deleteicon}
alt="删除"
style={{ width: 16, height: 16, cursor: 'pointer' }}
onClick={() => handleDelete(record)}
/>
</Space>
),
},
];
const handleSearch = (values) => {
console.log('搜索参数:', values);
// TODO: 实现搜索功能
};
const handleReset = () => {
form.resetFields();
// TODO: 重置搜索
};
const handleView = (record) => {
console.log('查看:', record);
// TODO: 实现查看功能
};
const handleEdit = (record) => {
console.log('编辑:', record);
// TODO: 实现编辑功能
};
const handleDownload = (record) => {
console.log('下载:', record);
// TODO: 实现下载功能
};
const handleDelete = (record) => {
console.log('删除:', record);
// TODO: 实现删除功能
};
const handleAdd = () => {
console.log('新增');
// TODO: 实现新增功能
};
const handleImport = () => {
console.log('导入');
// TODO: 实现导入功能
};
const handleQuery = () => {
console.log('查询');
// TODO: 实现查询功能
};
const handleTableChange = (pagination) => {
setPagination(pagination);
};
// 全选功能
const handleSelectAll = (checked) => {
if (checked) {
const allKeys = getCurrentPageData().map(item => item.key);
setSelectedRowKeys(allKeys);
} else {
setSelectedRowKeys([]);
}
};
// 批量操作
const handleBatchOperation = (operation) => {
console.log(`批量${operation}:`, selectedRowKeys);
// TODO: 实现批量操作功能
};
return (
<div className={styles.wastewaterFacilityContainer}>
{/* 第一块:操作按钮和筛选条件 */}
<div className={styles.searchSection}>
<div className={styles.leftButtons}>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={handleAdd}
className={styles.addButton}
>
新增
</Button>
<Button
// icon={<UploadOutlined />}
onClick={handleImport}
className={styles.importButton}
>
上传
</Button>
<Button
// icon={<UploadOutlined />}
onClick={() => handleBatchOperation('下载')}
className={styles.importButton}
disabled={selectedRowKeys.length === 0}
>
批量下载
</Button>
{/* <Button
onClick={() => handleBatchOperation('删除')}
className={styles.importButton}
disabled={selectedRowKeys.length === 0}
>
批量删除
</Button> */}
</div>
<div className={styles.rightControls}>
<span className={styles.filterLabel}>筛选条件</span>
<Select
placeholder="请选择"
style={{ width: 140, height: 28 }}
allowClear
className={styles.selectInput}
/>
<Input
placeholder="请输入"
style={{ width: 160, height: 28 }}
/>
<Button
onClick={handleQuery}
className={styles.queryButton}
>
查询
</Button>
<Button
onClick={handleReset}
className={styles.resetButton}
>
重置
</Button>
</div>
</div>
{/* 第二块:表格 */}
<div className={styles.tableSection}>
<StandardTable
columns={columns}
data={{
list: getCurrentPageData(),
pagination: {
...pagination,
total: dataSource.length,
showTotal: (total) => `${total}`,
showSizeChanger: false,
}
}}
onChange={handleTableChange}
scroll={{ x: 3000 }}
/>
</div>
{/* 图片弹窗 */}
<Modal
open={isModalVisible}
onCancel={() => setIsModalVisible(false)}
footer={null}
closeIcon={<CloseOutlined style={{ color: '#fff', fontSize: 20 }} />}
width="auto"
centered
styles={{
mask: { backgroundColor: '#10101080' },
content: { padding: 0, background: 'transparent', boxShadow: 'none' }
}}
>
{currentImage && <img src={currentImage} alt="许可证" style={{ width: '100%', display: 'block' }} />}
</Modal>
</div>
);
};
export default WastewaterFacilityManagement;

@ -0,0 +1,206 @@
.wastewaterFacilityContainer {
width: 100%;
height: 100%;
// padding: 20px;
background-color: #fff;
display: flex;
flex-direction: column;
margin: 0;
padding: 0;
.searchSection {
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.leftButtons {
display: flex;
gap: 12px;
.addButton {
background-color: #52c41a;
border-color: #52c41a;
color: white;
height: 32px;
border-radius: 4px;
display: flex;
align-items: center;
gap: 4px;
&:hover {
background-color: #73d13d;
border-color: #73d13d;
}
}
.importButton {
background-color: white;
border-color: #d9d9d9;
color: #333;
height: 32px;
border-radius: 4px;
&:hover {
border-color: #40a9ff;
color: #40a9ff;
}
}
}
.rightControls {
display: flex;
align-items: center;
gap: 12px;
.filterLabel {
color: #333;
font-size: 14px;
white-space: nowrap;
}
.queryButton {
background-color: #52c41a;
border-color: #52c41a;
color: white;
height: 28px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #73d13d;
border-color: #73d13d;
}
}
.selectInput {
:global {
.ant-select-selector {
display: flex;
align-items: center;
height: 28px !important;
}
.ant-select-selection-item {
display: flex;
align-items: center;
line-height: 1;
}
.ant-select-selection-placeholder {
display: flex;
align-items: center;
line-height: 1;
}
}
}
}
}
.tableSection {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 20px 0;
overflow: hidden; // 只设置垂直隐藏
min-width: 0; // 确保可以收缩
:global {
.ant-spin-nested-loading {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
.ant-spin-container {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
.ant-table-wrapper {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
margin: 0;
padding: 0;
}
}
}
.ant-table {
font-size: 12px;
.ant-table-thead > tr > th {
background-color: #fafafa;
font-weight: 400;
color: #000000D9;
border-right: none;
text-align: center;
white-space: nowrap; // 防止换行
overflow: hidden;
text-overflow: ellipsis;
}
.ant-table-tbody > tr > td {
border-right: none;
color: #000000D9;
font-weight: 400;
text-align: center;
white-space: nowrap; // 防止换行
overflow: hidden;
text-overflow: ellipsis;
}
.ant-table-tbody > tr:hover > td {
background-color: #f5f5f5;
}
// 固定列样式
.ant-table-thead > tr > th.ant-table-cell-fix-left,
.ant-table-tbody > tr > td.ant-table-cell-fix-left {
background-color: #fafafa;
z-index: 1;
}
.ant-table-thead > tr > th.ant-table-cell-fix-right,
.ant-table-tbody > tr > td.ant-table-cell-fix-right {
background-color: #fafafa;
z-index: 1;
}
// 固定列阴影效果
.ant-table-cell-fix-left {
// box-shadow: 0px 0 4px 0px #00000040;
}
.ant-table-cell-fix-right {
// box-shadow: 0px 0 4px 0px #00000040;
}
a {
color: #1890ff;
text-decoration: none;
&:hover {
color: #40a9ff;
}
}
}
.ant-pagination {
text-align: right;
margin-top: 25px !important;
}
}
}
}

@ -20,6 +20,7 @@ import license from '@/assets/img/license.svg'
import people from '@/assets/img/people.svg'
import risk from '@/assets/img/risk.svg'
import inforicon from '@/assets/business_basic/inforicon.svg'
import alarm from '@/assets/business_envmonitoring/alarm.svg'
import { CustomBreadcrumb } from '@/components/GlobalComponent'
@ -102,6 +103,35 @@ const SystemContentList = (props) => {
"key": "/topnavbar00/business/envInformation",
"label": "环保信息管理"
},
{
"path": "/topnavbar00/business/atmospherePollutantLibrary",
icon: <img
src={inforicon}
alt="大气污染特征污染物名录库"
style={{
width: '16px',
height: '16px',
opacity: selectedKey.includes('/topnavbar00/business/atmospherePollutantLibrary') ? 1 : 0.6
}}
/>,
"key": "/topnavbar00/business/atmospherePollutantLibrary",
"label": "大气污染特征污染物名录库",
"hideInMenu": true // 隐藏菜单,但允许路由匹配
},
{
"path": "/topnavbar00/business/envmonitoring",
icon: <img
src={alarm}
alt="环保监测管理"
style={{
width: '16px',
height: '16px',
opacity: selectedKey.includes('/topnavbar00/business/envmonitoring') ? 1 : 0.6
}}
/>,
"key": "/topnavbar00/business/envmonitoring",
"label": "环保监测管理"
},
{
"path": "/topnavbar00/business/basicinformation",
icon: <img src={fireHydrant1} alt="安全管理基础信息" style={{ width: '16px', height: '16px' }} />,

@ -18,8 +18,8 @@ const menuItem = [
key: '/topnavbar00/business/atmospherePollutantLibrary',
},
{
label: '安全管理基础信息',
key: '/topnavbar00/business/basicinformation',
label: '环保检测管理',
key: '/topnavbar00/business/envmonitoring',
},
{
label: '隐患排查',

Loading…
Cancel
Save