diff --git a/public/assets/duibifenxi.png b/public/assets/duibifenxi.png new file mode 100644 index 0000000..465ceb5 Binary files /dev/null and b/public/assets/duibifenxi.png differ diff --git a/public/assets/gongshui.jpg b/public/assets/gongshui.jpg new file mode 100644 index 0000000..18e9eb2 Binary files /dev/null and b/public/assets/gongshui.jpg differ diff --git a/src/models/map/index.js b/src/models/map/index.js index b1e05ec..25f5f92 100644 --- a/src/models/map/index.js +++ b/src/models/map/index.js @@ -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 }) diff --git a/src/models/map/layout.js b/src/models/map/layout.js index 9275231..62a5b69 100644 --- a/src/models/map/layout.js +++ b/src/models/map/layout.js @@ -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 } }, @@ -203,6 +210,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 } }, diff --git a/src/views/Home/PanelIndex.js b/src/views/Home/PanelIndex.js index 0aef001..129a099 100644 --- a/src/views/Home/PanelIndex.js +++ b/src/views/Home/PanelIndex.js @@ -47,7 +47,11 @@ import Szwtqytj from './panels/Szwtqytj' import Spjc from './panels/Spjc' import Skgl from './panels/Skgl' import Fxdd from './panels/Fxdd' - +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 === '天气') { @@ -146,7 +150,18 @@ export default function PanelIndex({ name, style, ...params }) { return } else if (name === '防汛调度') { return + } else if (name === '供水态势') { + return + } else if (name === '供水覆盖率') { + return + } else if (name === '日供水量') { + return + } else if (name === '对比分析') { + return + } else if (name === '供水监控') { + return } + return (

not impl

diff --git a/src/views/Home/components/ActionDock/index.js b/src/views/Home/components/ActionDock/index.js index c3ab882..842f6c7 100644 --- a/src/views/Home/components/ActionDock/index.js +++ b/src/views/Home/components/ActionDock/index.js @@ -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' }, diff --git a/src/views/Home/panels/Dbfx/index.js b/src/views/Home/panels/Dbfx/index.js new file mode 100644 index 0000000..2ea02f1 --- /dev/null +++ b/src/views/Home/panels/Dbfx/index.js @@ -0,0 +1,17 @@ +import React, { useMemo, useState } from 'react'; +import PanelBox from '../../components/PanelBox'; + +function DrpReal({ style }) { + + return ( + + + + ) +} + +export default DrpReal; diff --git a/src/views/Home/panels/Gsfgl/chart.js b/src/views/Home/panels/Gsfgl/chart.js new file mode 100644 index 0000000..240749e --- /dev/null +++ b/src/views/Home/panels/Gsfgl/chart.js @@ -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 ( + + ) +} + +export default React.memo(AreaDrpChart); diff --git a/src/views/Home/panels/Gsfgl/index.js b/src/views/Home/panels/Gsfgl/index.js new file mode 100644 index 0000000..5c4d8f4 --- /dev/null +++ b/src/views/Home/panels/Gsfgl/index.js @@ -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 ( + + + + ) +} diff --git a/src/views/Home/panels/Gsjk/Setting.js b/src/views/Home/panels/Gsjk/Setting.js new file mode 100644 index 0000000..419a37c --- /dev/null +++ b/src/views/Home/panels/Gsjk/Setting.js @@ -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 ( + +
+ + 水库显示设置 + +
+ +
+ 水库表格面板实时雨量显示雨量时段 + +
+
+ 显示水库图层 + dispath.map.setLayerVisible({ RealSkLayer: e.target.checked })} + /> +
+
+ 隐藏未超汛限水库图标注记 + dispath.map.setLayerSetting({ skNormalLabelInvisible: e.target.checked })} + /> +
+
+
+
+
+
+ ) +} + +export default Setting; diff --git a/src/views/Home/panels/Gsjk/index.js b/src/views/Home/panels/Gsjk/index.js new file mode 100644 index 0000000..1ec1a58 --- /dev/null +++ b/src/views/Home/panels/Gsjk/index.js @@ -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 ( + + + + + + 名称 + 漏损区域 + 漏损率 + 报警时间 + + + + {sortedData.map((row) => ( + + +
flyTo(row)}>{row.stnm}
+
+ {renderDrp(row, tableSkDrpField)} + {renderSkRz(row)} + {renderSkArz(row)} +
+ ))} +
+
+
+ { + setting && showSetting(false)} /> + } + + {/*水库图片弹窗*/} + + +
+ + +
+
+
+
+ ) +} + +export default React.memo(SkReal); diff --git a/src/views/Home/panels/Gsts/index.js b/src/views/Home/panels/Gsts/index.js new file mode 100644 index 0000000..f2bcfe1 --- /dev/null +++ b/src/views/Home/panels/Gsts/index.js @@ -0,0 +1,17 @@ +import React, { useMemo, useState } from 'react'; +import PanelBox from '../../components/PanelBox'; + +function DrpReal({ style }) { + + return ( + + + + ) +} + +export default DrpReal; diff --git a/src/views/Home/panels/Rgsl/chart.js b/src/views/Home/panels/Rgsl/chart.js new file mode 100644 index 0000000..86e6b32 --- /dev/null +++ b/src/views/Home/panels/Rgsl/chart.js @@ -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 ( + + ) +} + +export default React.memo(Hd24H); diff --git a/src/views/Home/panels/Rgsl/index.js b/src/views/Home/panels/Rgsl/index.js new file mode 100644 index 0000000..54d1b77 --- /dev/null +++ b/src/views/Home/panels/Rgsl/index.js @@ -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 ( + + + + ) +}