diff --git a/package.json b/package.json
index e2fc421..5e75836 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"crypto-js": "^4.1.1",
"echarts": "^4.9.0",
"echarts-for-react": "3.0.2",
+ "ezuikit-js": "8.0.4-beta.1",
"http-proxy-middleware": "^2.0.6",
"moment": "^2.29.4",
"react": "^18.2.0",
diff --git a/public/assets/images/blue2.png b/public/assets/images/blue2.png
new file mode 100644
index 0000000..a50c903
Binary files /dev/null and b/public/assets/images/blue2.png differ
diff --git a/public/assets/images/card/header.png b/public/assets/images/card/header.png
deleted file mode 100644
index 04aa698..0000000
Binary files a/public/assets/images/card/header.png and /dev/null differ
diff --git a/public/assets/images/card/smallCard.png b/public/assets/images/card/smallCard.png
deleted file mode 100644
index 01186e3..0000000
Binary files a/public/assets/images/card/smallCard.png and /dev/null differ
diff --git a/public/assets/images/card/textbg.png b/public/assets/images/card/textbg.png
deleted file mode 100644
index 2856b43..0000000
Binary files a/public/assets/images/card/textbg.png and /dev/null differ
diff --git a/public/assets/images/crt0.png b/public/assets/images/crt0.png
new file mode 100644
index 0000000..3b17f5a
Binary files /dev/null and b/public/assets/images/crt0.png differ
diff --git a/public/assets/images/crt1.png b/public/assets/images/crt1.png
new file mode 100644
index 0000000..99cd28e
Binary files /dev/null and b/public/assets/images/crt1.png differ
diff --git a/public/assets/images/crt2.png b/public/assets/images/crt2.png
new file mode 100644
index 0000000..120e1c8
Binary files /dev/null and b/public/assets/images/crt2.png differ
diff --git a/public/assets/images/crt3.png b/public/assets/images/crt3.png
new file mode 100644
index 0000000..6cd6f7b
Binary files /dev/null and b/public/assets/images/crt3.png differ
diff --git a/public/assets/images/crt4.png b/public/assets/images/crt4.png
new file mode 100644
index 0000000..d39f155
Binary files /dev/null and b/public/assets/images/crt4.png differ
diff --git a/public/assets/images/crt5.png b/public/assets/images/crt5.png
new file mode 100644
index 0000000..f600f78
Binary files /dev/null and b/public/assets/images/crt5.png differ
diff --git a/public/assets/images/crt6.png b/public/assets/images/crt6.png
new file mode 100644
index 0000000..d96e2e3
Binary files /dev/null and b/public/assets/images/crt6.png differ
diff --git a/public/assets/images/crt7.png b/public/assets/images/crt7.png
new file mode 100644
index 0000000..03106cc
Binary files /dev/null and b/public/assets/images/crt7.png differ
diff --git a/public/assets/images/crt8.png b/public/assets/images/crt8.png
new file mode 100644
index 0000000..dec5b25
Binary files /dev/null and b/public/assets/images/crt8.png differ
diff --git a/public/assets/images/dam.png b/public/assets/images/dam.png
new file mode 100644
index 0000000..1efe721
Binary files /dev/null and b/public/assets/images/dam.png differ
diff --git a/public/assets/images/fangda.png b/public/assets/images/fangda.png
new file mode 100644
index 0000000..a45cbdf
Binary files /dev/null and b/public/assets/images/fangda.png differ
diff --git a/public/assets/images/head/header.png b/public/assets/images/head/header.png
deleted file mode 100644
index ca5c339..0000000
Binary files a/public/assets/images/head/header.png and /dev/null differ
diff --git a/public/assets/images/head/menu1.png b/public/assets/images/head/menu1.png
deleted file mode 100644
index aee3475..0000000
Binary files a/public/assets/images/head/menu1.png and /dev/null differ
diff --git a/public/assets/images/head/menu2.png b/public/assets/images/head/menu2.png
deleted file mode 100644
index 10eb348..0000000
Binary files a/public/assets/images/head/menu2.png and /dev/null differ
diff --git a/public/assets/images/head/time.png b/public/assets/images/head/time.png
deleted file mode 100644
index 67aa348..0000000
Binary files a/public/assets/images/head/time.png and /dev/null differ
diff --git a/public/assets/images/head/user.png b/public/assets/images/head/user.png
deleted file mode 100644
index 9a3ed12..0000000
Binary files a/public/assets/images/head/user.png and /dev/null differ
diff --git a/public/assets/images/jk.png b/public/assets/images/jk.png
new file mode 100644
index 0000000..cf1e22b
Binary files /dev/null and b/public/assets/images/jk.png differ
diff --git a/public/assets/images/no-video.png b/public/assets/images/no-video.png
new file mode 100644
index 0000000..a7bf846
Binary files /dev/null and b/public/assets/images/no-video.png differ
diff --git a/public/assets/images/orange.png b/public/assets/images/orange.png
new file mode 100644
index 0000000..fb77183
Binary files /dev/null and b/public/assets/images/orange.png differ
diff --git a/public/assets/images/qiujiG.png b/public/assets/images/qiujiG.png
new file mode 100644
index 0000000..9447f03
Binary files /dev/null and b/public/assets/images/qiujiG.png differ
diff --git a/public/assets/images/red1.png b/public/assets/images/red1.png
new file mode 100644
index 0000000..fd79cd9
Binary files /dev/null and b/public/assets/images/red1.png differ
diff --git a/public/assets/images/ruler.png b/public/assets/images/ruler.png
new file mode 100644
index 0000000..2cf88a1
Binary files /dev/null and b/public/assets/images/ruler.png differ
diff --git a/public/assets/images/suoxiao.png b/public/assets/images/suoxiao.png
new file mode 100644
index 0000000..03d4bde
Binary files /dev/null and b/public/assets/images/suoxiao.png differ
diff --git a/public/assets/images/waterz.png b/public/assets/images/waterz.png
new file mode 100644
index 0000000..67635c2
Binary files /dev/null and b/public/assets/images/waterz.png differ
diff --git a/public/assets/images/white1.png b/public/assets/images/white1.png
new file mode 100644
index 0000000..b006ca3
Binary files /dev/null and b/public/assets/images/white1.png differ
diff --git a/src/components/VideoCom/VideoControler/index.js b/src/components/VideoCom/VideoControler/index.js
new file mode 100644
index 0000000..74f936f
--- /dev/null
+++ b/src/components/VideoCom/VideoControler/index.js
@@ -0,0 +1,76 @@
+import React,{useEffect,useState} from 'react'
+import {Slider} from "antd"
+import './index.less'
+export default function VideoControler({ selectItem = {},onOperation }) {
+ const ctrObj = {
+ 1: {name:"左上",value:'LEFT_UP'},
+ 2: {name:"上",value:'UP'},
+ 3: {name:"右上",value:'RIGHT_UP'},
+ 4: {name:"左",value:'LEFT'},
+ 5: {name:"重置",value:'GOTO_PRESET'},
+ 6: {name:"右",value:'RIGHT'},
+ 7: {name:"左下",value:'LEFT_DOWN'},
+ 8: {name:"下",value:'DOWN'},
+ 9: {name:"右下",value:'RIGHT_DOWN'},
+ }
+ const ctrBtnArr = Array(9).fill(0).map((item, index) => ({
+ title: ctrObj[index + 1]?.name,
+ value:ctrObj[index + 1]?.value
+ }))
+ const marks = {
+ 10: "1",
+ 30: "3",
+ 80: "8",
+ }
+
+ const [selectData, setSelect] = useState()
+ useEffect(() => {
+ if (selectData) {
+ onOperation({...selectData,speed:selectData?.speed || 30})
+ }
+ }, [selectData])
+
+ return (
+
+
云台控制
+
+ 当前设备:
+ {selectItem?.name}
+
+ {/*
{selectItem?.name}
*/}
+
+
+ {ctrBtnArr.map((item, index) =>
+
setSelect({...selectData,command:item?.value})}>
+
+

+
+
+ )}
+
+
+
+ {setSelect({...selectData,speed:e})}}
+ />
+
+
+
setSelect({...selectData,command:"ZOOM_OUT"})}>
+

+
+
setSelect({...selectData,command:"ZOOM_IN"})}>
+

