合并代码

lsf-dev
秦子超 2025-05-22 16:16:56 +08:00
commit 3516030b2e
14 changed files with 747 additions and 3 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
public/assets/gongshui.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@ -315,6 +315,21 @@ const map = {
Object.keys(DCPJ_TYPES).forEach(key => {
layerVisible['Dcpj_' + key + 'Layer'] = false;
});
} else if (id === 301) {
layerVisible = {
RealDrpLayer: false,
RealHDLayer: false,
RealSkLayer: true,
BxSkLayer: false,
FzdxLayer: false,
WataLayer: false,
AdcdLayer: true,
RoadLayer: true,
RivlLayer: true,
};
Object.keys(DCPJ_TYPES).forEach(key => {
layerVisible['Dcpj_' + key + 'Layer'] = false;
});
}
dispatch.runtime.setLayerSetting({ contour: null, dem: undefined })

View File

@ -87,6 +87,13 @@ export default function calcLayout(view, rightStack, hidePanels) {
{ key: '水库管理', style: { height: '40rem', flexGrow: 1 } },
];
leftFullHeight = true;
} else if (view === 301) {
left = [
{ key: '天气' },
{ key: '供水态势',style: { height: '16rem', flexGrow: 1 } },
{ key: '日供水量',style: { height: '16rem', flexGrow: 1 } },
{ key: '供水覆盖率',style: { height: '16rem', flexGrow: 1 } },
];
} else if (view === 501) {
left = [
{ key: '雨量监测',style: { height: '8rem', flexGrow: 1 } },
@ -217,6 +224,13 @@ export default function calcLayout(view, rightStack, hidePanels) {
right = [
{ key: '警报' },
];
} else if (view === 301) {
right = [
{ key: '警报' },
{ key: '对比分析', style: { height: '19rem', flexGrow: 1 } },
{ key: '供水监控', style: { flexGrow: 1 } },
rightFullHeight = true
];
} else if (view === 501) {
right = [
{ key: '水质监测',style: { height: '20rem', flexGrow: 1 } },

View File

@ -47,7 +47,6 @@ import Szwtqytj from './panels/Szwtqytj'
import Spjc from './panels/Spjc'
import Skgl from './panels/Skgl'
import Fxdd from './panels/Fxdd'
import GqSta from './panels/GqSta'
import Gqyssqs from './panels/Gqyqs'
import Gqbl from './panels/Gqbl'
@ -56,6 +55,11 @@ import Gqycqd from './panels/Gqycqd'
import Gqdb from './panels/Gqdb'
import Szzdd from './panels/Szzdd'
import Yyfa from './panels/Yyfa'
import Gsts from './panels/Gsts'
import Gsfgl from './panels/Gsfgl'
import Rgsl from './panels/Rgsl'
import Dbfx from './panels/Dbfx'
import Gsjk from './panels/Gsjk'
export default function PanelIndex({ name, style, ...params }) {
if (name === '天气') {
@ -170,7 +174,18 @@ export default function PanelIndex({ name, style, ...params }) {
return <Szzdd style={style} />
}else if (name == '预演方案') {
return <Yyfa style={style} />
} else if (name === '供水态势') {
return <Gsts style={style} />
} else if (name === '供水覆盖率') {
return <Gsfgl style={style} />
} else if (name === '日供水量') {
return <Rgsl style={style} />
} else if (name === '对比分析') {
return <Dbfx style={style} />
} else if (name === '供水监控') {
return <Gsjk style={style} />
}
return (
<PanelBox style={style} title={name} color="red">
<p>not impl</p>

View File

@ -22,7 +22,9 @@ const VIEWS = [
{ id: 205, title: '防汛调度', img: '/assets/menu/辅助决策.png' },
{ id: 206, title: '水库管理', img: '/assets/menu/病险水库.png' },
] },
{ id: 300, title: '水厂', img: '/assets/menu/实时数据.png',children:[
{ id: 300, title: '水厂', img: '/assets/menu/水利设施.png',children:[
{ id: 301, title: '供水态势', img: '/assets/menu/降雨中心.png' },
// 供水态势
// 水质安全
// 水厂运行
@ -31,7 +33,7 @@ const VIEWS = [
// 决策支持与报表
] },
{
id: 400, title: '灌区', img: '/assets/menu/实时数据.png', children: [
id: 400, title: '灌区', img: '/assets/menu/预警分析.png', children: [
{ id: 501, title: '灌区监测', img: '/assets/menu/防洪形势.png' },
{ id: 504, title: '水旱灾害防御', img: '/assets/menu/水利设施.png' },
// { id: 502, title: '预警信息管理', img: '/assets/menu/实时数据.png' },

View File

@ -0,0 +1,17 @@
import React, { useMemo, useState } from 'react';
import PanelBox from '../../components/PanelBox';
function DrpReal({ style }) {
return (
<PanelBox
style={style}
title="对比分析"
color="green"
>
<img src='/assets/duibifenxi.png' style={{width:'429.81px', height:'254px',marginLeft:'1px'}}/>
</PanelBox>
)
}
export default DrpReal;

View File

@ -0,0 +1,134 @@
import React, { useMemo } from 'react';
import echarts from 'echarts/lib/echarts';
import ReactEcharts from 'echarts-for-react';
const pallete = [
['#177ab3', '#51c3e7'],
['#9976dc', '#c792ee'],
['#94a1eb', '#a7caf8'],
['#7ae5c3', '#c9f4ea'],
['#c7dca5', '#f5fcd5'],
['#7988d9', '#9dc6f1'],
['#d9ed8f', '#d3f89b'],
];
const palleteLen = pallete.length;
const AreaDrpChart = ({ data }) => {
const { max, sdata } = useMemo(() => {
let max = 0;
data.forEach(o => {
max = Math.max(max, o.av)
});
max = [10, 20, 50, 100, 200, 300, 400].find(i => i >= max);
return {
max,
sdata: data.map(o => ({ ...o, name: o.NAME.split('').join('\n') })),
}
}, [data]);
var option = {
tooltip: {
trigger: 'item',
formatter: function (params) {
var res = `供水覆盖率:${params.data}%`;
return res;
}
},
grid: {
x: 28,
y: 24,
x2: 18,
y2: 36,
borderWidth: 0
},
calculable: true,
xAxis: [
{
type: 'category',
data: ['中馆驿镇', '宋埠镇', '歧亭镇', '白果镇', '夫子河镇', '阎家河镇', '龟山镇', '盐田河镇', '张家畈镇', '木子店镇', '三河口镇', '黄土岗镇', '福田河镇', '乘马岗镇', '顺河镇'],
splitLine: {
show: false
},
axisLabel: {
color: '#bbb',
fontSize: 10,
textShadowBlur: 4,
textShadowColor: '#6ab',
interval: 0,
rotate: 35,
},
axisLine: {
lineStyle: {
color: '#07a6ff',
width: 0.5,
}
},
axisTick: {
show: false,
}
}
],
yAxis: [
{
type: 'value',
position: 'left',
splitLine: {
show: true,
lineStyle: {
color: '#07a6ff',
width: 0.25,
type: 'dashed'
}
},
axisLabel: {
color: '#bbb',
fontSize: 10,
textShadowBlur: 4,
textShadowColor: '#6ab',
},
axisLine: {
show: false
},
axisTick: {
show: false,
},
min: 0,
max: 100,
}
],
series: [
{
name: '覆盖率',
type: 'bar',
barWidth: '60%',
data: [82.3, 80.4, 81.8, 72, 82.2, 82.4, 80.4, 80.8, 82.3, 72.3, 80.1, 79.4, 81.6, 80.4, 80],
itemStyle: {
normal: {
color: '#3b84bf'
}
},
label: {
show: true,
position: 'top',
color: '#bbb',
fontSize: 12,
textShadowBlur: 4,
textShadowColor: '#6ab',
},
}
]
};
return (
<ReactEcharts
option={option}
style={{ height: '98%', width: '100%' }}
/>
)
}
export default React.memo(AreaDrpChart);

View File

@ -0,0 +1,61 @@
import clsx from 'clsx';
import React, { useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { ZhenDataPromise } from '../../../../models/_/adcd';
import { DrpRealPromise } from '../../../../models/_/real';
import useRefresh from '../../../../utils/useRefresh';
import useRequest from '../../../../utils/useRequest';
import PanelBox from '../../components/PanelBox';
import AreaDrpChart from './chart';
export default function AreaDrp({ style }) {
const t = useRefresh(60 * 1000);
const { data: adcdList } = useRequest(ZhenDataPromise.get);
const { data: drpList } = useRequest(DrpRealPromise.get, t);
const areaDrpTmType = useSelector(s => s.overallview.areaDrpTmType);
const dispatch = useDispatch();
const data = useMemo(() => {
if (!adcdList) {
return [];
}
const resMap = {};
adcdList.forEach(({ ADCD, NAME }) => {
resMap[ADCD.substr(0, 9)] = {
ADCD,
NAME,
drp: 0,
cnt: 0,
av: 0,
}
});
drpList?.forEach(o => {
const adcd = o?.adcd?.substr(0, 9);
if (resMap[adcd]) {
resMap[adcd].drp += o[areaDrpTmType] || 0;
resMap[adcd].cnt += 1;
}
});
const ret = Object.values(resMap);
ret.forEach(o => {
if (o.cnt) {
o.av = parseFloat((o.drp / o.cnt).toFixed(1));
}
})
return Object.values(resMap);
}, [adcdList, drpList, areaDrpTmType]);
return (
<PanelBox
style={style}
title="供水覆盖率"
color="blue"
>
<AreaDrpChart data={data} />
</PanelBox>
)
}

View File

@ -0,0 +1,77 @@
import React from 'react';
import Dialog from '@material-ui/core/Dialog';
import DialogContent from '@material-ui/core/DialogContent';
import DpPaperComponent from '../../../../layouts/mui/DpPaperCompanent';
import { FormControl, FormGroup, InputLabel, MenuItem, Select, Switch, Typography } from '@material-ui/core';
import DpDialogTitle from '../../../../layouts/mui/DpDialogTitle';
import { useDispatch, useSelector } from 'react-redux';
import { getLayerSetting, getLayerVisible } from '../../../../models/map/selectors';
import DpBackgroundDrop from '../../../../layouts/mui/DpBackdrop';
function Setting({ onClose }) {
const layerVisible = useSelector(getLayerVisible);
const layerSetting = useSelector(getLayerSetting);
const tableSkDrpField = useSelector(s => s.realview.tableSkDrpField);
const dispath = useDispatch();
return (
<Dialog
open={true}
onClose={onClose}
maxWidth="xl"
style={{ borderRadius: 0 }}
PaperComponent={DpPaperComponent}
BackdropComponent={DpBackgroundDrop}
>
<div className="boxhead"></div>
<DpDialogTitle>水库显示设置</DpDialogTitle>
<DialogContent>
<div style={{ width: 320, padding: '1rem 0' }}>
<FormGroup>
<div style={{ marginBottom: '2rem' }}>
<Typography variant="subtitle2">水库表格面板实时雨量显示雨量时段</Typography>
<Select
style={{ fontSize: '1.2rem' }}
fullWidth
value={tableSkDrpField}
onChange={(event) => dispath.realview.setTableSkDrpField(event.target.value)}
>
<MenuItem value="h1">小时雨量</MenuItem>
<MenuItem value="h3">3小时雨量</MenuItem>
<MenuItem value="h6">6小时雨量</MenuItem>
<MenuItem value="h12">12小时雨量</MenuItem>
<MenuItem value="h24">24小时雨量</MenuItem>
<MenuItem value="h48">48小时雨量</MenuItem>
</Select>
</div>
<div style={{ marginBottom: '1rem' }}>
<Typography variant="subtitle2">显示水库图层</Typography>
<Switch
checked={!!layerVisible.RealSkLayer}
color="primary"
edge="start"
onChange={(e) => dispath.map.setLayerVisible({ RealSkLayer: e.target.checked })}
/>
</div>
<div>
<Typography variant="subtitle2">隐藏未超汛限水库图标注记</Typography>
<Switch
checked={!!layerSetting.skNormalLabelInvisible}
color="primary"
edge="start"
onChange={(e) => dispath.map.setLayerSetting({ skNormalLabelInvisible: e.target.checked })}
/>
</div>
</FormGroup>
</div>
</DialogContent>
<div className="boxfoot"></div>
</Dialog>
)
}
export default Setting;

View File

@ -0,0 +1,143 @@
import React, { useMemo, useState } from 'react';
import useRequest from '../../../../utils/useRequest';
import PanelBox from '../../components/PanelBox';
import HighlightOff from '@material-ui/icons/HighlightOff';
import { makeStyles } from '@material-ui/core/styles';
import Modal from '@material-ui/core/Modal';
import Backdrop from '@material-ui/core/Backdrop';
import Fade from '@material-ui/core/Fade';
import Table from '@material-ui/core/Table';
import TableContainer from '@material-ui/core/TableContainer';
import TableBody from '@material-ui/core/TableBody';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import DpTableCell from '../../../../layouts/mui/DpTableCell';
import DpTableRow from '../../../../layouts/mui/DpTableRow';
import { useDispatch, useSelector } from 'react-redux';
import useRefresh from '../../../../utils/useRefresh';
import { SkRealPromise } from '../../../../models/_/real';
import Setting from './Setting';
import appconsts from '../../../../models/appconsts';
import { renderDrp, renderSkArz, renderSkRz } from '../../../../utils/renutils';
import { InfoPopNames } from '../../InfoPops';
import config from '../../../../config';
import SkPicReal from '../../../Mgr/xqjs/SkPicReal';
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: "80%",
marginLeft: "10%",
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '1px solid rgba(41, 182, 246, 0.4)',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
width: "100%",
},
}));
function SkReal({ style }) {
const dispatch = useDispatch();
const skAutoRefresh = useSelector(s => s.realview.skAutoRefresh);
const t = useRefresh(skAutoRefresh ? 60 * 1000 : 0);
let { data } = useRequest(SkRealPromise.get, t);
const sortedData = useMemo(() => (data || []).sort((a, b) => (b.aRz - a.aRz), [data]));
const [setting, showSetting] = useState(false);
const tableSkDrpField = useSelector(s => s.realview.tableSkDrpField);
data = data || [];
const flyTo = (record) => {
const { lgtd, lttd } = record;
if (lgtd && lttd) {
dispatch.runtime.setFeaturePop({ type: InfoPopNames.RealSkPop, properties: record, coordinates: [lgtd, lttd] });
dispatch.runtime.setCameraTarget({
center: [lgtd, lttd + config.poiPositionOffsetY.sk],
zoom: config.poiPositionZoom.sk,
pitch: config.poiPitch,
bearing: 0,
});
}
}
const toggleAutoRefresh = () => {
dispatch.realview.setSkAutoRefresh(!skAutoRefresh);
}
const classes = useStyles();
const [open, setOpen] = React.useState(false);
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<PanelBox
style={style}
title="供水监控"
color="green"
>
<TableContainer style={{ height: '100%' }}>
<Table size="small" stickyHeader>
<TableHead>
<TableRow>
<DpTableCell style={{ maxWidth: '20%' }} align="left">名称</DpTableCell>
<DpTableCell align="right">漏损区域</DpTableCell>
<DpTableCell align="right">漏损率</DpTableCell>
<DpTableCell align="right">报警时间</DpTableCell>
</TableRow>
</TableHead>
<TableBody>
{sortedData.map((row) => (
<DpTableRow key={row.stcd}>
<DpTableCell component="th" scope="row">
<div className="table-ellipsis cursor-pointer" onClick={() => flyTo(row)}>{row.stnm}</div>
</DpTableCell>
<DpTableCell align="right">{renderDrp(row, tableSkDrpField)}</DpTableCell>
<DpTableCell align="right">{renderSkRz(row)}</DpTableCell>
<DpTableCell align="right">{renderSkArz(row)}</DpTableCell>
</DpTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{
setting && <Setting onClose={() => showSetting(false)} />
}
{/*水库图片弹窗*/}
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<HighlightOff style={{ color: "#fff", position: "absolute", right: "1rem", fontSize: "2rem", cursor: "pointer" }} onClick={handleClose} />
<SkPicReal />
</div>
</Fade>
</Modal>
</PanelBox>
)
}
export default React.memo(SkReal);

View File

@ -0,0 +1,17 @@
import React, { useMemo, useState } from 'react';
import PanelBox from '../../components/PanelBox';
function DrpReal({ style }) {
return (
<PanelBox
style={style}
title="供水态势"
color="green"
>
<img src='/assets/gongshui.jpg' style={{width:'429.81px', height:'212px',marginLeft:'1px'}}/>
</PanelBox>
)
}
export default DrpReal;

View File

@ -0,0 +1,208 @@
import React, { useMemo, useState } from 'react';
import echarts from 'echarts/lib/echarts';
import ReactEcharts from 'echarts-for-react';
import { hdyjColor } from '../../../../utils/renutils';
const Hd24H = ({ data, st}) => {
const option = useMemo(() => {
const serialData = [
[
"2025-05-21 15:00:00",
133.58
],
[
"2025-05-21 17:00:00",
133.58
],
[
"2025-05-21 18:00:00",
133.58
],
[
"2025-05-21 19:00:00",
133.56
],
[
"2025-05-21 20:00:00",
133.6
],
[
"2025-05-21 21:00:00",
133.59
],
[
"2025-05-21 22:00:00",
133.6
],
[
"2025-05-22 02:00:00",
133.63
],
[
"2025-05-22 03:00:00",
133.64
],
[
"2025-05-22 04:00:00",
133.65
],
[
"2025-05-22 05:00:00",
133.65
],
[
"2025-05-22 06:00:00",
133.66
],
[
"2025-05-22 07:00:00",
133.66
],
[
"2025-05-22 09:00:00",
133.67
],
[
"2025-05-22 11:00:00",
133.68
],
[
"2025-05-22 12:00:00",
133.68
]
];
return {
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#fff'
}
}
},
grid: {
x: 24,
y: 24,
x2: 38,
y2: 32,
borderWidth: 0
},
calculable: true,
xAxis: [
{
type: 'time',
splitLine: {
show: false
},
axisLabel: {
color: '#bbb',
fontSize: 9,
textShadowBlur: 4,
textShadowColor: '#6ab',
// formatter: val => val.substr('2020-10-14 '.length, 2)
},
axisLine: {
lineStyle: {
color: '#07a6ff',
width: 0.5,
}
},
axisTick: {
show: false,
}
}
],
yAxis: [
{
type: 'value',
position: 'right',
splitLine: {
show: true,
lineStyle: {
color: '#07a6ff',
width: 0.25,
type: 'dashed'
}
},
axisLabel: {
color: '#bbb',
fontSize: 10,
textShadowBlur: 4,
textShadowColor: '#6ab',
},
axisLine: {
show: false
},
axisTick: {
show: false,
},
min: 133,
max: 134,
}
],
series: [
{
name: '供水量',
type: 'line',
showSymbol: false,
label: {
show: false,
},
data: serialData,
lineStyle: {
normal: {
width: 1,
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(3, 194, 236, 0.3)'
}, {
offset: 0.8,
color: 'rgba(3, 194, 236, 0)'
}
], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: '#03C2EC'
}
},
markPoint: {
data: [
{ type: 'max', name: '最大值', symbol: 'circle', symbolSize: 1, symbolOffset: [0, -12] },
{ type: 'min', name: '最小值', symbol: 'circle', symbolSize: 1, symbolOffset: [0, 12] }
]
},
// markLine: {
// silent: true,
// symbol: 'none',
// label: {
// position: 'start',
// formatter: (p) => p?.name + ' ' + p?.value,
// },
// data: markLine
// }
}
]
};
}, [data, st]);
return (
<ReactEcharts
option={option}
style={{ height: '100%', width: '100%', overflow: 'hidden' }}
/>
)
}
export default React.memo(Hd24H);

View File

@ -0,0 +1,41 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { OverallPromise } from '../../../../models/_/real';
import moment from 'moment';
import { normalizeSearchTmRange } from '../../../../utils/tools';
import useRequest from '../../../../utils/useRequest';
import PanelBox from '../../components/PanelBox';
import Chart from './chart'
import useRefresh from '../../../../utils/useRefresh';
import { rzSearch } from '../../../../models/_/search';
import clsx from 'clsx';
export default function HD24H({ style }) {
const t = useRefresh(60 * 1000);
const activeRzSt = useSelector(s => s.overallview.activeRzSt);
const { data } = useRequest(OverallPromise.get, t);
const st = data?.crucialRz?.find(o => o.stcd === activeRzSt);
const { data: data24h } = useRequest(async () => {
if (!st) {
return [];
}
const tm = [moment().add(-24, 'hour'), moment()];
const resultTm = normalizeSearchTmRange(tm, 'h');
const ret = await rzSearch(st.type, st.stcd, 'h', resultTm) || [];
return ret;
}, st);
const dispatch = useDispatch();
return (
<PanelBox
style={style}
title="日供水量趋势"
color="green"
>
<Chart />
</PanelBox>
)
}