+
+
+
+
+
注:云台操作的生效根据网络状况延时约1-10秒
+
+
+ )
+}
diff --git a/src/components/VideoCom/VideoControler/index.less b/src/components/VideoCom/VideoControler/index.less
new file mode 100644
index 0000000..295297d
--- /dev/null
+++ b/src/components/VideoCom/VideoControler/index.less
@@ -0,0 +1,120 @@
+.controler-wrap{
+ width: 100%;
+ height: 265px;
+ padding-left: 5px;
+ color: #fff;
+ .title{
+ line-height: 50px;
+ font-size: 25px;
+ font-weight: 900;
+ color: #fff;
+ }
+ .sub-title{
+ font-size: 18px;
+ font-weight:800;
+ color: #fff;
+ }
+ .controler-box{
+ display: flex;
+ height: 170px;
+ padding: 10px 20px;
+ background-color: rgba(255, 255, 255, 0.05);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 4px;
+ flex-wrap: wrap;
+ .left{
+ width: 135px;
+ display: flex;
+ flex-wrap: wrap;
+ .ctr-btn{
+ background-color: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 5px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ -webkit-box-pack: center;
+ justify-content: center;
+ -webkit-box-align: center;
+ align-items: center;
+ cursor: pointer;
+ transition: all 0.3s;
+
+ &:hover {
+ background-color: rgba(24, 144, 255, 0.3);
+ border-color: #1890ff;
+ }
+
+ img{
+ width: 15px;
+ height: 15px;
+ filter: invert(1);
+ }
+ }
+ }
+ .right{
+ width:calc(100% - 135px);
+ .slider-wrap{
+ // padding: 10px;
+ height: 40px;
+ // margin-bottom: 5px;
+
+ .ant-slider-mark-text {
+ color: #fff !important ;
+ }
+ .ant-slider-rail {
+ background-color: rgba(255, 255, 255, 0.2);
+ }
+ .ant-slider-track {
+ background-color: #1890ff;
+ }
+ .ant-slider-handle {
+ border-color: #1890ff;
+ }
+
+ }
+ .zoom-wrap{
+ display: flex;
+ -webkit-box-pack: justify;
+ // justify-content: space-between;
+ width: 100%;
+ margin-top: -3px;
+ .zoom-btn{
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background-color: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 5px;
+ margin-right: 5px;
+ margin-bottom: 5px;
+ width: 100%;
+ height: 40px;
+ -webkit-box-pack: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s;
+
+ &:hover {
+ background-color: rgba(24, 144, 255, 0.3);
+ border-color: #1890ff;
+ }
+
+ img{
+ width: 25px;
+ height: 25px;
+ filter: invert(1);
+ }
+ }
+ }
+ }
+ .tips{
+ margin-top: 0;
+ margin-bottom: 1rem;
+ color: rgba(255, 255, 255);
+ font-size: 12px;
+ }
+ }
+}
diff --git a/src/components/VideoCom/videoPlary.js b/src/components/VideoCom/videoPlary.js
new file mode 100644
index 0000000..de3a642
--- /dev/null
+++ b/src/components/VideoCom/videoPlary.js
@@ -0,0 +1,243 @@
+import { FC, useEffect, useRef, useState } from 'react'
+// import styles from './index.module.less'
+import { message, Spin } from "antd";
+import EZUIKit from 'ezuikit-js';
+import moment from 'moment';
+
+/**
+ * 海康视频H5插件视频播放
+ * @author QC班长
+ * @since 20230727
+ */
+
+//wsUrl [{src:''}] playerId:string size:分屏播放1代表1*1,2代表2*2 最大4*4
+const HFivePlayer = ({ wsUrl, playerID, size }) => {
+ const [isLoading, setIsLoading] = useState(false)
+ const player = useRef({})//播放器
+ const playerYsy = useRef({})
+ const parentRef = useRef(null);//为了设置播放器宽高
+ const [width, setWidth] = useState(null);
+ const [height, setHeight] = useState(null);
+ const [type, setType] = useState(false);
+
+
+
+
+ const initVideo = () => {
+ const hasReplayRange = wsUrl?.beginTime && wsUrl?.endTime && wsUrl?.indexCode;
+ const beginStr = hasReplayRange ? moment(wsUrl.beginTime).format('YYYYMMDDHHmmss') : '';
+ const endStr = hasReplayRange ? moment(wsUrl.endTime).format('YYYYMMDDHHmmss') : '';
+ const ezUrl = hasReplayRange
+ ? `ezopen://open.ys7.com/${wsUrl.indexCode}/1.rec?begin=${beginStr}&end=${endStr}`
+ : `ezopen://open.ys7.com/${wsUrl.indexCode}/1.live`;
+ playerYsy.current = new EZUIKit.EZUIKitPlayer({
+ id: 'player' + playerID, // 视频容器ID
+ accessToken: wsUrl.src,
+ url: ezUrl,
+ // plugin: ["talk"], // 加载插件,talk-对讲
+ width: parentRef.current?.offsetWidth,
+ height: parentRef.current?.offsetHeight,
+ template: 'simple',
+ footer: ['hd', 'fullScreen'],
+ });
+ playerYsy.current?.play();
+ }
+
+ /**
+ * 初始化播放器
+ */
+ const initPlayer = () => {
+ console.log(document.getElementById('player' + playerID),'play.current');
+ // return
+ player.current = new window.JSPlugin({
+ // 需要英文字母开头 必填
+ szId: 'player' + playerID,
+ // 必填,引用H5player.min.js的js相对路径
+ szBasePath: '/h5Player/',
+ // 当容器div#play_window有固定宽高时,可不传iWidth和iHeight,窗口大小将自适应容器宽高
+ iWidth: parentRef.current?.offsetWidth,
+ iHeight: parentRef.current?.offsetHeight,
+ bSupporDoubleClickFull: true,
+ // 分屏播放,默认最大分屏4*4
+ iMaxSplit: 3,
+ // iCurrentSplit: 1,
+ // 样式
+ oStyle: {
+ border: 'rgb(53 116 237)',
+ borderSelect: '#1d325d',
+ background: '#1d325d',
+ }
+ })
+ // 设置播放容器的宽高并监听窗口大小变化
+ //初始化插件
+ initPlugin()
+ }
+
+ /**
+ * 事件初始化
+ */
+ const initPlugin = () => {
+
+ player.current.JS_SetWindowControlCallback({
+ windowEventSelect(iWindIndex) {
+ // 插件选中窗口回调
+ console.log('windowSelect callback: ', iWindIndex)
+ //点击视频全屏显示
+ // wholeFullScreen()
+ },
+ pluginErrorHandler(iWindIndex, iErrorCode, oError) {
+ // 插件错误回调
+ // console.error(`window-${iWindIndex}, errorCode: ${iErrorCode}`, oError)
+ // message.error('播放失败1:' + VideoPlayerException[iErrorCode])
+ setIsLoading(false)
+ //重新播放
+ // initPlayer()
+ },
+ windowEventOver(iWindIndex) {
+ // 鼠标移过回调
+ // console.log('鼠标移过回调', iWindIndex)
+ },
+ windowEventOut(iWindIndex) {
+ // 鼠标移出回调
+ // console.log('鼠标移出回调', iWindIndex)
+ },
+ windowFullScreenChange(bFull) {
+ // 全屏切换回调
+ // console.log('全屏切换回调', bFull)
+ },
+ firstFrameDisplay(iWndIndex, iWidth, iHeight) {
+ // 首帧显示回调
+ // console.log('首帧显示回调', iWndIndex, iWidth, iHeight)
+ //停止加载
+ setIsLoading(false)
+ },
+ performanceLack(iWndIndex) {
+ // 性能不足回调
+ console.log('性能不足回调', iWndIndex)
+ setIsLoading(false)
+
+ }
+ })
+ console.log(player, '成功');
+ //播放
+ play()
+ }
+
+ const handleWindowResize = () => {
+ console.log(111111);
+ if (parentRef.current) {
+ setWidth(parentRef.current?.offsetWidth)
+ setHeight(parentRef.current?.offsetHeight)
+ }
+ console.log(parentRef.current?.offsetHeight, parentRef.current?.offsetWidth);
+ }
+ /**
+ * 播放
+ */
+ const play = () => {
+ console.log(wsUrl?.src, '6543');
+ // if(!wsUrl?.src) return
+ if (wsUrl?.src) {
+ setIsLoading(true) //开始加载
+ let preUrl = wsUrl?.src // 播放地址
+ // 支持回放时间范围(海康取流地址若后端支持时间参数则追加)
+ if (wsUrl?.beginTime && wsUrl?.endTime) {
+ const begin = moment(wsUrl.beginTime).subtract(1,'hours').format('YYYY-MM-DD HH:mm:ss');
+ const end = moment(wsUrl.endTime).format('YYYY-MM-DD HH:mm:ss');
+ const sep = preUrl.includes('?') ? '&' : '?';
+ preUrl = `${preUrl}${sep}beginTime=${begin}&endTime=${end}`;
+ }
+ console.log(preUrl);
+ const param = {
+ playURL: preUrl,
+ // 1:高级模式 0:普通模式,高级模式支持所有
+ mode: 0
+ }
+ // 当前播放窗口下标
+ // let index = 0
+ player.current.JS_Play(preUrl, param, 0).then(() => {
+ // 播放成功回调
+ // console.log('播放成功')
+ }, (err) => {
+ // console.log('播放失败')
+ // console.info('JS_Play failed:', err)
+ setIsLoading(false)
+ message.error('视频离线,播放失败')
+ }
+ )
+
+ }
+ }
+
+ useEffect(() => {
+ if (wsUrl) {
+ if (wsUrl?.relType == 'ysy') {
+ initVideo()
+ } else {
+ initPlayer()
+ }
+
+ }
+
+ }, [wsUrl])
+ useEffect(() => {
+ handleWindowResize()
+ }, [size])
+
+ const styles = {}
+ return (
+
+
+ playerYsy.current.fullScreen()}>
+
+
+
+
+
+
+ )
+}
+export default HFivePlayer
+
+/**
+ * 海康威视视频播放异常错误代码常量
+ */
+export const VideoPlayerException = {
+ '0x12f900001': '接口调用参数错误',
+ '0x12f900002': '不在播放状态',
+ '0x12f900003': '仅回放支持该功能',
+ '0x12f900004': '普通模式不支持该功能',
+ '0x12f900005': '高级模式不支持该功能',
+ '0x12f900006': '高级模式的解码库加载失败',
+ '0x12f900008': 'url格式错误',
+ '0x12f900009': '取流超时错误',
+ '0x12f900010': '设置或者是获取音量失败,因为没有开启音频的窗口',
+ '0x12f900011': '设置的音量不在1-100范围',
+ '0x12f910000': 'websocket连接失败,请检查网络是否通畅,URL是否正确',
+ '0x12f910010': '取流失败',
+ '0x12f910011': '流中断,电脑配置过低,程序卡主线程都可能导致流中断',
+ '0x12f910014': '没有音频数据',
+ '0x12f910015': '未找到对应websocket,取流套接字被动关闭的报错',
+ '0x12f910016': 'websocket不在连接状态',
+ '0x12f910017': '不支持智能信息展示',
+ '0x12f910018': 'websocket长时间未收到message',
+ '0x12f910019': 'wss连接失败,原因:端口尚未开通、证书未安装、证书不安全',
+ '0x12f910020': '单帧回放时不能暂停',
+ '0x12f910021': '已是最大倍速',
+ '0x12f910022': '已是最小倍速',
+ '0x12f910023': 'ws/wss连接超时,默认6s超时时间,原因:网络异常,网络不通',
+ '0x12f910026': 'jsdecoder1.0解码报错视频编码格式不支持',
+ '0x12f910027': '后端取流超时,主动关闭连接(设备突然离线或重启,网络传输超时20s)',
+ '0x12f910028': '设置的缓冲区大小无效,大小0-510241024,不在该范围的报错',
+ '0x12f910029': '普通模式的报错,码流异常导致黑屏,尝试重新取流',
+ '0x12f910031': '普通模式下播放卡主会出现',
+ '0x12f910032': '码流编码格式普通模式下不支持,可切换高级模式尝试播放',
+ '0x12f920015': '未调用停止录像,再次调用开始录像',
+ '0x12f920016': '未开启录像调用停止录像接口错误',
+ '0x12f920017': '紧急录像目标格式不支持,非ps/mp4',
+ '0x12f920018': '紧急录像文件名为null',
+ '0x12f930010': '内存不足',
+ '0x12f930011': '首帧显示之前无法抓图,请稍后重试',
+ '0x12f950000': '采集音频失败,可能是在非https域下使用对讲导致',
+ '0x12f950001': '对讲不支持这种音频编码格式',
+}
\ No newline at end of file
diff --git a/src/service/apiurl.js b/src/service/apiurl.js
index 5eec320..c847038 100644
--- a/src/service/apiurl.js
+++ b/src/service/apiurl.js
@@ -10,6 +10,20 @@ const apiurl = {
station: {
rainlist: service + '/real/rain/list',//雨量站
reservoirlist: service + '/reservoir/water/listV2',//水库水位站
+ flowlist: service + '/stFlowR/list',
+ },
+
+ spjk: {
+ page1: service + "/iscaiEvent/page",
+ page: service + "/stImgWarnR/page",
+ list: service + "/attCctvBase/list",
+ controler: service + "/attCctvBase/control",
+ treeList: service + '/cctvBMenu/list',
+ treeListById: service + '/xfCctvB/listByMenuId/',
+ srcData: service + '/attCctvBase/preview/',
+ videoBystcd: service + '/stbprp/cctv/listByStcd/',
+ videoList: service + '/attCctvBase/list',
+ ysyToken: service + '/ysy/getAccessToken'
},
sq: {
qfg: {
@@ -36,7 +50,13 @@ const apiurl = {
nearbyHistory:service + '/attResBase/maxRain' //获取历史近几小时数据
},
reservoir: {
- list:service + '/screen/monitoring/rsvr'
+ list: service + '/screen/monitoring/rsvr',
+ monitor: service + '/reservoir/water/monitor/data',
+ detail:service + '/reservoir/water/detail'
+ },
+ flow: {
+ history: service + '/stFlowR/upperDataCheck',
+ max:service + '/stFlowR/lowerDataCheck'
}
},
qzq: {
diff --git a/src/service/station.js b/src/service/station.js
index 6bbe8bf..6c6e20c 100644
--- a/src/service/station.js
+++ b/src/service/station.js
@@ -33,5 +33,22 @@ export async function reservoirlist(params) {
lttd : Number(i.lttd)-1.2,
}))
+ return mapData||[];
+}
+
+// 流量站
+export async function flowlist(params) {
+ const {data, code, msg} = await httpget(apiurl.station.flowlist, params) || {};
+ if (code !== 200) {
+ message.error(msg || '请求失败');
+ return [];
+ }
+
+ const mapData = data.map(i=>({
+ ...i,
+ lgtd : Number(i.lgtd)-1,
+ lttd : Number(i.lttd)-1.2,
+ }))
+
return mapData||[];
}
\ No newline at end of file
diff --git a/src/views/Home/components/Business/SiQuan/components/AllWeatherControl/index.js b/src/views/Home/components/Business/SiQuan/components/AllWeatherControl/index.js
index 25293b9..1b88ed4 100644
--- a/src/views/Home/components/Business/SiQuan/components/AllWeatherControl/index.js
+++ b/src/views/Home/components/Business/SiQuan/components/AllWeatherControl/index.js
@@ -8,12 +8,28 @@ import apiurl from '@/service/apiurl';
import { httpget, httppost } from '@/utils/request';
import './index.less';
import RightPanel from '../ModalComponents/AllWeatherModal/RainMonitor/RightPanel';
+import ReservoirPanel from '../ModalComponents/AllWeatherModal/ReservoirPanel';
+import VideoList from '../ModalComponents/VideoList'
+import UAVModal from '../ModalComponents/UAVModal';
const AllWeatherControl = () => {
const [reservoirItem, setReservoirItem] = useState({})
const [rainList, setRainList] = useState([])
const [detailVisible, setDetailVisible] = useState(false)
+ const [reservoirVisible, setReservoirVisible] = useState(false)
+ const [videoOpen, setVideoOpen] = useState(false)
const [selectedStcd, setSelectedStcd] = useState(null)
+
+ // UAV Modal States
+ const [uavModalVisible, setUavModalVisible] = useState(false);
+ const [uavActiveTab, setUavActiveTab] = useState('1');
+
+ const uavTabs = [
+ { label: '直播画面', value: '1' },
+ { label: '飞行任务', value: '2' },
+ { label: '历史记录', value: '3' }
+ ];
+
const rainColumns = [
{
title: '站名',
@@ -46,10 +62,10 @@ const AllWeatherControl = () => {
// Reservoir Data
const reservoirData = [
- { label: '主坝坝前', value: reservoirItem?.rz, unit: 'm', upArrow: reservoirItem?.status > 0, underline: true,downArrow:reservoirItem?.status < 0 },
+ { label: '主坝坝前', value: reservoirItem?.rz, unit: 'm', upArrow: reservoirItem?.status > 0, underline: true,downArrow:reservoirItem?.status < 0, clickable: true },
{ label: '汛限水位', value: reservoirItem?.flLowLimLev, unit: 'm' },
{ label: '距汛限', value: reservoirItem?.gapFlLowLimLev, unit: 'm', isNegative: reservoirItem?.gapFlLowLimLev < 0 },
- { label: '副坝坝前', value: reservoirItem?.rz, unit: 'm' },
+ { label: '副坝坝前', value: reservoirItem?.rz, unit: 'm', clickable: true,underline: true },
{ label: '当前库容', value: reservoirItem?.nowCap, unit: '万m³' },
{ label: '有效库容', value: reservoirItem?.effectiveCap, unit: '万m³' },
];
@@ -80,6 +96,11 @@ const AllWeatherControl = () => {
getReservoir()
}, [])
+ const openUavModal = (key) => {
+ setUavActiveTab(key);
+ setUavModalVisible(true);
+ }
+
return (
{/* 雨情 Section */}
@@ -111,7 +132,6 @@ const AllWeatherControl = () => {
visible={detailVisible}
onClose={() => setDetailVisible(false)}
width={'70%'}
- bodyStyle={{ background: 'transparent', padding: 12 }}
>
@@ -130,7 +150,12 @@ const AllWeatherControl = () => {
{
+ if (item.clickable) {
+ setReservoirVisible(true)
+ }
+ }}
>
{item.value}
@@ -142,6 +167,14 @@ const AllWeatherControl = () => {
))}
+
setReservoirVisible(false)}
+ width={'90%'}
+ >
+
+
{/* 无人机 Section */}
@@ -151,7 +184,7 @@ const AllWeatherControl = () => {
无人机
- 视频墙
+ setVideoOpen(true)}>视频墙
{
style={{ backgroundImage: `url(${wrj})` }}
/>
-
直播画面
-
巡查任务
+
openUavModal('1')}>直播画面
+
openUavModal('2')}>巡查任务
+
setVideoOpen(false)}
+ width={'80%'}
+ >
+
+
+
setUavModalVisible(false)}
+ width={'70%'}
+ tabs={uavTabs}
+ activeTab={uavActiveTab}
+ onTabChange={setUavActiveTab}
+ >
+
+
);
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/RightPanel.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/RightPanel.js
new file mode 100644
index 0000000..64af747
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/RightPanel.js
@@ -0,0 +1,135 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { DatePicker, Button, Table } from 'antd';
+import { SearchOutlined } from '@ant-design/icons';
+import ReactEcharts from 'echarts-for-react';
+import apiurl from '@/service/apiurl';
+import { httppost } from '@/utils/request';
+import moment from 'moment';
+import flowOption from './chartOption';
+import './index.less';
+
+const { RangePicker } = DatePicker;
+
+export default function RightPanel({ stcd, cleanMode = false }) {
+ const defaultRange = [
+ moment().subtract(7, 'days').set({ minute: 0, second: 0 }),
+ moment().set({ minute: 0, second: 0 }),
+ ];
+ const [dates, setDates] = useState(defaultRange);
+ const [historyList, setHistoryList] = useState([]);
+ const [maxInfo, setMaxInfo] = useState({})
+ const [loading, setLoading] = useState(false);
+
+ const columns = [
+ {
+ title: '时间', dataIndex: 'tm', key: 'tm', align: 'center', width: 160,
+ render: (text) => moment(text).format('YYYY-MM-DD HH:mm')
+ },
+ {
+ title: '瞬时流量(m³/s)', dataIndex: 'q', key: 'q', align: 'center',
+ },
+ {
+ title: '累计水量(万m³)', dataIndex: 'totalWater', key: 'totalWater', align: 'center',
+ },
+ ];
+
+ const getHistoryList = async (params) => {
+ setLoading(true);
+ try {
+ const { data, code } = await httppost(apiurl.sq.qth.flow.history, params);
+ if (code == 200) {
+ setHistoryList(data);
+ setLoading(false);
+ }
+ } catch (error) {
+ console.log(error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const getHistoryMax = async (params) => {
+ try {
+ const { data, code } = await httppost(apiurl.sq.qth.flow.max, params);
+ if (code == 200) {
+ setMaxInfo(data);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ const handleSearch = () => {
+ if (!stcd) return;
+ const params = {
+ stcd,
+ dateTimeRangeSo: {
+ start:dates && dates[0] ? dates[0].format('YYYY-MM-DD HH:mm:ss'):undefined,
+ end:dates && dates[1] ?dates[1].format('YYYY-MM-DD HH:mm:ss'):undefined
+ }
+ };
+ getHistoryList(params);
+ getHistoryMax(params)
+ };
+
+ useEffect(() => {
+ if (stcd) {
+ handleSearch();
+ }
+ }, [stcd]);
+
+ const option = useMemo(() => flowOption({ data: historyList }), [historyList]);
+
+ return (
+
+
+
数据查询
+
+
+ } onClick={handleSearch} loading={loading}>
+ 查询
+
+
+
+
+
+
+
+
最大瞬时流量(m³/s)
+
累计水量(万m³)
+
+ {maxInfo?.maxQ ?? '-'}
+ {maxInfo?.maxQTm &&({moment(maxInfo?.maxQTm).format('YYYY-MM-DD HH:mm:ss')})}
+
+
{maxInfo?.totalWater ?? '-'}
+
+
+ );
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/chartOption.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/chartOption.js
new file mode 100644
index 0000000..9b8052c
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/chartOption.js
@@ -0,0 +1,76 @@
+import moment from "moment";
+
+export default function flowOption({ data }) {
+ const qList = data.map(o => o.q ?? 0);
+ const wList = data.map(o => o.totalWater ?? 0);
+ const tms = data?.map(o=> o.tm ? moment(o.tm).format('YYYY-MM-DD HH:mm'):null)
+ const maxQ = Math.ceil(Math.max(...qList, 0))
+ const minQ = Math.floor(Math.min(...qList, 0))
+ const maxW = Math.ceil(Math.max(...wList, 0))
+ const minW = Math.floor(Math.min(...wList, 0))
+
+
+ return {
+ tooltip: { trigger: 'axis' },
+ legend: {
+ show: true,
+ textStyle: { color: '#fff' },
+ data: ['累计水量', '瞬时流量']
+ },
+ grid: {
+ top: '15%', left: '2%', right: '2%', bottom: '5%', containLabel: true
+ },
+ xAxis: {
+ type: 'category',
+ data: tms,
+ inverse:true,
+ axisLabel: { color: '#fff' },
+ axisLine: { lineStyle: { color: 'rgba(255,255,255,0.5)' } }
+ },
+ yAxis: [
+ {
+ type: 'value',
+ name: '累计水量(万m³)',
+ position: 'left',
+ axisLabel: { color: '#fff' },
+ nameTextStyle: { color: '#fff' },
+ splitLine: { show: false },
+ min: minW,
+ max:maxW
+ },
+ {
+ type: 'value',
+ name: '瞬时流量(m³/s)',
+ position: 'right',
+ axisLabel: { color: '#fff' },
+ nameTextStyle: { color: '#fff' },
+ splitLine: {
+ show: true,
+ lineStyle: { color: 'rgba(255,255,255,0.2)', type: 'dashed' }
+ },
+ min: minQ,
+ max:maxQ
+ }
+ ],
+ series: [
+ {
+ name: '累计水量',
+ type: 'line',
+ yAxisIndex: 0,
+ data: wList,
+ itemStyle: { color: '#1890ff' }, // Blue
+ showSymbol: false,
+ smooth: true
+ },
+ {
+ name: '瞬时流量',
+ type: 'line',
+ yAxisIndex: 1,
+ data: qList,
+ itemStyle: { color: '#52c41a' }, // Green
+ showSymbol: false,
+ smooth: true
+ }
+ ]
+ };
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/index.js
new file mode 100644
index 0000000..a67cd58
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/index.js
@@ -0,0 +1,115 @@
+import React, { useEffect, useState } from 'react';
+import { Select } from 'antd';
+import { flowlist } from '@/service/station';
+import RightPanel from './RightPanel';
+import moment from 'moment';
+import './index.less';
+
+const FlowPanel = ({ stcd, cleanMode = false }) => {
+ const [selectList, setSelectList] = useState([]);
+ const [selected, setSelected] = useState('');
+ const [detail, setDetail] = useState({});
+
+ // 获取站点
+ const getStationList = async () => {
+ try {
+ // Reusing reservoir list for now
+ const data = await flowlist({});
+ const formattedData = data.map(item => ({
+ label: item.stnm,
+ value: item.stcd,
+ ...item
+ }));
+ setSelectList(formattedData);
+
+ let targetStcd = selected || stcd;
+ if (targetStcd) {
+ const item = formattedData.find(i => i.stcd === targetStcd);
+ if (item) {
+ setSelected(targetStcd);
+ setDetail(item);
+ } else if (formattedData.length > 0) {
+ setSelected(formattedData[0].stcd);
+ setDetail(formattedData[0]);
+ }
+ } else if (formattedData.length > 0) {
+ setSelected(formattedData[0].stcd);
+ setDetail(formattedData[0]);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ useEffect(() => {
+ if (stcd) {
+ setSelected(stcd);
+ }
+ }, [stcd]);
+
+ const handleStationChange = (val) => {
+ setSelected(val);
+ const item = selectList.find(i => i.value === val);
+ if (item) {
+ setDetail(item);
+ }
+ };
+
+ useEffect(() => {
+ getStationList();
+ }, []);
+
+ const {
+ q,change24Q,avg24Q,total24V
+ } = detail || {}
+ const stats = [
+ { label: '站点类型', value: '-', unit: '' },
+ { label: '实时瞬时流量', value: q ?? '-', unit: 'm³/s' },
+ { label: '近24h变幅', value: change24Q ?? '-', unit: 'm³/s' },
+ { label: '近24h平均流量', value: avg24Q ?? '-', unit: 'm³/s' },
+ { label: '近24h累计水量', value: total24V ?? '-', unit: '万m³' },
+ ];
+
+ return (
+
+
+
+
+
实时流量
+ {!cleanMode && (
+
+ 站点:
+
+
+ )}
+
+
+
+ 流量最新上报时间:{detail?.tm ?? moment().format('YYYY-MM-DD HH:mm:ss')}
+
+
+
+ {stats.map((item, idx) => (
+
+
{item.label}:
+
+ {item.value}
+ {item.unit}
+
+
+ ))}
+
+
+
+
+
+
+ );
+};
+
+export default FlowPanel;
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/index.less b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/index.less
new file mode 100644
index 0000000..840329a
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/FlowPanel/index.less
@@ -0,0 +1,199 @@
+.flow-panel-container {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ color: #fff;
+
+ .main-content {
+ flex: 1;
+ display: flex;
+ gap: 16px;
+ overflow: hidden;
+
+ .left-panel {
+ width: 440px;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+
+ .panel-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 40px;
+
+ .query-label {
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ font-weight: bold;
+ .dot {
+ width: 4px;
+ height: 16px;
+ background: #00a0e9;
+ margin-right: 8px;
+ }
+ }
+
+ .station-select {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ }
+
+ .update-time-banner {
+ width: 70%;
+ margin: 0 auto;
+ background: rgba(0, 160, 233, 0.1);
+ border: 1px solid rgba(0, 160, 233, 0.3);
+ padding: 8px;
+ text-align: center;
+ border-radius: 4px;
+ color: #fff;
+ margin-bottom: 0px;
+ }
+
+ .data-list {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ padding: 0 10px;
+
+ .data-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 8px;
+ border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
+
+ .label {
+ font-size: 14px;
+ color: rgba(255, 255, 255);
+ }
+
+ .value-container {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .value {
+ font-size: 16px;
+ font-weight: bold;
+ font-family: Arial, sans-serif;
+ }
+
+ .unit {
+ font-size: 12px;
+ color: rgba(255, 255, 255, 0.6);
+ margin-left: 4px;
+ }
+ }
+ }
+ }
+
+ .visual-placeholder {
+ flex: 1;
+ background: rgba(255, 255, 255, 0.02);
+ border-radius: 4px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: rgba(255, 255, 255, 0.3);
+ border: 1px dashed rgba(255, 255, 255, 0.1);
+ }
+ }
+
+ .right-panel {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ border-radius: 4px;
+
+ .panel-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .query-label {
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ font-weight: bold;
+ .dot {
+ width: 4px;
+ height: 16px;
+ background: #00a0e9;
+ margin-right: 8px;
+ }
+ }
+
+ .query-controls {
+ display: flex;
+ gap: 10px;
+ }
+ }
+
+ .table-chart-layout {
+ flex: 1;
+ display: flex;
+ gap: 16px;
+ overflow: hidden;
+
+ .data-table {
+ width: 450px;
+ overflow: auto;
+ }
+
+ .chart-view {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+
+ .chart-container {
+ flex: 1;
+ min-height: 0;
+ }
+ }
+ }
+
+ .bottom-stats-grid {
+ display: flex;
+ flex-wrap: wrap;
+ border-top: 1px solid rgba(0, 160, 233, 0.2);
+ border-left: 1px solid rgba(0, 160, 233, 0.2);
+
+ .grid-header {
+ width: 50%;
+ background: rgba(0, 160, 233, 0.1);
+ color: rgba(255, 255, 255);
+ padding: 8px 4px;
+ text-align: center;
+ font-size: 14px;
+ border-right: 1px solid rgba(0, 160, 233, 0.2);
+ border-bottom: 1px solid rgba(0, 160, 233, 0.2);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .grid-value {
+ width: 50%;
+ color: #fff;
+ padding: 8px 4px;
+ text-align: center;
+ font-size: 14px;
+ font-weight: bold;
+ border-right: 1px solid rgba(0, 160, 233, 0.2);
+ border-bottom: 1px solid rgba(0, 160, 233, 0.2);
+
+ .special-text {
+ color: #00e5ff;
+ font-weight: normal;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/RainMonitor/drpOption.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/RainMonitor/drpOption.js
index bcefe0d..cf7c9f0 100644
--- a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/RainMonitor/drpOption.js
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/RainMonitor/drpOption.js
@@ -49,8 +49,7 @@ export default function DrpOption({ echartData, grid }) {
},
axisLine: {
lineStyle: {
- color: '#07a6ff',
- width: 0.5,
+ color: 'rgba(255,255,255,0.5)',
}
},
axisTick: {
@@ -67,8 +66,7 @@ export default function DrpOption({ echartData, grid }) {
splitLine: {
show: true,
lineStyle: {
- color: '#07a6ff',
- width: 0.25,
+ color: 'rgba(255,255,255,0.5)',
type: 'dashed'
}
},
@@ -93,8 +91,7 @@ export default function DrpOption({ echartData, grid }) {
splitLine: {
show: true,
lineStyle: {
- color: '#07a6ff',
- width: 0.25,
+ color: 'rgba(255,255,255,0.5)',
type: 'dashed'
}
},
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/RightPanel.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/RightPanel.js
new file mode 100644
index 0000000..666c094
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/RightPanel.js
@@ -0,0 +1,174 @@
+import React, { useEffect, useMemo, useState } from 'react';
+import { DatePicker, Button, Table } from 'antd';
+import { SearchOutlined } from '@ant-design/icons';
+import ReactEcharts from 'echarts-for-react';
+import apiurl from '@/service/apiurl';
+import { httpget, httppost } from '@/utils/request';
+import moment from 'moment';
+import drpOption from './drpOption';
+import './index.less';
+
+const { RangePicker } = DatePicker;
+
+export default function RightPanel({ stcd, cleanMode = false,record }) {
+ const defaultRange = [
+ moment().subtract(7, 'days').set({ minute: 0, second: 0 }),
+ moment().set({ minute: 0, second: 0 }),
+ ];
+ const [dates, setDates] = useState(defaultRange);
+ const [historyTableList, setHistoryTableList] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [historySkDetail, setHistorySkDetail] = useState({});
+
+ const columns = [
+ {
+ title: '时间', dataIndex: 'time', key: 'time', align: 'center', width: 120,
+ render:(rec,row)=>row.tm?moment(row.tm).format('MM-DD HH:mm'):''
+ },
+ {
+ title: '雨量(mm)', dataIndex: 'drp', key: 'drp', align: 'center',
+ render: (rec) => {rec ?? "-"}
+
+ },
+ {
+ title: '水位(m)', dataIndex: 'rz', key: 'rz', align: 'center',
+ render: (rec) => {rec ? rec.toFixed(2) : "-"}
+ },
+ {
+ title: '库容(万m³)', dataIndex: 'w', key: 'w', align: 'center',
+ render: (rec) => {rec ??"-"},
+ },
+ ];
+
+ // Placeholder stats
+ const {
+ h1, h3, h6, h12, h24, h48,
+ yearDrpDay, rzDiff, today, yesterdayDrp,
+ monthDrp, yearDrp, maxDrp, maxDrpTime, maxRz
+ } = historySkDetail || {};
+
+ const bottomStats = [
+ { label: '近1h雨量(mm)', value: h1 ?? '-' },
+ { label: '近3h雨量(mm)', value: h3 ?? '-' },
+ { label: '近6h雨量(mm)', value: h6 ?? '-' },
+ { label: '近12h雨量(mm)', value: h12 ?? '-' },
+ { label: '近24h雨量(mm)', value: h24 ?? '-' },
+ { label: '本年降雨天数', value: yearDrpDay || 0 },
+ { label: '24h水位变幅(m)', value: rzDiff ? (rzDiff > 0 ? "+" : "") + rzDiff.toFixed(2) : '-' },
+ { label: '近48h雨量(mm)', value: h48 ?? '-' },
+ { label: '今日雨量(mm)', value: today ?? '-' },
+ { label: '昨日雨量(mm)', value: yesterdayDrp ?? '-' },
+ { label: '本月雨量(mm)', value: monthDrp ?? '-' },
+ { label: '本年雨量(mm)', value: yearDrp ?? '-' },
+ { label: '本年最大日雨量(mm)', value: maxDrp !==null ? `${maxDrp}(${maxDrpTime ? moment(maxDrpTime).format('MM-DD') : ''})` : '-' },
+ { label: '本年最高水位(m)', value: maxRz ? maxRz.toFixed(2) : '-' },
+ ];
+
+ // Chart Option
+ const option = useMemo(() => {
+ return drpOption({data:historyTableList,afsltdz:record.afsltdz,flLowLimLev:record.flLowLimLev,desFloodLev:record.desFloodLev,calFloodLev:record.calFloodLev})
+ }, [historyTableList]);
+
+
+ // 获取水库历史数据
+ const getHistoryList = async (params) => {
+ setLoading(true);
+ try {
+ const { data, code } = await httppost(apiurl.sq.qth.reservoir.monitor, params)
+ if (code == 200) {
+ setLoading(false)
+ setHistoryTableList(data)
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ // 获取水库近几小时数据
+ const getHourDetail= async (params) => {
+ try {
+ const { data, code } = await httpget(apiurl.sq.qth.reservoir.detail, params)
+ if (code == 200) {
+ setHistorySkDetail(data);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ const handleSearch = async () => {
+ if (!stcd) return;
+
+ const params = {
+ stcd,
+ stm: dates[0].format('YYYY-MM-DD HH:mm:ss'),
+ etm: dates[1].format('YYYY-MM-DD HH:mm:ss')
+ };
+ getHistoryList(params)
+ getHourDetail(params)
+ };
+
+ useEffect(() => {
+ if (stcd) {
+ handleSearch();
+ }
+ }, [stcd]);
+
+ return (
+
+
+
数据查询
+
+
+ } onClick={handleSearch} loading={loading}>
+ 查询
+
+
+
+
+
+
+
+ {bottomStats.slice(0, 7).map((i, idx) =>
{i.label}
)}
+ {bottomStats.slice(0, 7).map((i, idx) => (
+
+ {i.value}
+
+ ))}
+ {bottomStats.slice(7, 14).map((i, idx) =>
{i.label}
)}
+ {bottomStats.slice(7, 14).map((i, idx) => (
+
+ {i.value}
+
+ ))}
+
+
+ );
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/drpOption.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/drpOption.js
new file mode 100644
index 0000000..484ebaa
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/drpOption.js
@@ -0,0 +1,257 @@
+export default function drpOption ({
+ data,
+ afsltdz,
+ flLowLimLev,
+ desFloodLev,
+ calFloodLev
+}) {
+
+ // flLowLimLev 汛限水位 desFloodLev 设计水位 calFloodLev校核水位
+ const maxVal = Math.max(...data.map(obj => obj.drp))
+ // const minVal = Math.min(...data.map(obj => obj.drp))
+ const maxSw = Math.ceil(Math.max(...data.map(obj => obj.rz),flLowLimLev,desFloodLev,calFloodLev))
+ const minSw = Math.floor(Math.min(...data.map(obj => obj.rz),flLowLimLev,desFloodLev,calFloodLev))
+ const maxKr = Math.ceil(Math.max(...data.map(obj => obj.w)))
+ const minKr = Math.floor(Math.min(...data.map(obj => obj.w)))
+ return {
+ tooltip: {
+ trigger: 'axis'
+ },
+ grid: [
+ {
+ top: '15%',
+ left: '10%',
+ right: '8%',
+ width: '80%',
+ height: '35%'
+ },
+ {
+ bottom: '5%',
+ left: '10%',
+ right: '8%',
+ width: '80%',
+ height: '35%'
+ }
+ ],
+ legend: {
+ // 显示图例
+ show: true,
+ textStyle: { color: '#fff' },
+ // 图例的位置
+ // data: ['汛限水位', '设计水位', '校核水位', "降雨量", "水位", "库容"],
+ data: ['校核水位', '设计水位', '汛限水位', '降雨量', '水位', '库容']
+ },
+ xAxis: [
+ {
+ gridIndex: 0,
+ type: 'category',
+ data: data.map(o => o.tm).reverse(),
+
+ splitLine: {
+ show: false
+ },
+ axisLabel: {
+ color: '#fff',
+ fontSize: 12,
+ show: false
+ },
+ axisLine: {
+ lineStyle: {
+ color: 'rgba(255,255,255,0.5)',
+ }
+ },
+ axisTick: {
+ show: false
+ }
+ },
+ {
+ gridIndex: 1,
+ type: 'category',
+ data: data.map(o => o.tm),
+ inverse: true,
+ splitLine: {
+ show: false
+ },
+ axisLabel: {
+ color: '#fff',
+ fontSize: 12,
+ formatter: val => val.substr('2020-'.length, 11)
+ },
+ axisLine: {
+ lineStyle: {
+ color: 'rgba(255,255,255,0.5)',
+ }
+ },
+ axisTick: {
+ show: false
+ }
+ }
+ ],
+ yAxis: [
+ {
+ inverse: true,
+ gridIndex: 0,
+ type: 'value',
+ position: 'left',
+ name: '降雨量(mm)',
+ nameLocation: 'start',
+ nameTextStyle: {
+ color: '#fff'
+ },
+ axisLabel: {
+ color: '#fff',
+ fontSize: 12
+ },
+ splitLine: {
+ show: true,
+ lineStyle: {
+ color: 'rgba(255,255,255,0.5)',
+ type: 'dotted'
+ }
+ },
+ axisLine: {
+ show: false
+ },
+ axisTick: {
+ show: false
+ },
+ min: 0,
+ max: maxVal
+ },
+ {
+ gridIndex: 1,
+ type: 'value',
+ position: 'left',
+ name: '水位(m)',
+ nameTextStyle: {
+ color: '#fff'
+ },
+ splitLine: {
+ show: true,
+ lineStyle: {
+ color: 'rgba(255,255,255,0.5)',
+ type: 'dotted'
+ }
+ },
+ axisLabel: {
+ color: '#fff',
+ fontSize: 12
+ },
+ axisLine: {
+ show: false
+ },
+ axisTick: {
+ show: false
+ },
+ min: minSw,
+ max: maxSw
+ },
+ {
+ gridIndex: 1,
+ type: 'value',
+ position: 'right',
+ name: '库容(万m³)',
+ nameTextStyle: {
+ color: '#fff'
+ },
+ splitLine: {
+ show: false,
+ lineStyle: {
+ color: '#07a6ff',
+ width: 0.25,
+ type: 'dotted'
+ }
+ },
+ axisLabel: {
+ color: '#fff',
+ fontSize: 12
+ },
+ axisLine: {
+ show: false
+ },
+ axisTick: {
+ show: false
+ },
+ min: minKr,
+ max: maxKr
+ }
+ ],
+ series: [
+ {
+ xAxisIndex: 1,
+ yAxisIndex: 1,
+ name: '校核水位',
+ type: 'line',
+ color: '#D9001B',
+ lineStyle: {
+ type: 'dashed'
+ },
+ data: data.map(o => calFloodLev),
+ symbol: 'none' // 设置标记点为'none',即去掉圆点
+ },
+ {
+ xAxisIndex: 1,
+ yAxisIndex: 1,
+ name: '设计水位',
+ type: 'line',
+ color: '#F59A23',
+ data: data.map(o => desFloodLev),
+ lineStyle: {
+ type: 'dashed'
+ },
+ symbol: 'none' // 设置标记点为'none',即去掉圆点
+ },
+ {
+ xAxisIndex: 1,
+ yAxisIndex: 1,
+ name: '汛限水位',
+ type: 'line',
+ color: '#FDDC9F',
+ data: data.map(o => {
+ return flLowLimLev
+ }),
+ lineStyle: {
+ type: 'dashed'
+ },
+ symbol: 'none' // 设置标记点为'none',即去掉圆点
+ },
+ {
+ name: '降雨量',
+ type: 'bar',
+ barWidth: '60%',
+ data: data.map(o => o.drp).reverse(),
+ itemStyle: {
+ color: '#007AFD'
+ },
+ label: {
+ show: false
+ }
+ },
+ {
+ xAxisIndex: 1,
+ yAxisIndex: 1,
+ name: '水位',
+ type: 'line',
+ symbol: 'none',
+ color: '#0AE0B5',
+ label: {
+ show: false
+ },
+ data: data.map(o => o.rz ? o.rz.toFixed(2):null )
+ },
+ {
+ xAxisIndex: 1,
+ yAxisIndex: 2,
+ name: '库容',
+ type: 'line',
+ color: '#007AFD',
+ symbol: 'none',
+ showSymbol: false,
+ label: {
+ show: false
+ },
+ data: data.map(o => o.w)
+ }
+ ]
+ }
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/index.js
new file mode 100644
index 0000000..ffc82a7
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/index.js
@@ -0,0 +1,133 @@
+import React, { useEffect, useState } from 'react';
+import { Select } from 'antd';
+import apiurl from '@/service/apiurl';
+import { httpget, httppost } from '@/utils/request';
+import { reservoirlist } from '@/service/station';
+import RightPanel from './RightPanel';
+import moment from 'moment';
+import MyImg from './myImg';
+import './index.less';
+
+const ReservoirPanel = ({ stcd, cleanMode = false }) => {
+ const [selectList, setSelectList] = useState([]);
+ const [selected, setSelected] = useState('');
+ const [reservoirDetail, setReservoirDetail] = useState({});
+
+ // 获取水库站点
+ const getReservoirList = async () => {
+ try {
+ const data = await reservoirlist({});
+ const formattedData = data.map(item => ({
+ label: item.stnm,
+ value: item.stcd,
+ ...item
+ }));
+ setSelectList(formattedData);
+
+ // Determine which station to select
+ let targetStcd = selected || stcd;
+
+ if (targetStcd) {
+ const item = formattedData.find(i => i.stcd === targetStcd);
+ if (item) {
+ setSelected(targetStcd);
+ setReservoirDetail(item);
+ } else if (formattedData.length > 0) {
+ // Fallback if target not found
+ setSelected(formattedData[0].stcd);
+ setReservoirDetail(formattedData[0]);
+ }
+ } else if (formattedData.length > 0) {
+ setSelected(formattedData[0].stcd);
+ setReservoirDetail(formattedData[0]);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+
+ useEffect(() => {
+ if (stcd) {
+ setSelected(stcd);
+ }
+ }, [stcd]);
+
+ const handleStationChange = (val) => {
+ setSelected(val);
+ const item = selectList.find(i => i.value === val);
+ if (item) {
+ setReservoirDetail(item);
+ }
+ };
+
+ useEffect(() => {
+ getReservoirList();
+ }, []);
+
+ // Data items for the left list
+ const dataItems = [
+ { label: '监测水位', value: reservoirDetail?.rz ??'-', unit: 'm', isPrimary: true },
+ { label: '校核洪水位', value: reservoirDetail?.calFloodLev ?? '-', unit: 'm', diff: (reservoirDetail?.calFloodLev && reservoirDetail?.rz) ? (reservoirDetail.rz - reservoirDetail.calFloodLev).toFixed(2) : null },
+ { label: '设计洪水位', value: reservoirDetail?.desFloodLev ?? '-', unit: 'm', diff: (reservoirDetail?.desFloodLev && reservoirDetail?.rz) ? (reservoirDetail.rz - reservoirDetail.desFloodLev).toFixed(2) : null },
+ { label: '汛限水位', value: reservoirDetail?.flLowLimLev ?? '-', unit: 'm', diff: (reservoirDetail?.flLowLimLev && reservoirDetail?.rz) ? (reservoirDetail.rz - reservoirDetail.flLowLimLev).toFixed(2) : null },
+ { label: '死水位', value: reservoirDetail?.deadLev ?? '-', unit: 'm', diff: (reservoirDetail?.deadLev && reservoirDetail?.rz) ? (reservoirDetail.rz - reservoirDetail.deadLev ).toFixed(2) : null },
+ { label: '坝顶高程', value: reservoirDetail?.crestElev ?? '-', unit: 'm' },
+ { label: '水库当前库容', value: reservoirDetail?.nowCap ?? '-', unit: '万m³' },
+ { label: '兴利库容', value: reservoirDetail?.benResCap ?? '-', unit: '万m³' },
+ ];
+
+ return (
+
+
+ {/* Left Side: Stats List */}
+
+
+
实时水位
+ {!cleanMode && (
+
+ 站点:
+
+
+ )}
+
+
+
+ 水位上报时间:{reservoirDetail?.tm ?? moment().format('YYYY-MM-DD HH:mm:ss')}
+
+
+
+ {dataItems.map((item, idx) => (
+
+
{item.label}:
+
+ {item.value}
+ {item.diff && (
+ 0 ? '#ff4d4f' : '#52c41a' }}>
+ {item.diff > 0 ? `| +${item.diff}` : `| -${Math.abs(item.diff)}`}
+
+ )}
+ {item.unit}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ReservoirPanel;
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/index.less b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/index.less
new file mode 100644
index 0000000..12c850c
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/index.less
@@ -0,0 +1,262 @@
+.reservoir-panel-container {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ color: #fff;
+
+ .main-content {
+ flex: 1;
+ display: flex;
+ gap: 16px;
+ overflow: hidden;
+
+ .left-panel {
+ width: 480px;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+
+ .panel-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ height: 42px;
+ .query-label {
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ font-weight: bold;
+ .dot {
+ width: 4px;
+ height: 16px;
+ background: #00a0e9;
+ margin-right: 8px;
+ }
+ }
+ .station-select {
+ display: flex;
+ align-items: center;
+ span { margin-right: 8px; }
+ }
+ }
+
+ .update-time-banner {
+ width: 70%;
+ margin: 0 auto;
+ background: rgba(0, 160, 233, 0.1);
+ border: 1px solid rgba(0, 160, 233, 0.3);
+ padding: 8px;
+ text-align: center;
+ border-radius: 4px;
+ color: #fff;
+ margin-bottom: 0px;
+ }
+
+ .data-list {
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+
+ .data-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding-bottom: 2px;
+ border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
+
+ .label {
+ color: #fff;
+ font-size: 14px;
+ }
+
+ .value-container {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ .value {
+ font-size: 16px;
+ font-weight: bold;
+ color: #fff;
+ }
+
+ .diff {
+ font-size: 14px;
+ font-weight: bold;
+ }
+
+ .unit {
+ font-size: 12px;
+ color: rgba(255, 255, 255, 0.6);
+ margin-left: 4px;
+ }
+ }
+ }
+ }
+
+ .visual-placeholder {
+ flex: 1;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 200px;
+ margin-top: 10px;
+ overflow: hidden; /* Added to contain overflow */
+
+ .placeholder-content {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ /* text-align: center; removed */
+ /* color: rgba(255, 255, 255, 0.5); removed */
+
+ /* img { removed generic img style as MyImg handles it
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ } */
+ }
+ }
+ }
+
+ .right-panel {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ // padding: 16px;
+ border-radius: 4px;
+
+ .panel-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .query-label {
+ display: flex;
+ align-items: center;
+ font-size: 16px;
+ font-weight: bold;
+ .dot {
+ width: 4px;
+ height: 16px;
+ background: #00a0e9;
+ margin-right: 8px;
+ }
+ }
+
+ .query-controls {
+ display: flex;
+ gap: 10px;
+ align-items: center;
+
+ .ant-picker {
+ background: rgba(0, 0, 0, 0.2);
+ border: 1px solid rgba(0, 160, 233, 0.5);
+ color: #fff;
+
+ input { color: #fff; }
+ .ant-picker-suffix { color: rgba(255, 255, 255, 0.5); }
+ }
+ }
+ }
+
+ .table-chart-layout {
+ flex: 1;
+ display: flex;
+ gap: 16px;
+ overflow: hidden;
+
+ .data-table {
+ flex: 0 0 400px;
+
+ .ant-table {
+ background: transparent;
+ color: #fff;
+
+ .ant-table-thead > tr > th {
+ background: rgba(0, 160, 233, 0.2);
+ color: #fff;
+ border-bottom: none;
+ }
+
+ .ant-table-tbody > tr > td {
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+ color: #fff;
+
+ &:hover {
+ background: rgba(0, 160, 233, 0.1) !important;
+ }
+ }
+
+ .ant-table-tbody > tr:hover > td {
+ background: rgba(0, 160, 233, 0.1) !important;
+ }
+ }
+ }
+
+ .chart-view {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+
+ .chart-container {
+ flex: 1;
+ min-height: 0;
+ }
+ }
+ }
+
+ .bottom-stats-grid {
+ display: flex;
+ flex-wrap: wrap;
+ border-top: 1px solid rgba(0, 160, 233, 0.2);
+ border-left: 1px solid rgba(0, 160, 233, 0.2);
+
+ .grid-header {
+ width: 14.2%;
+ background: rgba(0, 160, 233, 0.1);
+ color: rgba(255, 255, 255);
+ padding: 8px 4px;
+ text-align: center;
+ font-size: 14px;
+ border-right: 1px solid rgba(0, 160, 233, 0.2);
+ border-bottom: 1px solid rgba(0, 160, 233, 0.2);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .grid-value {
+ width: 14.2%;
+ color: #fff;
+ padding: 8px 4px;
+ text-align: center;
+ font-size: 14px;
+ font-weight: bold;
+ border-right: 1px solid rgba(0, 160, 233, 0.2);
+ border-bottom: 1px solid rgba(0, 160, 233, 0.2);
+
+ .special-text {
+ color: #00e5ff;
+ font-weight: normal;
+ }
+ }
+ }
+ }
+ }
+}
+
+.ant-btn-ghost-blue {
+ background: transparent;
+ border-color: #00a0e9;
+ color: #00a0e9;
+
+ &:hover, &:focus {
+ color: #40a9ff;
+ border-color: #40a9ff;
+ background: rgba(0, 160, 233, 0.1);
+ }
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/myImg.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/myImg.js
new file mode 100644
index 0000000..ce3bed6
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/ReservoirPanel/myImg.js
@@ -0,0 +1,154 @@
+const MyImg = ({ record }) => {
+ console.log("record: ", record);
+ /**
+ *
+ * @param {*} h1 动态水位
+ * @param {*} h2 坝高
+ * @param {*} h3 死水位
+ * @returns
+ */
+ const computerHeight = (h1, h2, h3) => {
+ let height;
+ let comHeight;
+ if (h1 && h2 && h3) {
+ comHeight = (h1 - h3) / (h2 - h3)
+
+ // height = comHeight * 100 < 80 ? `${comHeight}%` : "80%"
+ height = `${comHeight * 80}%`
+ return comHeight > 0 ? height : 10
+ } else {
+ return '0%'
+ }
+ }
+ return (
+
+

+
+

+
+

+
+
+

+
+ 设计洪水位 {record.desFloodLev ? record.desFloodLev.toFixed(2) : "-"}m
+
+
+
+
+

+
+ 汛限水位 {record.flLowLimLev ? record.flLowLimLev.toFixed(2) : "-"}m
+
+
+
+
+

+
+
+ 实时水位 {record.rz ? record.rz.toFixed(2) : "-"}m
+
+
+
+
+
+

+
+ 死水位 {record.deadLev ? record.deadLev.toFixed(2) : ''}m
+
+
+
+ )
+}
+export default MyImg
\ No newline at end of file
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js
index 6e0b7e2..7d137ef 100644
--- a/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/AllWeatherModal/index.js
@@ -6,68 +6,10 @@ import { createCrudService } from '@/components/crud/_';
import apiurl from '@/service/apiurl';
import './index.less';
import RainMonitor from './RainMonitor';
+import ReservoirPanel from './ReservoirPanel';
+import FlowPanel from './FlowPanel';
const { RangePicker } = DatePicker;
-
-
-const ReservoirPanel = () => {
- const columns = [
- { title: '时间', dataIndex: 'tm', key: 'tm', width: 140, align: 'center' },
- { title: '水位(m)', dataIndex: 'rz', key: 'rz', align: 'center' },
- { title: '库容(万m³)', dataIndex: 'w', key: 'w', align: 'center' },
- ];
- const data = [];
- const option = {
- title: { text: '水位趋势', left: 'center', textStyle: { color: '#fff' } },
- grid: { left: '8%', right: '4%', bottom: '10%', top: '15%' },
- tooltip: { trigger: 'axis' },
- xAxis: { type: 'category', data: data.map(i => i.tm), axisLabel: { color: '#fff' } },
- yAxis: { type: 'value', axisLabel: { color: '#fff' }, splitLine: { show: true, lineStyle: { color: 'rgba(255,255,255,0.2)' } } },
- series: [{ type: 'line', smooth: true, data: data.map(i => i.rz), lineStyle: { color: '#00a0e9' } }],
- };
- return (
-
- );
-};
-
-const FlowPanel = () => {
- const columns = [
- { title: '时间', dataIndex: 'tm', key: 'tm', width: 140, align: 'center' },
- { title: '入库(m³/s)', dataIndex: 'qin', key: 'qin', align: 'center' },
- { title: '出库(m³/s)', dataIndex: 'qout', key: 'qout', align: 'center' },
- ];
- const data = [];
- const option = {
- title: { text: '出入库流量', left: 'center', textStyle: { color: '#fff' } },
- grid: { left: '8%', right: '4%', bottom: '10%', top: '15%' },
- tooltip: { trigger: 'axis' },
- legend: { data: ['入库', '出库'], textStyle: { color: '#fff' } },
- xAxis: { type: 'category', data: data.map(i => i.tm), axisLabel: { color: '#fff' } },
- yAxis: { type: 'value', axisLabel: { color: '#fff' }, splitLine: { show: true, lineStyle: { color: 'rgba(255,255,255,0.2)' } } },
- series: [
- { name: '入库', type: 'line', data: data.map(i => i.qin), lineStyle: { color: '#00a0e9' } },
- { name: '出库', type: 'line', data: data.map(i => i.qout), lineStyle: { color: '#3b7cff' } },
- ],
- };
- return (
-
- );
-};
-
const SafetyPanel = () => {
return (
内容待接入
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/FlightTasks.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/FlightTasks.js
new file mode 100644
index 0000000..ad635bc
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/FlightTasks.js
@@ -0,0 +1,55 @@
+import React from 'react';
+import { Table } from 'antd';
+
+export default function FlightTasks() {
+ const columns = [
+ {
+ title: '序号',
+ dataIndex: 'index',
+ key: 'index',
+ align: 'center',
+ width: 80,
+ render: (text, record, index) => index + 1
+ },
+ {
+ title: '名称',
+ dataIndex: 'name',
+ key: 'name',
+ align: 'center',
+ },
+ {
+ title: '起飞',
+ key: 'takeoff',
+ align: 'center',
+ width: 120,
+ render: () => 起飞
+ },
+ {
+ title: '航线',
+ key: 'route',
+ align: 'center',
+ width: 120,
+ render: () => 显示
+ }
+ ];
+
+ const data = [
+ { id: 1, name: '视频任务' },
+ { id: 2, name: '拍摄' },
+ { id: 3, name: '坝体巡检' },
+ { id: 4, name: '溢洪道下泄巡查' },
+ { id: 5, name: '界桩巡检01' },
+ ];
+
+ return (
+
+ );
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/History.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/History.js
new file mode 100644
index 0000000..b61962a
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/History.js
@@ -0,0 +1,75 @@
+import React, { useState } from 'react';
+import { Table, DatePicker, Button } from 'antd';
+import moment from 'moment';
+
+const { RangePicker } = DatePicker;
+
+export default function History() {
+ const [dates, setDates] = useState([]);
+
+ const columns = [
+ {
+ title: '序号',
+ dataIndex: 'index',
+ key: 'index',
+ align: 'center',
+ width: 80,
+ render: (text, record, index) => index + 1
+ },
+ {
+ title: '名称',
+ dataIndex: 'taskName',
+ key: 'taskName',
+ align: 'center',
+ },
+ {
+ title: '开始时间',
+ dataIndex: 'startTime',
+ key: 'startTime',
+ align: 'center',
+ },
+ {
+ title: '结束时间',
+ dataIndex: 'endTime',
+ key: 'endTime',
+ align: 'center',
+ },
+ {
+ title: '操作',
+ key: 'action',
+ align: 'center',
+ width: 100,
+ render: () => 回放
+ }
+ ];
+
+ const data = [
+ { id: 1, taskName: '视频任务', startTime: '2025-12-15 09:31:23', endTime: '2025-12-15 09:51:23' },
+ { id: 2, taskName: '拍摄', startTime: '2025-12-15 09:31:23', endTime: '2025-12-15 09:51:23' },
+ { id: 3, taskName: '坝体巡检', startTime: '2025-12-15 09:31:23', endTime: '2025-12-15 09:51:23' },
+ { id: 4, taskName: '溢洪道下泄巡查', startTime: '2025-12-15 09:31:23', endTime: '2025-12-15 09:51:23' },
+ { id: 5, taskName: '界桩巡检01', startTime: '2025-12-15 09:31:23', endTime: '2025-12-15 09:51:23' },
+ ];
+
+ return (
+
+ );
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/index.js
new file mode 100644
index 0000000..7b370f8
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/index.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import './index.less';
+import FlightTasks from './FlightTasks';
+import History from './History';
+import VideoList from '../VideoList';
+
+export default function UAVModal({ activeKey }) {
+ const renderContent = () => {
+ switch (activeKey) {
+ case '1':
+ return (
+
+ 暂无直播画面
+
+ );
+ case '2':
+ return ;
+ case '3':
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+ {renderContent()}
+
+ );
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/index.less b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/index.less
new file mode 100644
index 0000000..faae76c
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/UAVModal/index.less
@@ -0,0 +1,25 @@
+.uav-modal-container {
+ height: 100%;
+ width: 100%;
+ color: #fff;
+
+ // Common Table Styles for Tabs
+ .tab-content-wrapper {
+ height: 100%;
+ padding: 10px;
+ // background: rgba(255, 255, 255, 0.05);
+ // border: 1px solid rgba(255, 255, 255, 0.1);
+ border-radius: 4px;
+
+ .action-link {
+ color: #1890ff;
+ cursor: pointer;
+ margin-right: 10px;
+
+ &:hover {
+ color: #40a9ff;
+ text-decoration: underline;
+ }
+ }
+ }
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/http.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/http.js
new file mode 100644
index 0000000..b76ec48
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/http.js
@@ -0,0 +1,27 @@
+import { httppost,httpget } from "@/utils/request"
+import apiUrl from "@/service/apiurl"
+
+export const treeList = async (params) => {
+ const res = await httppost(apiUrl.spjk.treeList, params)
+ return res
+}
+
+export const treeListById = async (params) => {
+ const res = await httpget(`${apiUrl.spjk.treeListById}${params}`, params)
+ return res
+}
+
+export const srcData = async (params) => {
+ const res = await httpget(`${apiUrl.spjk.srcData}${params}`)
+ return res
+}
+
+export const ysyToken = async (params) => {
+ const res = await httpget(apiUrl.spjk.ysyToken)
+ return res
+}
+
+export const videoList = async (params) => {
+ const res = await httppost(apiUrl.spjk.videoList)
+ return res
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.css b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.css
new file mode 100644
index 0000000..03526b6
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.css
@@ -0,0 +1,80 @@
+.videoList {
+ height: 100%;
+}
+.videoList .treeRight {
+ width: 350px;
+ height: 100%;
+ padding: 16px 8px;
+ background-color: #fff;
+ box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
+}
+.videoList .treeRight .ant-tree-node-selected .iconSelect {
+ color: blue;
+}
+.videoList .treeLeft {
+ flex: 1;
+ margin-left: 12px;
+ background-color: #fff;
+ height: 100%;
+ padding: 16px 8px;
+ box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
+}
+.videoList .treeLeft .splitScreen {
+ height: 100%;
+}
+.videoList .treeLeft .splitScreen .borderF {
+ height: calc(100% - 40px);
+}
+.videoList .treeLeft .splitScreen .borderFa {
+ width: 100%;
+ height: 100%;
+ background: #f6f9fd;
+ border: 1px solid rgba(42, 117, 214, 0.2);
+ position: relative;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ padding-bottom: 10px;
+}
+.videoList .treeLeft .splitScreen .borderType {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+}
+.videoList .treeLeft .splitScreen .borderType .text {
+ width: 100%;
+ height: 22px;
+ font-size: 16px;
+ font-family: PingFangSC-Regular, PingFang SC;
+ font-weight: 400;
+ color: #79a2d7;
+ text-align: center;
+}
+.videoList .treeLeft .splitScreen .videoBorder {
+ border: 3px solid red;
+}
+.videoList .treeLeft .splitScreen .videoBorder1 {
+ flex: 1;
+ height: 100%;
+}
+.videoList .treeLeft .splitScreen .videoBorder4 {
+ width: 50%;
+ height: 50%;
+}
+.videoList .treeLeft .splitScreen .videoBorder9 {
+ width: 33%;
+ height: 33%;
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.js b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.js
new file mode 100644
index 0000000..683d2cf
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.js
@@ -0,0 +1,157 @@
+import TreeData from "./treeData"
+import './index.less'
+import SplitScreen from "./splitScreen"
+import { useEffect, useState } from "react"
+import { treeList, srcData, videoList, ysyToken } from './http'
+import VideoControler from "@/components/VideoCom/VideoControler"
+import { httppost } from "@/utils/request"
+import apiurl from "@/service/apiurl"
+
+
+const VideoList = () => {
+ const [videoArr, setVideo] = useState([])
+ const [count, setCount] = useState(999)
+ const [indexData, setIndex] = useState(999)
+ const [size, setSize] = useState(1)
+ const [treeListData, setTreeData] = useState([])
+ const [selectList, setSelectList] = useState()
+
+ const selectedKeys = async (list, node) => {
+ console.log(node, 'node');
+ let src = null
+ let obj = {}
+ setSelectList(list[list.length - 1])
+ if (indexData === 999) {
+ if (node.selected) {
+ console.log(node.node.relType, 'asdkjashkd');
+ src = node.node.relType == "ysy" ? (await ysyToken(node.node.indexCode)).data : (await srcData(node.node.indexCode)).data
+ obj = { ...node.node, src: src }
+ if (videoArr.length < size) {
+ setVideo([...videoArr, obj])
+ } else {
+ videoArr[size - 1] = obj
+ console.log(videoArr, '45678');
+ setVideo((videoArr) => {
+ const newA = [...videoArr]
+ return newA
+ })
+ }
+ } else {
+ let count = videoArr.findIndex(item => item.id === node.node.id)
+ setVideo(() => {
+ const newA = [...videoArr]
+ newA.splice(count, 1)
+ return newA
+ })
+ }
+ } else {
+ if (node.selected) {
+ src = node.node.relType == 'ysy' ? (await ysyToken(node.node.indexCode)).data : (await srcData(node.node.indexCode)).data
+ obj = { ...node.node, src: src }
+ videoArr[indexData] = obj
+ setVideo((videoArr) => {
+ const newA = [...videoArr]
+ return newA
+ })
+ }
+
+ }
+
+ }
+ const clickIndex = (index, item) => {
+ setIndex(index)
+ setSelectList(videoArr[index])
+ }
+ const getType = (size) => {
+ console.log(size, videoArr.slice(0, size), 'szie');
+ setSize(size)
+ setIndex(999)
+ setVideo(() => {
+ const newA = videoArr.slice(0, size)
+ console.log(newA);
+ return newA
+ })
+
+ }
+ const getTreeData = async () => {
+ const res = await treeList()
+ const res1 = await videoList()
+ const arr = res1.data.filter(item => item.menuId).map((item, index) => {
+ item.parentId = item.menuId
+ item.id = 999 + index
+ item.isLeaf = true
+ // item.icon=
+ return item
+ })
+ const arr1 = [...arr, ...res.data]
+ console.log("before", arr1);
+ setTreeData(buildTree(arr1))
+ }
+ function buildTree(data, parentId = "0") {
+ const tree = [];
+ for (const item of data) {
+ if (item.parentId == parentId) {
+ const children = buildTree(data, item.id);
+ if (children.length > 0) item.children = children;
+ tree.push(item)
+ }
+
+ }
+ return tree;
+ }
+ let timer = null;
+ // 云台控制
+ const onOperation = async (params) => {
+ let data = {
+ ...params,
+ indexCode: selectList?.indexCode,
+ action: 0
+ }
+ try {
+ const res = await httppost(apiurl.spjk.controler, data)
+ if (res.code == 200) {
+ if (timer) clearTimeout(timer)
+ timer = setTimeout(() => {
+ onOperation1(params)
+ }, 500)
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ }
+ const onOperation1 = async (params) => {
+ let data = {
+ ...params,
+ indexCode: selectList?.indexCode,
+ action: 1
+ }
+ try {
+ const res = await httppost(apiurl.spjk.controler, data)
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ useEffect(() => {
+ getTreeData()
+ }, [])
+ return (
+
+
+
+ {
+ selectList && selectList.type == 1 ?
+
+
+
+ : null
+ }
+
+
+
+ )
+}
+export default VideoList
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.less b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.less
new file mode 100644
index 0000000..826f544
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/index.less
@@ -0,0 +1,114 @@
+.videoList{
+ height: 100%;
+ .treeRight{
+ position: relative;
+ width: 350px;
+ height: 100%;
+ padding: 16px 8px;
+ color: #fff;
+ border-radius: 4px;
+ &.ptz-visible{
+ padding-bottom: 260px;
+ }
+ .ant-tree-iconEle{
+ .iconSelect{
+ // color: blue;
+ width: 25px;
+ height: 25px;
+ margin-bottom: 5px;
+ }
+ }
+
+ .ant-tree {
+ background: transparent;
+ color: #fff;
+
+ .ant-tree-node-content-wrapper {
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.1);
+ }
+ &.ant-tree-node-selected {
+ background-color: rgba(24, 144, 255, 0.3);
+ }
+ }
+ .ant-tree-title {
+ color: #fff;
+ }
+ .ant-tree-switcher {
+ color: #fff;
+ }
+ }
+ }
+ .treeLeft{
+ flex: 1;
+ margin-left: 12px;
+ // background-color: rgba(255, 255, 255, 0.05);
+ color: #fff;
+ height: 100%;
+ padding: 16px 8px;
+ border-radius: 4px;
+ // border: 1px solid rgba(255, 255, 255, 0.1);
+
+ .splitScreen{
+ height: 100%;
+ .borderF{
+ height: 100%;
+ // padding: 20px;
+ }
+ .borderFa{
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.3);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ position: relative;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ padding-bottom: 10px;
+ }
+ .borderType{
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ .text{
+ width: 100%;
+ height: 22px;
+ font-size: 16px;
+ font-family: PingFangSC-Regular, PingFang SC;
+ font-weight: 400;
+ color: rgba(255, 255, 255, 0.6);
+ text-align: center;
+ }
+ }
+ .videoBorder{
+ border: 2px solid #1890ff;
+ }
+ .videoBorder1{
+ flex: 1;
+ height: 100%;
+ }
+ .videoBorder4{
+ width: 50%;
+ height: 50%;
+ }
+ .videoBorder9{
+ width: 33%;
+ height: 33%;
+ }
+ }
+
+ }
+}
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/splitScreen.tsx b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/splitScreen.tsx
new file mode 100644
index 0000000..5420b4c
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/splitScreen.tsx
@@ -0,0 +1,100 @@
+import TreeData from "./treeData"
+import './index.less'
+import { Radio, Tabs, Empty } from "antd"
+import { useEffect, useRef, useState } from "react";
+import HFivePlayer from "@/components/VideoCom/videoPlary";
+// import HFivePlayer from "./video1";
+
+const SplitScreen: React.FC = (props: any) => {
+ const [size, setSize] = useState(1);
+ const [list, setList] = useState([]);
+ const [videoList, setVideoList] = useState([]);
+ const [num, setNum] = useState(999);
+ const [styleType, setStyle] = useState(false);
+
+
+ const onChange = (e: any) => {
+ setSize(Number(e));
+ handDate(Number(e))
+ setNum(999)
+ };
+ const handDate = (size: any) => {
+ props.getType(size)
+ console.log(new Array(size).fill(1),'4678999');
+
+ setList(new Array(size).fill(1))
+ setVideoList([])
+ }
+ const clickVideo = (index: any,item:any) => {
+ if(num == index){
+ props.clickIndex(999,item)
+ setStyle(true)
+ setNum(999)
+ }else{
+ props.clickIndex(index,item)
+ setStyle(false)
+ setNum(index)
+ }
+
+ }
+ const getNum = () => {
+ if (num === 999) {
+ // return props.videoArr.length - 1
+ } else {
+ return num
+ }
+ }
+ useEffect(() => {
+ if (props.count !== 999) {
+ setNum(props.count)
+ }
+ }, [props.count])
+ useEffect(() => {
+
+ setVideoList(props.videoArr)
+ console.log(props.videoArr,'props.videoArr');
+
+ }, [props.videoArr])
+
+ useEffect(() => {
+ onChange('4')
+ }, [])
+ const items = [
+ { key: '1', label: '单屏' },
+ { key: '4', label: '四分屏' },
+ { key: '9', label: '九分屏' },
+ ]
+ return (
+
+ {/*
*/}
+ {/* {props.videoArr} */}
+
+ {list.map((item: any, index: any) => {
+ return
clickVideo(index,item)}
+ // onClick={()=>clickVideo(index)}
+ className={[getNum() == index ? 'videoBorder' : null,
+ size == 1 ? 'videoBorder1' : null, size == 4 ? 'videoBorder4' : null,
+ size == 9 ? 'videoBorder9' : null,'borderFa'].join(' ')}>
+ {props.videoArr[index]&&
+
+ {videoList.length&&}
+
+
{videoList[index]?.name}
+
}
+ {!props.videoArr[index]&&
+

+
暂无视频数据
+
}
+
+
+ })}
+
+
+ )
+}
+export default SplitScreen
\ No newline at end of file
diff --git a/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/treeData.tsx b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/treeData.tsx
new file mode 100644
index 0000000..7739a5b
--- /dev/null
+++ b/src/views/Home/components/Business/SiQuan/components/ModalComponents/VideoList/treeData.tsx
@@ -0,0 +1,104 @@
+import { DownOutlined, VideoCameraFilled } from '@ant-design/icons';
+import React, { useEffect, useState } from 'react';
+import { Tree } from 'antd';
+
+interface DataNode {
+ title: string;
+ key: string;
+ isLeaf?: boolean;
+ children?: DataNode[];
+}
+
+let arr:any=[]
+// It's just a simple demo. You can use tree map to optimize update perf.
+const updateTreeData = (list: DataNode[], key: React.Key, children: DataNode[]): DataNode[] =>
+ list.map((node: any) => {
+ if (node.id === key) {
+ return {
+ ...node,
+ children,
+ };
+ }
+ if (node.children) {
+ return {
+ ...node,
+ children: updateTreeData(node.children, key, children),
+ };
+ }
+ return node;
+ });
+
+const TreeData = (props: any) => {
+ console.log(props.treeListData);
+
+ const [checkNode, setCheckNode] = useState([]);
+ const onSelect = async (selectedKeys: (any | never[]), info: any) => {
+ if(!info.node.isLeaf && !info.node.selected)return
+ if(selectedKeys.length < props.size){
+ setCheckNode(selectedKeys)
+ props.selectedKeys(info.selectedNodes,info)
+ }else{
+ setCheckNode((selectedKeys) => {
+ const newA:any = [...selectedKeys]
+ newA[Number(props.size)-1]=info.node.id
+
+ const arr =newA.map((item:any)=>{
+ let count = info.selectedNodes.findIndex((itemq:any) => itemq.id === item)
+ if(count !== -1){
+ return info.selectedNodes[count]
+ }
+ })
+ props.selectedKeys(arr,info)
+ return newA
+ })
+ }
+
+ };
+ const iconSelect = (a: any) => {
+ const type = a.data.type;
+ // const online = a.data.online
+ if (type == 1) {
+ return
+
+ } else if(type ==2) {
+ return
+ }
+
+ // if (a.data.isLeaf) {
+ // if(a.data.type ==1)
+ // return
+ // }
+ }
+ useEffect(()=>{
+ setCheckNode(() => {
+ const newA = props.videoArr.map((item:any)=>{
+ return item?.id
+ })
+ return newA
+ })
+ // setCheckNode(selectedKeys)
+ },[props.videoArr])
+ // useEffect(()=>{})
+ return
+ {props.treeListData.length !== 0 &&
+ }
+
;
+};
+
+export default TreeData;
\ No newline at end of file
diff --git a/src/views/Home/components/Business/SiQuan/index.js b/src/views/Home/components/Business/SiQuan/index.js
index 0f850b6..d0ad9ec 100644
--- a/src/views/Home/components/Business/SiQuan/index.js
+++ b/src/views/Home/components/Business/SiQuan/index.js
@@ -117,7 +117,7 @@ const SiQuan = () => {
tabs={modalType === 'monitor' ? tabs : (modalType === 'allweather' ? tabsAllWeather : [])}
activeTab={activeTab}
onTabChange={handleTabChange}
- width={modalType === 'cycle' ? '70%': undefined}
+ width={modalType === 'cycle' ? '70%':modalType === 'allweather' ? '90%': undefined}
>
{/* Content changes based on activeTab */}
diff --git a/src/views/Home/components/UI/YearSelect/index.less b/src/views/Home/components/UI/YearSelect/index.less
index 434245a..3778481 100644
--- a/src/views/Home/components/UI/YearSelect/index.less
+++ b/src/views/Home/components/UI/YearSelect/index.less
@@ -6,7 +6,7 @@
input {
color: #fff !important;
- font-size: 16px;
+ font-size: 14px;
font-weight: normal; // Changed from bold to normal
cursor: pointer;
}