Compare commits
No commits in common. "d246a90eabcb16dc56cf5c057ed6820edeeffdd6" and "6fd8692b8deee2a0e7fcb1002b889a5a533ce1b5" have entirely different histories.
d246a90eab
...
6fd8692b8d
|
|
@ -25,8 +25,7 @@
|
||||||
"craco-less": "2.1.0-alpha.0",
|
"craco-less": "2.1.0-alpha.0",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"echarts": "^4.9.0",
|
"echarts": "^4.9.0",
|
||||||
"echarts-for-react": "3.0.2",
|
"echarts-for-react": "^3.0.2",
|
||||||
"ezuikit-js": "8.0.4-beta.1",
|
|
||||||
"http-proxy-middleware": "^2.0.6",
|
"http-proxy-middleware": "^2.0.6",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 567 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 140 B |
|
Before Width: | Height: | Size: 199 B |
|
Before Width: | Height: | Size: 143 B |
|
Before Width: | Height: | Size: 259 B |
|
Before Width: | Height: | Size: 651 B |
|
Before Width: | Height: | Size: 269 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 226 B |
|
Before Width: | Height: | Size: 153 B |
|
Before Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 1014 B |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 619 B |
|
After Width: | Height: | Size: 589 B |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 646 B |
|
Before Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 664 B |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 964 B |
|
Before Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 604 B |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
|
@ -1,76 +0,0 @@
|
||||||
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 (
|
|
||||||
<div className='controler-wrap'>
|
|
||||||
<div className='title'>云台控制</div>
|
|
||||||
<div >
|
|
||||||
<span className='sub-title'>当前设备:</span>
|
|
||||||
<span >{selectItem?.name}</span>
|
|
||||||
</div>
|
|
||||||
{/* <div className='sub-title-name'>{selectItem?.name}</div> */}
|
|
||||||
<div className='controler-box'>
|
|
||||||
<div className='left'>
|
|
||||||
{ctrBtnArr.map((item, index) =>
|
|
||||||
<div key={index} onClick={() => setSelect({...selectData,command:item?.value})}>
|
|
||||||
<div className='ctr-btn' title={item?.title}>
|
|
||||||
<img alt='' src={`${process.env.PUBLIC_URL}/assets/images/crt${index}.png`} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className='right'>
|
|
||||||
<div className='slider-wrap'>
|
|
||||||
<Slider
|
|
||||||
marks={marks}
|
|
||||||
defaultValue={30}
|
|
||||||
step={10}
|
|
||||||
min={10}
|
|
||||||
max={80}
|
|
||||||
onChange={(e) => {setSelect({...selectData,speed:e})}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='zoom-wrap'>
|
|
||||||
<div className='zoom-btn' title='缩小' onClick={() => setSelect({...selectData,command:"ZOOM_OUT"})}>
|
|
||||||
<img alt='' src={`${process.env.PUBLIC_URL}/assets/images/suoxiao.png`} style={{width:25,height:25}}/>
|
|
||||||
</div>
|
|
||||||
<div className='zoom-btn' title='放大' onClick={() => setSelect({...selectData,command:"ZOOM_IN"})}>
|
|
||||||
<img alt='' src={`${process.env.PUBLIC_URL}/assets/images/fangda.png`} />
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p className='tips'>注:云台操作的生效根据网络状况延时约1-10秒</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,243 +0,0 @@
|
||||||
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 (
|
|
||||||
<div ref={parentRef} style={{ height: '100%', width: '100%' }}>
|
|
||||||
<Spin tip="Loading" spinning={isLoading} size="small">
|
|
||||||
<div style={{ width: width, height: height }} onDoubleClick={() => playerYsy.current.fullScreen()}>
|
|
||||||
<div id={'player' + playerID} style={{ width: width, height: height}} >
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Spin>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
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': '对讲不支持这种音频编码格式',
|
|
||||||
}
|
|
||||||
|
|
@ -238,22 +238,6 @@ input:-webkit-autofill:active {
|
||||||
&:not(.ant-select-disabled):hover .ant-select-selector {
|
&:not(.ant-select-disabled):hover .ant-select-selector {
|
||||||
border-color: #3b7cff !important;
|
border-color: #3b7cff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiple Select Tags
|
|
||||||
&.ant-select-multiple .ant-select-selection-item {
|
|
||||||
background: rgba(0, 160, 233, 0.2); // Light blue transparent
|
|
||||||
border: 1px solid #00a0e9;
|
|
||||||
color: #fff;
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
|
|
||||||
.ant-select-selection-item-remove {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dropdowns (Select, DatePicker, etc.)
|
// Dropdowns (Select, DatePicker, etc.)
|
||||||
|
|
@ -285,56 +269,16 @@ input:-webkit-autofill:active {
|
||||||
th { color: #fff; }
|
th { color: #fff; }
|
||||||
td { color: rgba(255, 255, 255, 0.8); }
|
td { color: rgba(255, 255, 255, 0.8); }
|
||||||
}
|
}
|
||||||
.ant-picker-time-panel-cell-inner{
|
|
||||||
color: #fff!important;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-time-panel-column > li.ant-picker-time-panel-cell-selected .ant-picker-time-panel-cell-inner{
|
|
||||||
background: #00a0e9;
|
|
||||||
}
|
|
||||||
.ant-picker-time-panel-column > li.ant-picker-time-panel-cell .ant-picker-time-panel-cell-inner:hover{
|
|
||||||
background: #013056 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.ant-picker-cell {
|
.ant-picker-cell {
|
||||||
color: rgba(255, 255, 255, 0.5); // Default inactive color
|
|
||||||
|
|
||||||
&:hover .ant-picker-cell-inner {
|
&:hover .ant-picker-cell-inner {
|
||||||
background: rgba(0, 160, 233, 0.3) !important;
|
background: rgba(0, 160, 233, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-in-view {
|
&-in-view {
|
||||||
color: #fff; // Active month days
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix: Remove white background from cells
|
|
||||||
.ant-picker-cell-inner {
|
|
||||||
background: transparent !important;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-selected .ant-picker-cell-inner {
|
&-selected .ant-picker-cell-inner {
|
||||||
background: #00a0e9 !important; // Project Cyan
|
background: #3b7cff !important;
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-today .ant-picker-cell-inner {
|
|
||||||
border: 1px solid #00a0e9;
|
|
||||||
&::before {
|
|
||||||
border: 1px solid #00a0e9 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range Selection
|
|
||||||
&-in-range::before {
|
|
||||||
background: rgba(0, 160, 233, 0.2) !important;
|
|
||||||
}
|
|
||||||
&-range-start .ant-picker-cell-inner,
|
|
||||||
&-range-end .ant-picker-cell-inner {
|
|
||||||
background: #00a0e9 !important;
|
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,76 +308,14 @@ input:-webkit-autofill:active {
|
||||||
// 4. Button Component (Ghost Blue Style)
|
// 4. Button Component (Ghost Blue Style)
|
||||||
// ==============================================================================
|
// ==============================================================================
|
||||||
.ant-btn-ghost-blue {
|
.ant-btn-ghost-blue {
|
||||||
background: rgba(18, 56, 102, 0.6) !important;
|
background: rgba(24, 144, 255, 0.3) !important; // Slight blue tint background
|
||||||
border: 1px solid #00a0e9 !important;
|
border: 1px solid #1890ff !important;
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
box-shadow: 0 0 8px rgba(0, 160, 233, 0.2) inset;
|
box-shadow: 0 0 8px rgba(24, 144, 255, 0.3) inset;
|
||||||
}
|
|
||||||
|
|
||||||
.ant-btn.ant-btn-ghost-blue,
|
|
||||||
.ant-btn.ant-btn-primary.ant-btn-ghost-blue {
|
|
||||||
background: rgba(18, 56, 102, 0.6) !important;
|
|
||||||
border-color: #00a0e9 !important;
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-btn.ant-btn-ghost-blue:hover,
|
|
||||||
.ant-btn.ant-btn-ghost-blue:focus,
|
|
||||||
.ant-btn.ant-btn-primary.ant-btn-ghost-blue:hover,
|
|
||||||
.ant-btn.ant-btn-primary.ant-btn-ghost-blue:focus {
|
|
||||||
background: rgba(0, 160, 233, 0.3) !important;
|
|
||||||
border-color: #33b5ed !important;
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==============================================================================
|
|
||||||
// 5. Timeline Component
|
|
||||||
// ==============================================================================
|
|
||||||
.ant-timeline {
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.ant-timeline-item-label {
|
|
||||||
color: #fff;
|
|
||||||
width: calc(50% - 12px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-tail {
|
|
||||||
border-left: 2px solid rgba(0, 160, 233, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-head {
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-head-blue {
|
|
||||||
color: #00a0e9;
|
|
||||||
border-color: #00a0e9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==============================================================================
|
|
||||||
// 6. Button Component (Global Override for Dark Theme)
|
|
||||||
// ==============================================================================
|
|
||||||
.ant-btn {
|
|
||||||
&.ant-btn-primary {
|
|
||||||
background: #00a0e9; // Project Theme Cyan
|
|
||||||
border-color: #00a0e9;
|
|
||||||
&:hover, &:focus {
|
|
||||||
background: #33b5ed;
|
|
||||||
border-color: #33b5ed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default button style in dark mode
|
|
||||||
&:not(.ant-btn-primary):not(.ant-btn-link):not(.ant-btn-text):not(.ant-btn-danger) {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
&:hover, &:focus {
|
&:hover, &:focus {
|
||||||
color: #00a0e9;
|
background: rgba(24, 144, 255, 0.3) !important;
|
||||||
border-color: #00a0e9;
|
border-color: #40a9ff !important;
|
||||||
}
|
color: #fff !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
export const config = {
|
|
||||||
ip: 'http://223.75.53.141:83',
|
|
||||||
minioIp:"http://223.75.53.141:9100/gs-ss"
|
|
||||||
}
|
|
||||||
|
|
@ -38,11 +38,11 @@ code {
|
||||||
background: transparent; // Override white background
|
background: transparent; // Override white background
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
background: rgba(0, 160, 233, 0.5);
|
background: #ECF2F9;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(0, 160, 233, 0.8);
|
background: #ECF2F9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -58,10 +58,10 @@ code {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ant-table-content::-webkit-scrollbar-thumb,.ant-table-body::-webkit-scrollbar-thumb {
|
.ant-table-content::-webkit-scrollbar-thumb,.ant-table-body::-webkit-scrollbar-thumb {
|
||||||
background: rgba(0, 160, 233, 0.5);//#ECF2F9;
|
background: #C1C1C1;//#ECF2F9;
|
||||||
}
|
}
|
||||||
.ant-table-content::-webkit-scrollbar-thumb,.ant-table-body::-webkit-scrollbar-thumb:hover {
|
.ant-table-content::-webkit-scrollbar-thumb,.ant-table-body::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(0, 160, 233, 0.8);//#ECF2F9;
|
background: #C1C1C1;//#ECF2F9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-title{
|
.ant-modal-title{
|
||||||
|
|
@ -70,7 +70,68 @@ code {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.adcdTreeSelectorBox{
|
||||||
|
width: 230px;
|
||||||
|
height:calc( 100vh - 145px );
|
||||||
|
margin:0 20px 0 0;
|
||||||
|
background: #fff;
|
||||||
|
//box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
position:relative;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.treeBox{
|
||||||
|
//height:calc( 100vh - 158px );
|
||||||
|
overflow: hidden auto;
|
||||||
|
padding: 8px 0 0 2px;
|
||||||
|
border-top: 1px solid #d9d9d9;
|
||||||
|
}
|
||||||
|
.ant-input-group .ant-input,.ant-btn{
|
||||||
|
border:0;
|
||||||
|
}
|
||||||
|
.ant-input-affix-wrapper{
|
||||||
|
border:0;
|
||||||
|
//border-right: 2px solid #d9d9d9;
|
||||||
|
}
|
||||||
|
.checkboxBox{
|
||||||
|
position:absolute;
|
||||||
|
top:34px;
|
||||||
|
width:100%;
|
||||||
|
height:30px;
|
||||||
|
background:#fff;
|
||||||
|
border-top: 1px solid #d9d9d9;
|
||||||
|
padding-top:4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.adcdTreeTableBox{
|
||||||
|
width:calc( 100vw - 602px );
|
||||||
|
height:calc( 100vh - 143px );
|
||||||
|
background: #fff;
|
||||||
|
//box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.CrudAdcdTreeTableBox{
|
||||||
|
width:calc( 100vw - 602px );
|
||||||
|
height:calc( 100vh - 143px );
|
||||||
|
background: #fff;
|
||||||
|
//box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.radarVideoModal{
|
||||||
|
.ant-modal-content{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.ant-modal-body{
|
||||||
|
width: 100%;
|
||||||
|
height: calc( 100% - 120px );
|
||||||
|
padding: 0 30px;
|
||||||
|
}
|
||||||
|
.ant-tabs-tabpane{
|
||||||
|
height: calc( 76vh - 120px );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.toolbarBox{
|
.toolbarBox{
|
||||||
.ant-form-item{
|
.ant-form-item{
|
||||||
|
|
|
||||||
|
|
@ -7,24 +7,6 @@ const apiurl = {
|
||||||
router: service + '/getRouters',
|
router: service + '/getRouters',
|
||||||
role: service + '/system/menu/list'
|
role: service + '/system/menu/list'
|
||||||
},
|
},
|
||||||
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: {
|
sq: {
|
||||||
qfg: {
|
qfg: {
|
||||||
info:service + '/attResBase/list'
|
info:service + '/attResBase/list'
|
||||||
|
|
@ -32,45 +14,7 @@ const apiurl = {
|
||||||
qys: {
|
qys: {
|
||||||
yhjmhPage:service + '/iaCFlrvvlg/page',
|
yhjmhPage:service + '/iaCFlrvvlg/page',
|
||||||
qsydwPage:service + '/iaCBsnssinfo/page',
|
qsydwPage:service + '/iaCBsnssinfo/page',
|
||||||
wxqPage: service + '/iaCDanad/page',
|
wxqPage:service + '/iaCDanad/page',
|
||||||
gcys: {
|
|
||||||
buildInfo: service + '/attResBuilding/info',
|
|
||||||
krlineList:service + '/stZvarlB/list',
|
|
||||||
xllineList:service + '/stZqrlB/list',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
qth: {
|
|
||||||
rainList: {
|
|
||||||
list: service + '/attResBase/rainBasinDivision/queryStPptnDetails/list',
|
|
||||||
queryStPptnDetails: service + '/attResBase/rainBasinDivision/queryStPptnDetails/stcd', //实时雨量近几小时数据
|
|
||||||
tableList: service + '/attResBase/rainBasinDivision/queryStStbprpPerHour/StcdAndStartTimeAndEndTime',//小时历史雨量表格数据
|
|
||||||
chartList: service + '/attResBase/rainBasinDivision/queryStStbprpPerHourChart/StcdAndStartTimeAndEndTime',//小时历史雨量图数据
|
|
||||||
dayTableList: service + '/attResBase/rainBasinDivision/queryStStbprpPerDay/StcdAndStartTimeAndEndTime',//日历史雨量表格数据
|
|
||||||
dayChartList: service + '/attResBase/rainBasinDivision/queryStStbprpPerDayChart/StcdAndStartTimeAndEndTime',//日小时历史雨量图数据
|
|
||||||
nearbyHistory:service + '/attResBase/maxRain' //获取历史近几小时数据
|
|
||||||
},
|
|
||||||
reservoir: {
|
|
||||||
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: {
|
|
||||||
list: service + '/projectEvents/doc/page',
|
|
||||||
export: service + '/projectEvents/export',
|
|
||||||
info :service + '/wholeCycle/get'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sz: {
|
|
||||||
jqjz: {
|
|
||||||
budgetInfo: service + '/fundBudget/get/',
|
|
||||||
manageInfo: service + '/screen/mechanisms/equipment',
|
|
||||||
managePic: service + '/screen/manageHouseImg/get',
|
|
||||||
wzPage:service + '/rescue/goods/page/query'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import apiurl from "./apiurl";
|
|
||||||
import { httpget, httppost } from "@/utils/request";
|
|
||||||
import {message} from 'antd';
|
|
||||||
|
|
||||||
//雨情列表
|
|
||||||
export async function rainlist(params) {
|
|
||||||
const {data, code, msg} = await httppost(apiurl.station.rainlist, 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||[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//水库列表
|
|
||||||
export async function reservoirlist(params) {
|
|
||||||
const {data, code, msg} = await httppost(apiurl.station.reservoirlist, 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||[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 流量站
|
|
||||||
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||[];
|
|
||||||
}
|
|
||||||
|
|
@ -49,15 +49,6 @@ async function send(url, options) {
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
async function sendFile(url, options) {
|
|
||||||
try {
|
|
||||||
const res = await request(url, options,'blob');
|
|
||||||
return res;
|
|
||||||
} catch (e) {
|
|
||||||
message.error(e);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function httpget(url, data = {}) {
|
export function httpget(url, data = {}) {
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
@ -115,7 +106,7 @@ export function ry_httpget(url, data = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function httppost(url, data = {},type) {
|
export function httppost(url, data = {}) {
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -126,8 +117,8 @@ export function httppost(url, data = {},type) {
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
};
|
};
|
||||||
const fun = type == 'blob' ? sendFile(url, options) : send(url, options);
|
|
||||||
return fun;
|
return send(url, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function httpPostFile(url, data = {}) {
|
export function httpPostFile(url, data = {}) {
|
||||||
|
|
|
||||||
|
|
@ -19,22 +19,6 @@ export function objType(obj) {
|
||||||
}
|
}
|
||||||
return typeof obj === 'object' || typeof obj === 'function' ? class2type[toString.call(obj)] || 'object' : typeof obj;
|
return typeof obj === 'object' || typeof obj === 'function' ? class2type[toString.call(obj)] || 'object' : typeof obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const exportFile = (name, res) => {
|
|
||||||
// 创建a标签
|
|
||||||
let blob = new Blob([res],{ type: 'application/octet-stream' })
|
|
||||||
// 创建下载链接
|
|
||||||
const downloadUrl = URL.createObjectURL(blob);
|
|
||||||
// 创建下载链接的 <a> 元素
|
|
||||||
const downloadLink = document.createElement("a");
|
|
||||||
downloadLink.href = downloadUrl;
|
|
||||||
downloadLink.download = name;
|
|
||||||
// 模拟点击下载链接
|
|
||||||
downloadLink.click();
|
|
||||||
// 清理创建的 URL 对象
|
|
||||||
URL.revokeObjectURL(downloadUrl);
|
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* 是否数组
|
* 是否数组
|
||||||
* @param {object} obj 需要判断的对象
|
* @param {object} obj 需要判断的对象
|
||||||
|
|
|
||||||
|
|
@ -1,223 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { Table } from 'antd';
|
|
||||||
import CommonModal from '@/views/Home/components/UI/CommonModal';
|
|
||||||
import arrowIcon from '@/assets/images/card/arrow.png';
|
|
||||||
import smallCard from '@/assets/images/card/smallCard.png';
|
|
||||||
import wrj from '@/assets/images/card/wrj.png';
|
|
||||||
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: '站名',
|
|
||||||
dataIndex: 'stnm',
|
|
||||||
key: 'stnm',
|
|
||||||
align: 'center',
|
|
||||||
width: 140,
|
|
||||||
ellipsis: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '今日',
|
|
||||||
dataIndex: 'today',
|
|
||||||
key: 'today',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '昨日',
|
|
||||||
dataIndex: 'yesterdayDrp',
|
|
||||||
key: 'yesterdayDrp',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '24h预报',
|
|
||||||
dataIndex: 'h24',
|
|
||||||
key: 'h24',
|
|
||||||
align: 'h24',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
// Reservoir Data
|
|
||||||
const reservoirData = [
|
|
||||||
{ 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', clickable: true,underline: true },
|
|
||||||
{ label: '当前库容', value: reservoirItem?.nowCap, unit: '万m³' },
|
|
||||||
{ label: '有效库容', value: reservoirItem?.effectiveCap, unit: '万m³' },
|
|
||||||
];
|
|
||||||
|
|
||||||
//指定水库获取
|
|
||||||
const getReservoir = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sq.qth.reservoir.list);
|
|
||||||
if (result.code == 200) {
|
|
||||||
setReservoirItem(result.data[0])
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const getRainList = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sq.qth.rainList.list);
|
|
||||||
if (result.code == 200) {
|
|
||||||
setRainList(result.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
|
||||||
getRainList()
|
|
||||||
getReservoir()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const openUavModal = (key) => {
|
|
||||||
setUavActiveTab(key);
|
|
||||||
setUavModalVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="all-weather-control">
|
|
||||||
{/* 雨情 Section */}
|
|
||||||
<div className="section rain-section">
|
|
||||||
<div className="section-header">
|
|
||||||
<div className="title-wrapper">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span className="section-title">雨情</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Table
|
|
||||||
columns={rainColumns}
|
|
||||||
dataSource={rainList}
|
|
||||||
pagination={false}
|
|
||||||
size="small"
|
|
||||||
rowKey={'stcd'}
|
|
||||||
rowClassName={(record) => record.isTotal ? 'total-row' : ''}
|
|
||||||
bordered={false}
|
|
||||||
scroll={{ y: 300 }}
|
|
||||||
onRow={(record) => ({
|
|
||||||
onClick: () => {
|
|
||||||
setSelectedStcd(record);
|
|
||||||
setDetailVisible(true);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<CommonModal
|
|
||||||
title={selectedStcd?.stnm}
|
|
||||||
visible={detailVisible}
|
|
||||||
onClose={() => setDetailVisible(false)}
|
|
||||||
width={'70%'}
|
|
||||||
>
|
|
||||||
<RightPanel stcd={selectedStcd?.stcd} cleanMode={true} />
|
|
||||||
</CommonModal>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 水库水情 Section */}
|
|
||||||
<div className="section reservoir-section">
|
|
||||||
<div className="section-header">
|
|
||||||
<div className="title-wrapper">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span className="section-title">水库水情</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="reservoir-cards">
|
|
||||||
{reservoirData.map((item, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="reservoir-card"
|
|
||||||
style={{ backgroundImage: `url(${smallCard})`, cursor: item.clickable ? 'pointer' : 'default' }}
|
|
||||||
onClick={() => {
|
|
||||||
if (item.clickable) {
|
|
||||||
setReservoirVisible(true)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className={`value ${item.isPrimary ? 'primary' : ''} ${item.label == '距汛限'?item.isNegative ? 'negative' : 'positive':""}`}>
|
|
||||||
<span className={`num ${item.underline ? 'underline' : ''}`}>{item.value}</span>
|
|
||||||
<span className="unit">{item.unit}</span>
|
|
||||||
</div>
|
|
||||||
<div className="label">{item.label}</div>
|
|
||||||
{item.upArrow && <span className="arrow-up">↑</span>}
|
|
||||||
{item.downArrow && <span className="arrow-down">↓</span>}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<CommonModal
|
|
||||||
title={reservoirItem?.stnm || '水库水情'}
|
|
||||||
visible={reservoirVisible}
|
|
||||||
onClose={() => setReservoirVisible(false)}
|
|
||||||
width={'90%'}
|
|
||||||
>
|
|
||||||
<ReservoirPanel stcd={reservoirItem?.stcd} cleanMode={true} />
|
|
||||||
</CommonModal>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* 无人机 Section */}
|
|
||||||
<div className="section uav-section">
|
|
||||||
<div className="section-header">
|
|
||||||
<div className="title-wrapper">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span className="section-title">无人机</span>
|
|
||||||
</div>
|
|
||||||
<span className="link" onClick={()=>setVideoOpen(true)}>视频墙</span>
|
|
||||||
</div>
|
|
||||||
<div className="uav-content">
|
|
||||||
<div
|
|
||||||
className="uav-image"
|
|
||||||
style={{ backgroundImage: `url(${wrj})` }}
|
|
||||||
/>
|
|
||||||
<div className="uav-actions">
|
|
||||||
<div className="uav-button" onClick={() => openUavModal('1')}>直播画面</div>
|
|
||||||
<div className="uav-button" onClick={() => openUavModal('2')}>巡查任务</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<CommonModal
|
|
||||||
title={'视频监控'}
|
|
||||||
visible={videoOpen}
|
|
||||||
onClose={() => setVideoOpen(false)}
|
|
||||||
width={'80%'}
|
|
||||||
>
|
|
||||||
<VideoList />
|
|
||||||
</CommonModal>
|
|
||||||
<CommonModal
|
|
||||||
title={'无人机巡查'}
|
|
||||||
visible={uavModalVisible}
|
|
||||||
onClose={() => setUavModalVisible(false)}
|
|
||||||
width={'70%'}
|
|
||||||
tabs={uavTabs}
|
|
||||||
activeTab={uavActiveTab}
|
|
||||||
onTabChange={setUavActiveTab}
|
|
||||||
>
|
|
||||||
<UAVModal activeKey={uavActiveTab} />
|
|
||||||
</CommonModal>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AllWeatherControl;
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
.all-weather-control {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
color: #fff;
|
|
||||||
padding: 5px;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
// Global Scrollbar
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
&:last-child {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 8px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #fff; // Matches the orange/gold color in screenshot
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: #00eaff;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: underline;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rain Table Styling
|
|
||||||
.rain-section {
|
|
||||||
.ant-table-wrapper {
|
|
||||||
.ant-table {
|
|
||||||
background: transparent;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.ant-table-thead > tr > th {
|
|
||||||
background: rgba(0, 70, 110, 0.6);
|
|
||||||
color: #fff;
|
|
||||||
border-bottom: none;
|
|
||||||
padding: 8px 4px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-tbody > tr > td {
|
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
color: #fff;
|
|
||||||
padding: 8px 4px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-tbody > tr:hover > td {
|
|
||||||
background: rgba(255, 255, 255, 0.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zebra striping if needed, or just transparent
|
|
||||||
.ant-table-tbody > tr.ant-table-row:nth-child(even) {
|
|
||||||
background-color: rgba(255, 255, 255, 0.05);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-placeholder {
|
|
||||||
background: transparent;
|
|
||||||
.ant-empty-description {
|
|
||||||
color: rgba(255,255,255,0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.reservoir-section {
|
|
||||||
.reservoir-cards {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.reservoir-card {
|
|
||||||
width: calc((100% - 20px) / 3);
|
|
||||||
margin-bottom: 5px;
|
|
||||||
margin-right: 10px;
|
|
||||||
&:nth-child(3n) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
height: 60px; // Adjust based on design
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00D8FF;
|
|
||||||
.num {
|
|
||||||
display: inline-block;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
.num.underline {
|
|
||||||
border-bottom: 1px solid #00a0e9;
|
|
||||||
}
|
|
||||||
.unit {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: normal;
|
|
||||||
margin-left: 2px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.positive { color: #ff4d52; }
|
|
||||||
&.negative { color: #68c639; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #9DD2E4;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-up,.arrow-down {
|
|
||||||
color: #ff4d4f;
|
|
||||||
position: absolute;
|
|
||||||
right: 5px;
|
|
||||||
top: 5px;
|
|
||||||
}
|
|
||||||
.arrow-down{
|
|
||||||
color: #68c639;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UAV Styling
|
|
||||||
.uav-section {
|
|
||||||
.uav-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 100px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.uav-image {
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
|
||||||
background-size: contain;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.uav-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 15px;
|
|
||||||
margin-left: 10px;
|
|
||||||
|
|
||||||
.uav-button {
|
|
||||||
width: 120px;
|
|
||||||
height: 36px;
|
|
||||||
line-height: 36px;
|
|
||||||
text-align: center;
|
|
||||||
background: rgba(18, 56, 102, 0.6);
|
|
||||||
border: 1px solid #00a0e9;
|
|
||||||
border-radius: 4px;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 14px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s;
|
|
||||||
box-shadow: 0 0 5px rgba(0, 160, 233, 0.3);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 160, 233, 0.4);
|
|
||||||
box-shadow: 0 0 10px rgba(0, 160, 233, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connecting lines visualization (optional, if needed to match 1:1 perfectly)
|
|
||||||
.connection-line {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
import React,{useState,useEffect} from 'react';
|
|
||||||
import smallCard from '@/assets/images/card/smallCard.png';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import { httpget } from '@/utils/request';
|
|
||||||
import PdfView from '@/views/Home/components/UI/PdfView';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const ManagementCycle = () => {
|
|
||||||
const [info, setInfo] = useState({})
|
|
||||||
const [pdfVisible, setPdfVisible] = useState(false);
|
|
||||||
const [pdfConfig, setPdfConfig] = useState({ title: '', url: '', fileId: '' });
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{ label: '安全鉴定', value: info?.identifyType },
|
|
||||||
{ label: '病险水库', value: info?.isDanger },
|
|
||||||
{ label: '除险加固', value: info?.startDate },
|
|
||||||
{ label: '降等报废', value: info?.implementationMeasure },
|
|
||||||
{
|
|
||||||
label: '调度规则',
|
|
||||||
value: info?.dispatchTime,
|
|
||||||
underline: true,
|
|
||||||
clickable: true,
|
|
||||||
fileId:info?.dispatchFileIds?.length? info?.dispatchFileIds[0]:undefined // Assuming this field exists
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '应急预案',
|
|
||||||
value: info?.emergencyTime,
|
|
||||||
underline: true,
|
|
||||||
clickable: true,
|
|
||||||
fileId:info?.emergencyFileIds?.length? info?.emergencyFileIds[0]:undefined // Assuming this field exists
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleItemClick = (item) => {
|
|
||||||
if (!item.fileId) return;
|
|
||||||
const url = '/gunshiApp/ss/resPlanB/file/download/';
|
|
||||||
// if (!item?.dispatchFileIds || item?.dispatchFileIds.length) return;
|
|
||||||
// const field = item.label == '调度规程' ? item?.dispatchFileIds[0] + '' :
|
|
||||||
// item?.emergencyFileIds[0] + ''
|
|
||||||
if (item.clickable) {
|
|
||||||
setPdfConfig({
|
|
||||||
title: item.label,
|
|
||||||
url,
|
|
||||||
fileId:item.fileId
|
|
||||||
});
|
|
||||||
setPdfVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getInfo = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sq.qzq.info)
|
|
||||||
if (result.code == 200) {
|
|
||||||
setInfo(result.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getInfo()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="management-cycle">
|
|
||||||
<div className="card-grid">
|
|
||||||
{data.map((item, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="cycle-card"
|
|
||||||
style={{
|
|
||||||
backgroundImage: `url(${smallCard})`,
|
|
||||||
cursor: item.clickable ? 'pointer' : 'default'
|
|
||||||
}}
|
|
||||||
onClick={() => handleItemClick(item)}
|
|
||||||
>
|
|
||||||
<div className={`value-wrapper ${item.underline ? 'underlined' : ''}`}>
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
</div>
|
|
||||||
<div className="label">{item.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<PdfView
|
|
||||||
visible={pdfVisible}
|
|
||||||
title={pdfConfig.title}
|
|
||||||
url={pdfConfig.url}
|
|
||||||
fileId={pdfConfig.fileId}
|
|
||||||
onClose={() => setPdfVisible(false)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ManagementCycle;
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
.management-cycle {
|
|
||||||
height: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
// Global Scrollbar
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-grid {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
height: 100%;
|
|
||||||
align-content: flex-start;
|
|
||||||
|
|
||||||
.cycle-card {
|
|
||||||
width: calc((100% - 20px) / 3);
|
|
||||||
height: 70px; // Slightly taller to accommodate text comfortably
|
|
||||||
margin-bottom: 15px;
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
&:nth-child(3n) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.value-wrapper {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
&.underlined {
|
|
||||||
border-bottom: 1px solid #00a0e9; // Match the blue theme
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00D8FF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
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 (
|
|
||||||
<div className="right-panel">
|
|
||||||
<div className="panel-header" >
|
|
||||||
<div className="query-label"><span className="dot"></span>数据查询</div>
|
|
||||||
<div className="query-controls" style={cleanMode ? { marginLeft: 0 } : {}}>
|
|
||||||
<RangePicker
|
|
||||||
showTime
|
|
||||||
value={dates}
|
|
||||||
onChange={setDates}
|
|
||||||
style={{ width: 340 }}
|
|
||||||
format="YYYY-MM-DD HH:mm"
|
|
||||||
allowClear={false}
|
|
||||||
/>
|
|
||||||
<Button type="primary" className="ant-btn-ghost-blue" icon={<SearchOutlined />} onClick={handleSearch} loading={loading}>
|
|
||||||
查询
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="table-chart-layout">
|
|
||||||
<div className="data-table">
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={historyList}
|
|
||||||
size="small"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: 400 }}
|
|
||||||
rowKey="tm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="chart-view">
|
|
||||||
<div className="chart-container">
|
|
||||||
<ReactEcharts
|
|
||||||
option={option}
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
notMerge={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bottom-stats-grid">
|
|
||||||
<div className="grid-header">最大瞬时流量(m³/s)</div>
|
|
||||||
<div className="grid-header">累计水量(万m³)</div>
|
|
||||||
<div className="grid-value">
|
|
||||||
{maxInfo?.maxQ ?? '-'}
|
|
||||||
{maxInfo?.maxQTm &&<span style={{fontSize: 12, color: '#aaa', marginLeft: 8}}>({moment(maxInfo?.maxQTm).format('YYYY-MM-DD HH:mm:ss')})</span>}
|
|
||||||
</div>
|
|
||||||
<div className="grid-value">{maxInfo?.totalWater ?? '-'}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
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 (
|
|
||||||
<div className="flow-panel-container">
|
|
||||||
<div className="main-content">
|
|
||||||
<div className="left-panel">
|
|
||||||
<div className="panel-header">
|
|
||||||
<div className="query-label"><span className="dot"></span>实时流量</div>
|
|
||||||
{!cleanMode && (
|
|
||||||
<div className="station-select">
|
|
||||||
<span>站点:</span>
|
|
||||||
<Select
|
|
||||||
value={selected}
|
|
||||||
onChange={handleStationChange}
|
|
||||||
style={{ width: 200 }}
|
|
||||||
options={selectList}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="update-time-banner">
|
|
||||||
流量最新上报时间:{detail?.tm ?? moment().format('YYYY-MM-DD HH:mm:ss')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="data-list">
|
|
||||||
{stats.map((item, idx) => (
|
|
||||||
<div key={idx} className="data-item">
|
|
||||||
<div className="label">{item.label}:</div>
|
|
||||||
<div className="value-container">
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
<span className="unit">{item.unit}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<RightPanel stcd={selected} cleanMode={cleanMode} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FlowPanel;
|
|
||||||
|
|
@ -1,199 +0,0 @@
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,235 +0,0 @@
|
||||||
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 }) {
|
|
||||||
const days = moment().diff(moment().startOf('year'), 'days') + 1;
|
|
||||||
const defaultRange = [
|
|
||||||
moment().subtract(7, 'days').add(1, 'hour').set({ minute: 0, second: 0 }),
|
|
||||||
moment().add(1, 'hour').set({ minute: 0, second: 0 }),
|
|
||||||
];
|
|
||||||
|
|
||||||
const [dates, setDates] = useState(defaultRange);
|
|
||||||
const [viewMode, setViewMode] = useState('hour');
|
|
||||||
const [historyTableList, setHistoryTableList] = useState([]);
|
|
||||||
const [historyChartList, setHistoryChartList] = useState({});
|
|
||||||
const [historyRainDetail, sethistoryRainDetail] = useState({});
|
|
||||||
const [rainDetail, setRainDetail] = useState({});
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ title: '时间', dataIndex: 'time', key: 'time', align: 'center', width: 200 },
|
|
||||||
{
|
|
||||||
title: viewMode === 'hour' ? '小时雨量(mm)' : '日雨量(mm)',
|
|
||||||
dataIndex: 'sumDrp',
|
|
||||||
key: 'sumDrp',
|
|
||||||
align: 'center',
|
|
||||||
render: (rec) => <span>{rec ?? '-'}</span>,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const option = useMemo(() => {
|
|
||||||
return drpOption({ echartData: historyChartList });
|
|
||||||
}, [historyChartList]);
|
|
||||||
|
|
||||||
const bottomStats = [
|
|
||||||
{ label: '最大1h雨量(mm)', value: historyRainDetail?.h1 ?? '-' },
|
|
||||||
{ label: '最大3h雨量(mm)', value: historyRainDetail?.h3 ?? '-' },
|
|
||||||
{ label: '最大6h雨量(mm)', value: historyRainDetail?.h6 ?? '-' },
|
|
||||||
{ label: '最大12h雨量(mm)', value: historyRainDetail?.h12 ?? '-' },
|
|
||||||
{ label: '本年降雨天数', value: rainDetail?.yearDrpDay, suffix: true, total: days },
|
|
||||||
{ label: '今日雨量(mm)', value: rainDetail?.today ?? '-' },
|
|
||||||
{ label: '昨日雨量(mm)', value: rainDetail?.yesterdayDrp ?? '-' },
|
|
||||||
{ label: '本月雨量(mm)', value: rainDetail?.monthDrp ?? '-' },
|
|
||||||
{ label: '本年雨量(mm)', value: rainDetail?.yearDrp ?? '-' },
|
|
||||||
{ label: '本年最大日雨量(mm)', value: rainDetail?.maxDrp ?? '-', suffix: true, total: rainDetail?.maxDrpTime },
|
|
||||||
];
|
|
||||||
|
|
||||||
const getRainDetail = async (stcd) => {
|
|
||||||
const result = await httpget(apiurl.sq.qth.rainList.queryStPptnDetails, { stcd });
|
|
||||||
if (result.code === 200) {
|
|
||||||
setRainDetail(result.data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRainHistoryData = async (params) => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qth.rainList.tableList, params);
|
|
||||||
if (result.code === 200) {
|
|
||||||
setHistoryTableList(result.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRainHistoryChartData = async (params) => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qth.rainList.chartList, params);
|
|
||||||
if (result.code === 200) {
|
|
||||||
setHistoryChartList(result.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDayRainHistoryData = async (params) => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qth.rainList.dayTableList, params);
|
|
||||||
if (result.code === 200) {
|
|
||||||
setHistoryTableList(result.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getDayRainHistoryChartData = async (params) => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qth.rainList.dayChartList, params);
|
|
||||||
if (result.code === 200) {
|
|
||||||
setHistoryChartList(result.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getRainHistoryDetail = async (params) => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qth.rainList.nearbyHistory, params);
|
|
||||||
if (result.code === 200) {
|
|
||||||
sethistoryRainDetail(result.data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSearch = () => {
|
|
||||||
if (!stcd) return;
|
|
||||||
const params = {
|
|
||||||
startTime: dates
|
|
||||||
? viewMode === 'hour'
|
|
||||||
? dates[0]?.format('YYYY-MM-DD HH:mm:00')
|
|
||||||
: dates[0]?.format('YYYY-MM-DD 00:00:00')
|
|
||||||
: undefined,
|
|
||||||
endTime: dates
|
|
||||||
? viewMode === 'hour'
|
|
||||||
? dates[1]?.format('YYYY-MM-DD HH:mm:59')
|
|
||||||
: dates[1]?.format('YYYY-MM-DD 59:59:59')
|
|
||||||
: undefined,
|
|
||||||
stcd,
|
|
||||||
};
|
|
||||||
if (viewMode === 'hour') {
|
|
||||||
getRainHistoryData(params);
|
|
||||||
getRainHistoryChartData(params);
|
|
||||||
} else {
|
|
||||||
getDayRainHistoryData(params);
|
|
||||||
getDayRainHistoryChartData(params);
|
|
||||||
}
|
|
||||||
getRainHistoryDetail(params);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (stcd) {
|
|
||||||
getRainDetail(stcd);
|
|
||||||
handleSearch();
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [stcd]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (stcd) {
|
|
||||||
handleSearch();
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [viewMode]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="rain-monitor-container">
|
|
||||||
<div className="main-content">
|
|
||||||
<div className="right-panel">
|
|
||||||
<div className="panel-header" style={cleanMode ? { justifyContent: 'flex-start' } : {}}>
|
|
||||||
{!cleanMode && <div className="query-label"><span className="dot"></span>数据查询</div>}
|
|
||||||
<div className="query-controls" style={cleanMode ? { marginLeft: 0 } : {}}>
|
|
||||||
<RangePicker
|
|
||||||
showTime={viewMode === 'hour'}
|
|
||||||
value={dates}
|
|
||||||
onChange={setDates}
|
|
||||||
style={{ width: 340 }}
|
|
||||||
format={viewMode === 'hour' ? 'YYYY-MM-DD HH:mm' : 'YYYY-MM-DD'}
|
|
||||||
allowClear={false}
|
|
||||||
dropdownClassName="rain-monitor-date-dropdown"
|
|
||||||
/>
|
|
||||||
<Button type="primary" className="ant-btn-ghost-blue" icon={<SearchOutlined />} onClick={handleSearch}>查询</Button>
|
|
||||||
<div className="time-toggle">
|
|
||||||
<Button
|
|
||||||
type={viewMode === 'hour' ? 'primary' : 'default'}
|
|
||||||
className={viewMode === 'hour' ? 'ant-btn-ghost-blue' : 'btn-transparent'}
|
|
||||||
onClick={() => setViewMode('hour')}
|
|
||||||
>
|
|
||||||
小时
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type={viewMode === 'day' ? 'primary' : 'default'}
|
|
||||||
className={viewMode === 'day' ? 'ant-btn-ghost-blue' : 'btn-transparent'}
|
|
||||||
onClick={() => setViewMode('day')}
|
|
||||||
>
|
|
||||||
日
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="table-chart-layout">
|
|
||||||
<div className="data-table">
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={historyTableList}
|
|
||||||
size="small"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: 400 }}
|
|
||||||
rowKey="time"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="chart-view">
|
|
||||||
<div className="chart-container">
|
|
||||||
<ReactEcharts
|
|
||||||
option={option}
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
notMerge={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bottom-stats-grid">
|
|
||||||
{bottomStats.slice(0, 5).map((i, idx) => <div className="grid-header" key={`h1-${idx}`}>{i.label}</div>)}
|
|
||||||
{bottomStats.slice(0, 5).map((i, idx) => (
|
|
||||||
<div className="grid-value" key={`v1-${idx}`}>
|
|
||||||
<span className={i.suffix ? 'special-text' : ''}>{i.value}</span>
|
|
||||||
{i.total && <span>/{i.total}</span>}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{bottomStats.slice(5, 10).map((i, idx) => <div className="grid-header" key={`h2-${idx}`}>{i.label}</div>)}
|
|
||||||
{bottomStats.slice(5, 10).map((i, idx) => (
|
|
||||||
<div className="grid-value" key={`v2-${idx}`}>
|
|
||||||
{i.value}
|
|
||||||
{i.total && <span className="special-text">{i.total ? `(${moment(i.total).format('YYYY-MM-DD')})` : ''}</span>}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
import echarts from 'echarts/lib/echarts';
|
|
||||||
|
|
||||||
export default function DrpOption({ echartData, grid }) {
|
|
||||||
let totalDrp = 0;
|
|
||||||
const DRPLEVEL = [10, 20, 50, 100, 250];
|
|
||||||
const maxVal = DRPLEVEL.find(o => o > totalDrp);
|
|
||||||
const xMaxVal = echartData?.actual ? DRPLEVEL.find(o => {
|
|
||||||
let max = Math.max(...echartData?.actual || [])
|
|
||||||
return o > max
|
|
||||||
}):maxVal
|
|
||||||
const yMaxVal = echartData?.actual ? DRPLEVEL.find(o => {
|
|
||||||
let max = Math.max(...echartData?.total)
|
|
||||||
return o > max
|
|
||||||
}): maxVal
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
},
|
|
||||||
grid: grid || {
|
|
||||||
x: 40,
|
|
||||||
y: 30,
|
|
||||||
x2: 30,
|
|
||||||
y2: 28,
|
|
||||||
borderWidth: 0
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
// 显示图例
|
|
||||||
show: true,
|
|
||||||
// 图例的位置
|
|
||||||
data: ['实测', '累计'],
|
|
||||||
textStyle: { color: '#fff' },
|
|
||||||
},
|
|
||||||
calculable: true,
|
|
||||||
xAxis: [
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
data: echartData?.time,
|
|
||||||
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: [
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
position: 'left',
|
|
||||||
name: "雨量mm",
|
|
||||||
nameTextStyle: { color: '#fff' },
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: 'rgba(255,255,255,0.5)',
|
|
||||||
type: 'dashed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
min: 0,
|
|
||||||
max: xMaxVal
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
position: 'right',
|
|
||||||
nameTextStyle: { color: '#fff' },
|
|
||||||
name:"累计mm",
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: 'rgba(255,255,255,0.5)',
|
|
||||||
type: 'dashed'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
min: 0,
|
|
||||||
max: yMaxVal
|
|
||||||
}
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '实测',
|
|
||||||
type: 'bar',
|
|
||||||
barWidth: '60%',
|
|
||||||
data: echartData?.actual,
|
|
||||||
itemStyle: {
|
|
||||||
normal: {
|
|
||||||
barBorderRadius: [3, 3, 0, 0],
|
|
||||||
color: new echarts.graphic.LinearGradient(
|
|
||||||
0, 0, 0, 1,
|
|
||||||
[
|
|
||||||
{ offset: 0, color: '#3876cd' },
|
|
||||||
{ offset: 0.5, color: '#45b4e7' },
|
|
||||||
{ offset: 1, color: '#54ffff' }
|
|
||||||
]
|
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
markPoint: {
|
|
||||||
data: [
|
|
||||||
{ type: 'max', name: '最大值', symbol: 'circle', symbolSize: 1, symbolOffset: [0, -12] },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
yAxisIndex: 1,
|
|
||||||
name: '累计',
|
|
||||||
type: 'line',
|
|
||||||
showSymbol: false,
|
|
||||||
label: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
data: echartData?.total,
|
|
||||||
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'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import { Select } from 'antd';
|
|
||||||
import usePageTable from '@/components/crud/usePageTable';
|
|
||||||
import { createCrudService } from '@/components/crud/_';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import { httpget,httppost } from '@/utils/request';
|
|
||||||
import moment from 'moment';
|
|
||||||
import { rainlist } from '@/service/station';
|
|
||||||
import RightPanel from './RightPanel';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const RainMonitor = () => {
|
|
||||||
//实时雨量
|
|
||||||
const [selectList, setSelectList] = useState([])
|
|
||||||
const [selected, setSelected] = useState('')
|
|
||||||
const [rainDetail, setRainDetail] = useState({})
|
|
||||||
|
|
||||||
const stats = [
|
|
||||||
{ label: '近1小时', value: rainDetail?.h1 ?? '-', unit: 'mm' },
|
|
||||||
{ label: '近3小时', value: rainDetail?.h3 ?? '-', unit: 'mm' },
|
|
||||||
{ label: '近6小时', value: rainDetail?.h6 ?? '-', unit: 'mm' },
|
|
||||||
{ label: '近12小时', value: rainDetail?.h12 ?? '-', unit: 'mm' },
|
|
||||||
{ label: '近24小时', value: rainDetail?.h24 ?? '-', unit: 'mm' },
|
|
||||||
{ label: '近48小时', value: rainDetail?.h48 ?? '-', unit: 'mm' },
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 获取雨情站点
|
|
||||||
const getRainStationList = async() => {
|
|
||||||
try {
|
|
||||||
const data = await rainlist({})
|
|
||||||
setSelectList(data.map(item => ({ label: item.stnm, value: item.stcd,...item})))
|
|
||||||
setSelected(data[0].stcd)
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取实时详细雨量的 近几小时数据
|
|
||||||
const getRainDetail = async (stcd) => {
|
|
||||||
const result = await httpget(apiurl.sq.qth.rainList.queryStPptnDetails, { stcd });
|
|
||||||
if (result.code == 200) {
|
|
||||||
setRainDetail(result.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getRainStationList();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (selected) {
|
|
||||||
getRainDetail(selected);
|
|
||||||
}
|
|
||||||
}, [selected]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="rain-monitor-container">
|
|
||||||
<div className="main-content">
|
|
||||||
{/* Left Side: Stats Cards */}
|
|
||||||
<div className="left-panel">
|
|
||||||
<div className="panel-header">
|
|
||||||
<div className="query-label"><span className="dot"></span>实时雨情</div>
|
|
||||||
<div className="station-select">
|
|
||||||
<span>站点:</span>
|
|
||||||
<Select
|
|
||||||
value={selected}
|
|
||||||
onChange={setSelected}
|
|
||||||
style={{ width: 200 }}
|
|
||||||
options={selectList}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="update-time-banner">
|
|
||||||
雨情最新上报时间:{rainDetail.tm}
|
|
||||||
</div>
|
|
||||||
<div className="stats-grid">
|
|
||||||
{stats.map((item, idx) => (
|
|
||||||
<div key={idx} className="stat-card">
|
|
||||||
<div className="stat-value">
|
|
||||||
<span className="num">{item.value}</span>
|
|
||||||
<span className="unit"> {item.unit}</span>
|
|
||||||
</div>
|
|
||||||
<div className="stat-label">{item.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<RightPanel stcd={selected} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RainMonitor;
|
|
||||||
|
|
@ -1,324 +0,0 @@
|
||||||
.rain-monitor-container {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.left-panel {
|
|
||||||
// flex: 1; /* 1:2 ratio */
|
|
||||||
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 {
|
|
||||||
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;
|
|
||||||
width: 70%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-grid {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 42px 20px;
|
|
||||||
|
|
||||||
.stat-card {
|
|
||||||
width: calc(50% - 10px);
|
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
padding:25px 12px;
|
|
||||||
border-radius: 4px;
|
|
||||||
text-align: center;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: rgba(0, 160, 233, 0.5);
|
|
||||||
background: rgba(0, 160, 233, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
margin-bottom: 24px;
|
|
||||||
.num {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00e5ff;
|
|
||||||
}
|
|
||||||
.unit {
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stat-label {
|
|
||||||
font-size: 16px;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-panel {
|
|
||||||
flex: 2; /* 1:2 ratio */
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.query-controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 12px;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.time-toggle {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
margin-left: 16px;
|
|
||||||
|
|
||||||
.btn-transparent {
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid #00a0e9;
|
|
||||||
color: #fff;
|
|
||||||
box-shadow: none;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #00a0e9;
|
|
||||||
background: rgba(0, 160, 233, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.table-chart-layout {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.data-table {
|
|
||||||
width: 330px;
|
|
||||||
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-view {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 12px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.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: 20%;
|
|
||||||
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: 20%;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Time Picker Panel Styles - REMOVED (moved to .rain-monitor-date-dropdown) */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dropdown Styles - Must be outside .rain-monitor-container because it renders in body */
|
|
||||||
.rain-monitor-date-dropdown {
|
|
||||||
background-color: #082f4d; // Ensure main background is dark
|
|
||||||
|
|
||||||
.ant-picker-panel-container {
|
|
||||||
background-color: #082f4d;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 160, 233, 0.3);
|
|
||||||
border: 1px solid rgba(0, 160, 233, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-header {
|
|
||||||
color: #fff;
|
|
||||||
border-bottom: 1px solid rgba(0, 160, 233, 0.2);
|
|
||||||
|
|
||||||
button {
|
|
||||||
color: #fff;
|
|
||||||
&:hover {
|
|
||||||
color: #00a0e9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-content {
|
|
||||||
th {
|
|
||||||
color: #00a0e9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell {
|
|
||||||
color: rgba(255, 255, 255, 0.7);
|
|
||||||
|
|
||||||
&.ant-picker-cell-in-view {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover .ant-picker-cell-inner {
|
|
||||||
background: rgba(0, 160, 233, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.ant-picker-cell-selected .ant-picker-cell-inner {
|
|
||||||
background: #00a0e9;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Range Selection Fix - Remove "Red Box" Color (Blue Background)
|
|
||||||
&-in-range::before {
|
|
||||||
background: transparent !important; // Remove the blue background
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure range start/end still have background
|
|
||||||
&-range-start .ant-picker-cell-inner,
|
|
||||||
&-range-end .ant-picker-cell-inner {
|
|
||||||
background: #00a0e9 !important;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Time Picker Styles */
|
|
||||||
.ant-picker-time-panel {
|
|
||||||
border-left: 1px solid rgba(0, 160, 233, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-datetime-panel {
|
|
||||||
.ant-picker-time-panel {
|
|
||||||
border-left: 1px solid rgba(0, 160, 233, 0.2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-time-panel-column {
|
|
||||||
border-left: 1px solid rgba(0, 160, 233, 0.2);
|
|
||||||
|
|
||||||
&::after {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
> li.ant-picker-time-panel-cell {
|
|
||||||
.ant-picker-time-panel-cell-inner {
|
|
||||||
color: rgba(255, 255, 255, 0.8) !important; // Force white color
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 160, 233, 0.2);
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.ant-picker-time-panel-cell-selected {
|
|
||||||
.ant-picker-time-panel-cell-inner {
|
|
||||||
background: rgba(0, 160, 233, 0.4);
|
|
||||||
color: #00a0e9 !important;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-footer {
|
|
||||||
border-top: 1px solid rgba(0, 160, 233, 0.2);
|
|
||||||
|
|
||||||
.ant-picker-today-btn {
|
|
||||||
color: #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-ok {
|
|
||||||
.ant-btn {
|
|
||||||
background-color: #00a0e9;
|
|
||||||
border-color: #00a0e9;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #008cc9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
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) => <span>{rec ?? "-"}</span>
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '水位(m)', dataIndex: 'rz', key: 'rz', align: 'center',
|
|
||||||
render: (rec) => <span>{rec ? rec.toFixed(2) : "-"}</span>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '库容(万m³)', dataIndex: 'w', key: 'w', align: 'center',
|
|
||||||
render: (rec) => <span>{rec ??"-"}</span>,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// 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 (
|
|
||||||
<div className="right-panel">
|
|
||||||
<div className="panel-header" >
|
|
||||||
<div className="query-label"><span className="dot"></span>数据查询</div>
|
|
||||||
<div className="query-controls" style={cleanMode ? { marginLeft: 0 } : {}}>
|
|
||||||
<RangePicker
|
|
||||||
showTime
|
|
||||||
value={dates}
|
|
||||||
onChange={setDates}
|
|
||||||
style={{ width: 340 }}
|
|
||||||
format="YYYY-MM-DD HH:mm"
|
|
||||||
allowClear={false}
|
|
||||||
/>
|
|
||||||
<Button type="primary" className="ant-btn-ghost-blue" icon={<SearchOutlined />} onClick={handleSearch} loading={loading}>
|
|
||||||
查询
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="table-chart-layout">
|
|
||||||
<div className="data-table">
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={historyTableList}
|
|
||||||
size="small"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: 400 }}
|
|
||||||
rowKey="time"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="chart-view">
|
|
||||||
<div className="chart-container">
|
|
||||||
<ReactEcharts
|
|
||||||
option={option}
|
|
||||||
style={{ height: '100%', width: '100%' }}
|
|
||||||
notMerge={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bottom-stats-grid">
|
|
||||||
{bottomStats.slice(0, 7).map((i, idx) => <div className="grid-header" key={`h1-${idx}`}>{i.label}</div>)}
|
|
||||||
{bottomStats.slice(0, 7).map((i, idx) => (
|
|
||||||
<div className="grid-value" key={`v1-${idx}`}>
|
|
||||||
<span className={i.suffix ? 'special-text' : ''}>{i.value}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{bottomStats.slice(7, 14).map((i, idx) => <div className="grid-header" key={`h2-${idx}`}>{i.label}</div>)}
|
|
||||||
{bottomStats.slice(7, 14).map((i, idx) => (
|
|
||||||
<div className="grid-value" key={`v2-${idx}`}>
|
|
||||||
{i.value}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,257 +0,0 @@
|
||||||
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)
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
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 (
|
|
||||||
<div className="reservoir-panel-container">
|
|
||||||
<div className="main-content">
|
|
||||||
{/* Left Side: Stats List */}
|
|
||||||
<div className="left-panel">
|
|
||||||
<div className="panel-header">
|
|
||||||
<div className="query-label"><span className="dot"></span>实时水位</div>
|
|
||||||
{!cleanMode && (
|
|
||||||
<div className="station-select">
|
|
||||||
<span>站点:</span>
|
|
||||||
<Select
|
|
||||||
value={selected}
|
|
||||||
onChange={handleStationChange}
|
|
||||||
style={{ width: 200 }}
|
|
||||||
options={selectList}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="update-time-banner">
|
|
||||||
水位上报时间:{reservoirDetail?.tm ?? moment().format('YYYY-MM-DD HH:mm:ss')}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="data-list">
|
|
||||||
{dataItems.map((item, idx) => (
|
|
||||||
<div key={idx} className="data-item">
|
|
||||||
<div className="label">{item.label}:</div>
|
|
||||||
<div className="value-container">
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
{item.diff && (
|
|
||||||
<span className="diff" style={{ color: item.diff > 0 ? '#ff4d4f' : '#52c41a' }}>
|
|
||||||
{item.diff > 0 ? `| +${item.diff}` : `| -${Math.abs(item.diff)}`}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className="unit">{item.unit}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="visual-placeholder">
|
|
||||||
<div className="placeholder-content">
|
|
||||||
<MyImg record={reservoirDetail}/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<RightPanel stcd={selected} record={reservoirDetail} cleanMode={cleanMode} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReservoirPanel;
|
|
||||||
|
|
@ -1,262 +0,0 @@
|
||||||
.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,154 +0,0 @@
|
||||||
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 (
|
|
||||||
<div style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
overflow: 'hidden',
|
|
||||||
position: 'relative',
|
|
||||||
backgroundColor: "#eff3f6",
|
|
||||||
borderRadius: "2%"
|
|
||||||
}}>
|
|
||||||
<img
|
|
||||||
src={`${process.env.PUBLIC_URL}/assets/images/waterz.png`}
|
|
||||||
style={
|
|
||||||
{
|
|
||||||
position: 'absolute',
|
|
||||||
bottom: computerHeight(record.rz, record.crestElev, record.deadLev),
|
|
||||||
// top: '50%'
|
|
||||||
transform: "translateY(100%)"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/dam.png`} style={{ position: 'absolute', bottom: 0, height: '100%', right: -20 }} />
|
|
||||||
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/ruler.png`} style={{ position: 'absolute', bottom: 0, height: '100%', left: 20 }} />
|
|
||||||
|
|
||||||
<div style={
|
|
||||||
{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 40,
|
|
||||||
// width: '65%',
|
|
||||||
bottom: computerHeight(record.desFloodLev, record.crestElev, record.deadLev),
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 8,
|
|
||||||
// zIndex:1
|
|
||||||
}}>
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/red1.png`}
|
|
||||||
style={{
|
|
||||||
width: 90,
|
|
||||||
height: 15,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div style={{
|
|
||||||
color: '#ff0a0a',
|
|
||||||
fontSize: 16,
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
display: 'flex',
|
|
||||||
columnGap: 10
|
|
||||||
}}>
|
|
||||||
设计洪水位 <strong>{record.desFloodLev ? record.desFloodLev.toFixed(2) : "-"}m</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={
|
|
||||||
{
|
|
||||||
position: 'absolute',
|
|
||||||
left: 40,
|
|
||||||
// width: '65%',
|
|
||||||
bottom: computerHeight(record.flLowLimLev, record.crestElev, record.deadLev),
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 8
|
|
||||||
}}>
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/orange.png`}
|
|
||||||
style={{
|
|
||||||
width: 90,
|
|
||||||
height: 15
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div style={{
|
|
||||||
// width: "210px",
|
|
||||||
// border: '2px solid red',
|
|
||||||
// backgroundColor: '#0008',
|
|
||||||
// padding: '2px 16px',
|
|
||||||
color: '#ff7200',
|
|
||||||
fontSize: 16,
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
display: 'flex',
|
|
||||||
columnGap: 10
|
|
||||||
}}>
|
|
||||||
汛限水位 <strong>{record.flLowLimLev ? record.flLowLimLev.toFixed(2) : "-"}m</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={
|
|
||||||
{
|
|
||||||
position: 'absolute',
|
|
||||||
width: '84%',
|
|
||||||
bottom: computerHeight(record.rz, record.crestElev, record.deadLev),
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 8,
|
|
||||||
justifyContent: 'end',
|
|
||||||
}}>
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/blue2.png`}
|
|
||||||
style={{
|
|
||||||
flex: 0.5,
|
|
||||||
// width: 30,
|
|
||||||
height: 15
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div style={{color: '#06caff',fontSize: 16}}>
|
|
||||||
<div style={{ display: 'flex', columnGap: 10 }}>
|
|
||||||
<span>实时水位</span> <strong>{record.rz ? record.rz.toFixed(2) : "-"}m</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{
|
|
||||||
position: 'absolute', left: 40,
|
|
||||||
// width: '65%',
|
|
||||||
top: '90%', transform: 'translateY(-50%)', display: 'flex', alignItems: 'center', gap: 8
|
|
||||||
}}>
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/white1.png`}
|
|
||||||
style={{
|
|
||||||
width: 90,
|
|
||||||
height: 15
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div style={{
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 16,
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
display: 'flex',
|
|
||||||
columnGap: 10
|
|
||||||
}}>
|
|
||||||
死水位 <strong>{record.deadLev ? record.deadLev.toFixed(2) : ''}m</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default MyImg
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import { DatePicker, Button, Table } from 'antd';
|
|
||||||
import ReactEcharts from 'echarts-for-react';
|
|
||||||
import usePageTable from '@/components/crud/usePageTable';
|
|
||||||
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 SafetyPanel = () => {
|
|
||||||
return (
|
|
||||||
<div className="awm-empty">内容待接入</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AllWeatherModal = ({ active }) => {
|
|
||||||
if (active === 'rain') return <RainMonitor />;
|
|
||||||
if (active === 'reservoir') return <ReservoirPanel />;
|
|
||||||
if (active === 'flow') return <FlowPanel />;
|
|
||||||
return <SafetyPanel />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AllWeatherModal;
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
.all-weather-modal {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.awm-grid {
|
|
||||||
height: calc(100% - 48px);
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
.awm-left {
|
|
||||||
flex: 0 0 420px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.awm-toolbar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
.awm-right {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
.awm-empty {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
color: rgba(255,255,255,0.6);
|
|
||||||
}
|
|
||||||
|
|
@ -1,187 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { Timeline, Select, DatePicker, Button, Tag, Image, Tooltip } from 'antd';
|
|
||||||
import { SearchOutlined, ReloadOutlined, DownloadOutlined, FilePdfOutlined, FileOutlined } from '@ant-design/icons';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import usePageTable from '@/components/crud/usePageTable';
|
|
||||||
import { createCrudService } from '@/components/crud/_';
|
|
||||||
import { exportFile } from '@/utils/tools'
|
|
||||||
import { httppost } from '@/utils/request';
|
|
||||||
import { config } from '@/config';
|
|
||||||
import PdfView from '@/views/Home/components/UI/PdfView';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const { RangePicker } = DatePicker;
|
|
||||||
|
|
||||||
const CycleArchive = () => {
|
|
||||||
const { tableProps, search, refresh } = usePageTable(createCrudService(apiurl.sq.qzq.list).find);
|
|
||||||
const [keyword, setKeyword] = useState([]);
|
|
||||||
const [dates, setDates] = useState();
|
|
||||||
const [pdfInfo, setPdfInfo] = useState({ visible: false, title: '', fileId: '' });
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
search({ search: {} });
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleSearch = () => {
|
|
||||||
const params = {
|
|
||||||
search: {
|
|
||||||
types: keyword.length > 0 ? keyword : undefined,
|
|
||||||
dateSo: dates ? {
|
|
||||||
start: dates[0]?.format('YYYY-MM-DD'),
|
|
||||||
end: dates[1]?.format('YYYY-MM-DD')
|
|
||||||
} : undefined
|
|
||||||
}
|
|
||||||
};
|
|
||||||
search(params);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReset = () => {
|
|
||||||
setKeyword([]);
|
|
||||||
setDates(undefined);
|
|
||||||
search({ search: {} });
|
|
||||||
};
|
|
||||||
|
|
||||||
const onExport = () => {
|
|
||||||
let params = {
|
|
||||||
search: {
|
|
||||||
types: keyword.length > 0 ? keyword : undefined,
|
|
||||||
dateSo: dates ? {
|
|
||||||
start: dates[0]?.format('YYYY-MM-DD'),
|
|
||||||
end: dates[1]?.format('YYYY-MM-DD')
|
|
||||||
} : undefined
|
|
||||||
},
|
|
||||||
pageSo:{
|
|
||||||
pageNum:1,pageSize:9999
|
|
||||||
}
|
|
||||||
}
|
|
||||||
httppost(apiurl.sq.qzq.export, params,'blob').then(res => {
|
|
||||||
exportFile(`全周期档案.xlsx`,res.data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleFileClick = (file) => {
|
|
||||||
const fileType = file.fileName?.split('.').pop()?.toLowerCase();
|
|
||||||
if (fileType === 'pdf') {
|
|
||||||
setPdfInfo({
|
|
||||||
visible: true,
|
|
||||||
title: file.fileName,
|
|
||||||
fileId: file.fileId
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Download for non-pdf files
|
|
||||||
window.open(config.minioIp + file.filePath, '_blank');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderFileIcon = (file) => {
|
|
||||||
const fileType = file.fileName?.split('.').pop()?.toLowerCase();
|
|
||||||
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(fileType);
|
|
||||||
|
|
||||||
if (isImage) {
|
|
||||||
return (
|
|
||||||
<Image
|
|
||||||
width={60}
|
|
||||||
height={60}
|
|
||||||
src={config.minioIp + file.filePath}
|
|
||||||
style={{ objectFit: 'cover', borderRadius: '4px' }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (fileType === 'pdf') {
|
|
||||||
return <FilePdfOutlined style={{ fontSize: '40px', color: '#ff4d4f' }} />;
|
|
||||||
} else {
|
|
||||||
return <FileOutlined style={{ fontSize: '40px', color: '#1890ff' }} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="cycle-archive-container">
|
|
||||||
{/* Filters */}
|
|
||||||
<div className="filter-bar">
|
|
||||||
<div className="filter-item">
|
|
||||||
<span className="label">类型:</span>
|
|
||||||
<Select
|
|
||||||
options={[
|
|
||||||
{value:1,label:'大事记'}, {value:2,label:'调度指令'}, {value:3,label:'维修养护'},{value:4,label:'安全鉴定'}, {value:5,label:"除险加固"}
|
|
||||||
]}
|
|
||||||
allowClear
|
|
||||||
placeholder="请输入类型"
|
|
||||||
style={{ width: 200 }}
|
|
||||||
value={keyword}
|
|
||||||
onChange={(e) => setKeyword(e)}
|
|
||||||
mode='multiple'
|
|
||||||
maxTagCount='responsive'
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="filter-item">
|
|
||||||
<span className="label">发生日期:</span>
|
|
||||||
<RangePicker
|
|
||||||
style={{ width: 300 }}
|
|
||||||
value={dates}
|
|
||||||
onChange={(val) => setDates(val)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="action-buttons">
|
|
||||||
<Button type="primary" className="ant-btn-ghost-blue" icon={<SearchOutlined />} onClick={handleSearch}>查询</Button>
|
|
||||||
<Button icon={<ReloadOutlined />} onClick={handleReset}>重置</Button>
|
|
||||||
<Button type="primary" icon={<DownloadOutlined />} className="ant-btn-ghost-blue" onClick={onExport}>导出</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Timeline Content */}
|
|
||||||
<div className="timeline-content">
|
|
||||||
<Timeline>
|
|
||||||
{tableProps.dataSource?.map((item, index) => (
|
|
||||||
<Timeline.Item
|
|
||||||
key={index}
|
|
||||||
label={
|
|
||||||
<div className="timeline-label-content">
|
|
||||||
<div className="date">{item.eventsDate}</div>
|
|
||||||
<Tag color="cyan" className="custom-tag">{item.typeName}</Tag>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="timeline-item-content">
|
|
||||||
<div className="item-header">
|
|
||||||
<span className="title">{item.eventsDesc}</span>
|
|
||||||
</div>
|
|
||||||
<div className="item-body">
|
|
||||||
<div className="attachment-label">附件:</div>
|
|
||||||
<div className="image-grid">
|
|
||||||
{item.files?.length > 0 && item.files.map((file, idx) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className="image-item"
|
|
||||||
onClick={() => !['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(file.fileName?.split('.').pop()?.toLowerCase()) && handleFileClick(file)}
|
|
||||||
style={{ cursor: 'pointer' }}
|
|
||||||
>
|
|
||||||
<Tooltip title={file.fileName}>
|
|
||||||
<div className="file-preview">
|
|
||||||
{renderFileIcon(file)}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
<span className="image-name">{file.fileName}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Timeline.Item>
|
|
||||||
))}
|
|
||||||
</Timeline>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{pdfInfo.visible && (
|
|
||||||
<PdfView
|
|
||||||
visible={pdfInfo.visible}
|
|
||||||
onClose={() => setPdfInfo({ ...pdfInfo, visible: false })}
|
|
||||||
title={pdfInfo.title}
|
|
||||||
fileId={pdfInfo.fileId}
|
|
||||||
url={"/gunshiApp/ss/projectEvents/file/download/"}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CycleArchive;
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
||||||
.cycle-archive-container {
|
|
||||||
padding: 0px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.filter-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
.filter-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
.label {
|
|
||||||
color: #fff;
|
|
||||||
margin-right: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.action-buttons {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-content {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
overflow-x: hidden; // Prevent horizontal scrollbar
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.ant-timeline-item {
|
|
||||||
padding-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-label {
|
|
||||||
color: #00a0e9;
|
|
||||||
font-size: 16px;
|
|
||||||
width: 120px !important; // Fixed width for label
|
|
||||||
position: absolute !important;
|
|
||||||
left: 0 !important;
|
|
||||||
text-align: right;
|
|
||||||
padding-right: 15px;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-label-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-end;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
.date {
|
|
||||||
color: #fff;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-tag {
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
color: #fff;
|
|
||||||
background: #00a0e9; // Match theme
|
|
||||||
font-weight: normal;
|
|
||||||
text-align: center;
|
|
||||||
width: fit-content;
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 0 5px;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-tail {
|
|
||||||
border-left: 2px solid rgba(0, 160, 233, 0.3);
|
|
||||||
left: 126px !important; // 120px (label) + ~6px (center of gap)
|
|
||||||
height: calc(100% - 10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-head {
|
|
||||||
background-color: transparent;
|
|
||||||
border-color: #00a0e9;
|
|
||||||
left: 126px !important; // Match tail
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-timeline-item-content {
|
|
||||||
margin-left: 140px !important; // 126px + padding
|
|
||||||
width: calc(100% - 140px) !important; // Take remaining width
|
|
||||||
top: 0;
|
|
||||||
padding-top: 0; // Align with label
|
|
||||||
min-height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.timeline-item-content {
|
|
||||||
background: transparent;
|
|
||||||
// border: 1px solid rgba(255, 255, 255, 0.1); // Removed border as per "clean" look
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.item-header {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 16px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.item-body {
|
|
||||||
.attachment-label {
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-grid {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 15px;
|
|
||||||
|
|
||||||
.image-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 4px;
|
|
||||||
gap: 10px;
|
|
||||||
width: 280px;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.1);
|
|
||||||
border-color: #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-preview {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-name {
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
import React, { useMemo } from 'react';
|
|
||||||
import ReactEcharts from 'echarts-for-react';
|
|
||||||
import { Table } from 'antd';
|
|
||||||
import { GetInterval } from '@/utils/tools';
|
|
||||||
const CapacityCurve = ({data=[]}) => {
|
|
||||||
const dataSource = (data && data.length > 0) ? data : [];
|
|
||||||
|
|
||||||
// Robust data parsing
|
|
||||||
const validData = dataSource.map(item => ({
|
|
||||||
...item,
|
|
||||||
rz: parseFloat(item.rz),
|
|
||||||
w: parseFloat(item.w)
|
|
||||||
})).filter(item => !isNaN(item.rz) && !isNaN(item.w));
|
|
||||||
|
|
||||||
const hasData = validData.length > 0;
|
|
||||||
|
|
||||||
let maxVal = hasData ? Math.ceil(Math.max(...validData.map(obj => obj.rz))) : 100;
|
|
||||||
let minVal = hasData ? Math.floor(Math.min(...validData.map(obj => obj.rz))) : 0;
|
|
||||||
let maxValX = hasData ? Math.max(...validData.map(obj => obj.w)) : 100;
|
|
||||||
let minValX = hasData ? Math.min(...validData.map(obj => obj.w)) : 0;
|
|
||||||
|
|
||||||
// Prevent min === max for axes
|
|
||||||
if (minVal === maxVal) {
|
|
||||||
maxVal += 1;
|
|
||||||
minVal -= 1;
|
|
||||||
}
|
|
||||||
if (minValX === maxValX) {
|
|
||||||
maxValX += 10;
|
|
||||||
minValX -= 10;
|
|
||||||
if (minValX < 0) minValX = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate safe interval
|
|
||||||
let intervalX = GetInterval(minValX, maxValX);
|
|
||||||
if (intervalX <= 0 || isNaN(intervalX)) intervalX = 20;
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '序号',
|
|
||||||
dataIndex: 'index',
|
|
||||||
key: 'index',
|
|
||||||
render: (text, record, index) => index + 1,
|
|
||||||
width: 80,
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '水位(m)',
|
|
||||||
dataIndex: 'rz',
|
|
||||||
key: 'rz',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '库容(万m³)',
|
|
||||||
dataIndex: 'w',
|
|
||||||
key: 'w',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const option = useMemo(() => {
|
|
||||||
return {
|
|
||||||
toolbox: {
|
|
||||||
show: true,
|
|
||||||
feature: {
|
|
||||||
saveAsImage: {
|
|
||||||
show: true,
|
|
||||||
excludeComponents: ['toolbox'],
|
|
||||||
pixelRatio: 2,
|
|
||||||
name:"库容曲线图"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
right: "14%",
|
|
||||||
top:"5%"
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: "库容曲线图",
|
|
||||||
left: "40%",
|
|
||||||
textStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
},
|
|
||||||
grid: [
|
|
||||||
{
|
|
||||||
top: "10%",
|
|
||||||
left: "15%",
|
|
||||||
right: "15%",
|
|
||||||
bottom: "8%"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
xAxis: [
|
|
||||||
{
|
|
||||||
name: "库容(万m³)",
|
|
||||||
nameTextStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
nameGap: 5,
|
|
||||||
type: 'value',
|
|
||||||
min:Math.floor(minValX / 5) *5,
|
|
||||||
max:Math.ceil(maxValX / 5) *5,
|
|
||||||
interval: intervalX,
|
|
||||||
splitLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
name: "库水位(m)",
|
|
||||||
nameTextStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
minInterval:1,
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
width: 0.25,
|
|
||||||
type: 'dotted'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
min: minVal,
|
|
||||||
max: maxVal
|
|
||||||
}
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'line',
|
|
||||||
color: "#007AFD",
|
|
||||||
data: validData.map(item=>[item.w,item.rz]),
|
|
||||||
smooth: true
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}, [minVal, maxVal, minValX, maxValX, intervalX, validData]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ display: 'flex', height: '100%', gap: '20px' }}>
|
|
||||||
<div style={{ flex: '0 0 400px', height: '100%', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<Table
|
|
||||||
dataSource={dataSource}
|
|
||||||
columns={columns}
|
|
||||||
rowKey="id"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: 'calc(100vh - 300px)' }} // Estimate height, will adjust if needed
|
|
||||||
size="small"
|
|
||||||
bordered={false}
|
|
||||||
className="custom-table"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, height: '100%', minWidth: 0, paddingRight: 20 }}>
|
|
||||||
<ReactEcharts option={option} style={{ height: '100%', width: '100%' }} notMerge={true} lazyUpdate={true} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CapacityCurve;
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import React,{useState,useEffect} from 'react';
|
import React from 'react';
|
||||||
import { Descriptions } from 'antd';
|
import { Descriptions } from 'antd';
|
||||||
const MainBuildingInfo = ({data={}}) => {
|
|
||||||
const [info, setInfo] = useState({})
|
|
||||||
|
|
||||||
|
const MainBuildingInfo = ({ data = {} }) => {
|
||||||
const commonLabelStyle = {
|
const commonLabelStyle = {
|
||||||
width: '200px',
|
width: '200px',
|
||||||
textAlign: 'right',
|
textAlign: 'right',
|
||||||
|
|
@ -22,68 +21,63 @@ const MainBuildingInfo = ({data={}}) => {
|
||||||
style: descriptionStyle,
|
style: descriptionStyle,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setInfo(data)
|
|
||||||
}, [data])
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="form-container">
|
<div className="form-container">
|
||||||
{/* 主坝 */}
|
{/* 主坝 */}
|
||||||
<Descriptions {...getDescriptionsProps('主坝')}>
|
<Descriptions {...getDescriptionsProps('主坝')}>
|
||||||
<Descriptions.Item label="坝型">{info.mainType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝型">{data.mainDamType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="坝顶高程(m)">{info.mainCrestElevation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝顶高程(m)">{data.mainDamCrestElevation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="坝顶长度(m)">{info.mainCrestLength || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝顶长度(m)">{data.mainDamLength || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="坝顶宽度(m)">{info.mainCrestWidth || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝顶宽度(m)">{data.mainDamWidth || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="最大坝高(m)">{info.mainMaxHeight || '-'}</Descriptions.Item>
|
<Descriptions.Item label="最大坝高(m)">{data.mainDamMaxHeight || '-'}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
|
||||||
{/* 副坝 */}
|
{/* 副坝 */}
|
||||||
<Descriptions {...getDescriptionsProps('副坝')}>
|
<Descriptions {...getDescriptionsProps('副坝')}>
|
||||||
<Descriptions.Item label="坝型">{info.auxType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝型">{data.auxDamType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="坝顶高程(m)">{info.auxCrestElevation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝顶高程(m)">{data.auxDamCrestElevation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="坝顶长度(m)">{info.auxCrestLength || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝顶长度(m)">{data.auxDamLength || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="坝顶宽度(m)">{info.auxCrestWidth || '-'}</Descriptions.Item>
|
<Descriptions.Item label="坝顶宽度(m)">{data.auxDamWidth || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="最大坝高(m)">{info.auxMaxHeight || '-'}</Descriptions.Item>
|
<Descriptions.Item label="最大坝高(m)">{data.auxDamMaxHeight || '-'}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
|
||||||
{/* 溢洪道 */}
|
{/* 溢洪道 */}
|
||||||
<Descriptions {...getDescriptionsProps('溢洪道')}>
|
<Descriptions {...getDescriptionsProps('溢洪道')}>
|
||||||
<Descriptions.Item label="型式">{info.spillwayType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="型式">{data.spillwayType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="堰顶型式">{info.spillwayCrestType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="堰顶型式">{data.spillwayCrestType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="地基特性">{info.spillwayFoundation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="地基特性">{data.spillwayFoundation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="溢流堰顶高程(m)">{info.spillwayCrestElevation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="溢流堰顶高程(m)">{data.spillwayCrestElevation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="溢流堰净宽(m)">{info.spillwayNetWidth || '-'}</Descriptions.Item>
|
<Descriptions.Item label="溢流堰净宽(m)">{data.spillwayNetWidth || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="消能型式">{info.spillwayEnergyDissipation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="消能型式">{data.spillwayDissipationType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="校核洪水下泄流量(m³/s)">{info.spillwayCheckFloodDischarge || '-'}</Descriptions.Item>
|
<Descriptions.Item label="校核洪水下泄流量(m³/s)">{data.spillwayCheckFlow || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="设计洪水下泄流量(m³/s)">{info.spillwayDesignFloodDischarge || '-'}</Descriptions.Item>
|
<Descriptions.Item label="设计洪水下泄流量(m³/s)">{data.spillwayDesignFlow || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="消能防冲下泄流量(m³/s)">{info.spillwayScouringDischarge || '-'}</Descriptions.Item>
|
<Descriptions.Item label="消能防冲下泄流量(m³/s)">{data.spillwayDissipationFlow || '-'}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
|
||||||
{/* 灌溉发电洞 */}
|
{/* 灌溉发电洞 */}
|
||||||
<Descriptions {...getDescriptionsProps('灌溉发电洞')}>
|
<Descriptions {...getDescriptionsProps('灌溉发电洞')}>
|
||||||
<Descriptions.Item label="型式">{info.irrigationType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="型式">{data.irrigationTunnelType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="衬砌型式">{info.irrigationLiningType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="衬砌型式">{data.irrigationTunnelLining || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="地基特性">{info.irrigationFoundation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="地基特性">{data.irrigationTunnelFoundation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="进口底板高程(m)">{info.irrigationInletElevation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="进口底板高程(m)">{data.irrigationTunnelInletElevation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="断面尺寸(m)">{info.irrigationCrossSection || '-'}</Descriptions.Item>
|
<Descriptions.Item label="断面尺寸(m)">{data.irrigationTunnelSectionSize || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="洞长(m)">{info.irrigationLength || '-'}</Descriptions.Item>
|
<Descriptions.Item label="洞长(m)">{data.irrigationTunnelLength || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="设计流量(m³/s)">{info.irrigationDesignFlow || '-'}</Descriptions.Item>
|
<Descriptions.Item label="设计流量(m³/s)">{data.irrigationTunnelDesignFlow || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="进口闸门型式">{info.irrigationGateType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="进口闸门型式">{data.irrigationTunnelGateType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="进口启闭机型式">{info.irrigationHoistType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="进口启闭机型式">{data.irrigationTunnelHoistType || '-'}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
|
|
||||||
{/* 放空洞 */}
|
{/* 放空洞 */}
|
||||||
<Descriptions {...getDescriptionsProps('放空洞')}>
|
<Descriptions {...getDescriptionsProps('放空洞')}>
|
||||||
<Descriptions.Item label="型式">{info.emptyingType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="型式">{data.outletTunnelType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="衬砌型式">{info.emptyingLiningType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="衬砌型式">{data.outletTunnelLining || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="地基特性">{info.emptyingFoundation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="地基特性">{data.outletTunnelFoundation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="进口底板高程(m)">{info.emptyingInletElevation || '-'}</Descriptions.Item>
|
<Descriptions.Item label="进口底板高程(m)">{data.outletTunnelInletElevation || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="断面尺寸(m)">{info.emptyingCrossSection || '-'}</Descriptions.Item>
|
<Descriptions.Item label="断面尺寸(m)">{data.outletTunnelSectionSize || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="洞长(m)">{info.emptyingLength || '-'}</Descriptions.Item>
|
<Descriptions.Item label="洞长(m)">{data.outletTunnelLength || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="设计流量(m³/s)">{info.emptyingDesignFlow || '-'}</Descriptions.Item>
|
<Descriptions.Item label="设计流量(m³/s)">{data.outletTunnelDesignFlow || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="进口闸门型式">{info.emptyingGateType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="进口闸门型式">{data.outletTunnelGateType || '-'}</Descriptions.Item>
|
||||||
<Descriptions.Item label="进口启闭机型式">{info.emptyingHoistType || '-'}</Descriptions.Item>
|
<Descriptions.Item label="进口启闭机型式">{data.outletTunnelHoistType || '-'}</Descriptions.Item>
|
||||||
</Descriptions>
|
</Descriptions>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
||||||
import React, { useMemo,useEffect,useState } from 'react';
|
|
||||||
import ReactEcharts from 'echarts-for-react';
|
|
||||||
import { Table,Empty } from 'antd';
|
|
||||||
import { GetInterval } from '@/utils/tools';
|
|
||||||
const XlCurve = ({data=[]}) => {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: '序号',
|
|
||||||
dataIndex: 'index',
|
|
||||||
key: 'index',
|
|
||||||
render: (text, record, index) => index + 1,
|
|
||||||
width: 80,
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '水位(m)',
|
|
||||||
dataIndex: 'z',
|
|
||||||
key: 'z',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '流量(m³/s)',
|
|
||||||
dataIndex: 'q',
|
|
||||||
key: 'q',
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const option = useMemo(() => {
|
|
||||||
if (data.length > 0) {
|
|
||||||
const maxVal = Math.ceil(Math.max(...data.map(obj => obj.q)))
|
|
||||||
const minVal = Math.floor(Math.min(...data.map(obj => obj.q)))
|
|
||||||
const maxValX = Math.max(...data.map(obj => obj.z))
|
|
||||||
const minValX = Math.min(...data.map(obj => obj.z))
|
|
||||||
return {
|
|
||||||
toolbox: {
|
|
||||||
show: true,
|
|
||||||
feature: {
|
|
||||||
saveAsImage: {
|
|
||||||
show: true,
|
|
||||||
excludeComponents: ['toolbox'],
|
|
||||||
pixelRatio: 2,
|
|
||||||
name:"泄流曲线图"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
right: "14%",
|
|
||||||
top:"5%"
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: "泄流曲线图",
|
|
||||||
left: "40%",
|
|
||||||
textStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
},
|
|
||||||
grid: [
|
|
||||||
{
|
|
||||||
top: "10%",
|
|
||||||
left: "15%",
|
|
||||||
right: "15%",
|
|
||||||
bottom: "8%"
|
|
||||||
},
|
|
||||||
],
|
|
||||||
xAxis: [
|
|
||||||
{
|
|
||||||
name: "库水位(m)",
|
|
||||||
nameTextStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
nameGap: 5,
|
|
||||||
type: 'value',
|
|
||||||
min:Math.floor(minValX / 5) *5,
|
|
||||||
max:Math.ceil(maxValX / 5) *5,
|
|
||||||
interval: GetInterval(minValX,maxValX),
|
|
||||||
splitLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
],
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
name: "流量(m³/s)",
|
|
||||||
nameTextStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
},
|
|
||||||
minInterval:1,
|
|
||||||
splitLine: {
|
|
||||||
show: true,
|
|
||||||
lineStyle: {
|
|
||||||
color: '#fff',
|
|
||||||
width: 0.25,
|
|
||||||
type: 'dotted'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
color: '#fff',
|
|
||||||
fontSize: 12,
|
|
||||||
},
|
|
||||||
axisLine: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
min: minVal,
|
|
||||||
max: maxVal
|
|
||||||
}
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
type: 'line',
|
|
||||||
color: "#007AFD",
|
|
||||||
data: data.map(item=>[item.z,item.q]),
|
|
||||||
smooth: true
|
|
||||||
},
|
|
||||||
]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [data]);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ display: 'flex', height: '100%', gap: '20px' }}>
|
|
||||||
<div style={{ flex: '0 0 400px', height: '100%', overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
|
|
||||||
<Table
|
|
||||||
dataSource={data}
|
|
||||||
columns={columns}
|
|
||||||
rowKey="id"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: 'calc(100vh - 300px)' }} // Estimate height, will adjust if needed
|
|
||||||
size="small"
|
|
||||||
bordered={false}
|
|
||||||
className="custom-table"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, height: '100%', minWidth: 0, paddingRight: 20 }}>
|
|
||||||
{data.length > 0 ? <ReactEcharts option={option} style={{ height: '100%', width: '100%' }} notMerge={true} /> :
|
|
||||||
<Empty description={"暂无数据"} />
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default XlCurve;
|
|
||||||
|
|
@ -1,22 +1,16 @@
|
||||||
import React, { useState,useEffect } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { PaperClipOutlined } from '@ant-design/icons';
|
import { PaperClipOutlined } from '@ant-design/icons';
|
||||||
import PdfView from '@/views/Home/components/UI/PdfView'
|
import PdfView from '@/views/Home/components/UI/PdfView'
|
||||||
import BasicInfo from './components/BasicInfo';
|
import BasicInfo from './components/BasicInfo';
|
||||||
import MainFeatureParams from './components/MainFeatureParams';
|
import MainFeatureParams from './components/MainFeatureParams';
|
||||||
import MainBuildingInfo from './components/MainBuildingInfo';
|
import MainBuildingInfo from './components/MainBuildingInfo';
|
||||||
import CapacityCurve from './components/CapacityCurve';
|
|
||||||
import XlCurve from './components/XlCurve';
|
|
||||||
import { httpget,httppost } from '@/utils/request';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const EngineeringElements = ({ data }) => {
|
const EngineeringElements = ({ data }) => {
|
||||||
const [activeButton, setActiveButton] = useState('basic');
|
const [activeButton, setActiveButton] = useState('basic');
|
||||||
const [pdfOpen, setPdfOpen] = useState(false)
|
const [pdfOpen, setPdfOpen] = useState(false)
|
||||||
const [filesItem, setFilesItem] = useState({})
|
const [filesItem, setFilesItem] = useState({})
|
||||||
const [info, setInfo] = useState({}) //主要建筑物信息
|
|
||||||
const [krLineList, setKrLineList] = useState([]) //库容曲线
|
|
||||||
const [xrLineList, setXrLineList] = useState([]) //泄流曲线
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{ label: '工程基础信息', value: 'basic' },
|
{ label: '工程基础信息', value: 'basic' },
|
||||||
{ label: '主要特征参数', value: 'params' },
|
{ label: '主要特征参数', value: 'params' },
|
||||||
|
|
@ -25,41 +19,6 @@ const EngineeringElements = ({ data }) => {
|
||||||
{ label: '水库泄流曲线', value: 'discharge-curve' },
|
{ label: '水库泄流曲线', value: 'discharge-curve' },
|
||||||
];
|
];
|
||||||
|
|
||||||
// 建筑物信息
|
|
||||||
const getBuildInfo = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sq.qys.gcys.buildInfo);
|
|
||||||
if (result.code == 200) {
|
|
||||||
setInfo(result.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 库容曲线
|
|
||||||
const getKrList = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qys.gcys.krlineList);
|
|
||||||
if (result.code == 200) {
|
|
||||||
setKrLineList(result.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 泄流曲线
|
|
||||||
const getXlList = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qys.gcys.xllineList);
|
|
||||||
if (result.code == 200) {
|
|
||||||
setXrLineList(result.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const handlePreview = (data) => {
|
const handlePreview = (data) => {
|
||||||
setPdfOpen(true)
|
setPdfOpen(true)
|
||||||
setFilesItem(data)
|
setFilesItem(data)
|
||||||
|
|
@ -72,11 +31,7 @@ const EngineeringElements = ({ data }) => {
|
||||||
case 'params':
|
case 'params':
|
||||||
return <MainFeatureParams data={data} />;
|
return <MainFeatureParams data={data} />;
|
||||||
case 'buildings':
|
case 'buildings':
|
||||||
return <MainBuildingInfo data={info} />;
|
return <MainBuildingInfo data={data} />;
|
||||||
case 'capacity-curve':
|
|
||||||
return <CapacityCurve data={krLineList} />;
|
|
||||||
case 'discharge-curve':
|
|
||||||
return <XlCurve data={xrLineList} />;
|
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<div className="placeholder-content">
|
<div className="placeholder-content">
|
||||||
|
|
@ -86,11 +41,7 @@ const EngineeringElements = ({ data }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
|
||||||
getBuildInfo()
|
|
||||||
getKrList()
|
|
||||||
getXlList()
|
|
||||||
}, [])
|
|
||||||
return (
|
return (
|
||||||
<div className="engineering-elements">
|
<div className="engineering-elements">
|
||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
|
|
|
||||||
|
|
@ -49,21 +49,14 @@
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar-thumb {
|
&::-webkit-scrollbar-thumb {
|
||||||
background: rgba(0, 160, 233, 0.5);
|
background: rgba(255, 255, 255, 0.2);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: rgba(0, 160, 233, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.engineering-descriptions {
|
.engineering-descriptions {
|
||||||
table {
|
|
||||||
table-layout: fixed !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-descriptions-view {
|
.ant-descriptions-view {
|
||||||
border-color: rgba(59, 124, 255, 0.3);
|
border-color: rgba(59, 124, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
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: () => <span className="action-link">起飞</span>
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '航线',
|
|
||||||
key: 'route',
|
|
||||||
align: 'center',
|
|
||||||
width: 120,
|
|
||||||
render: () => <span className="action-link">显示</span>
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{ id: 1, name: '视频任务' },
|
|
||||||
{ id: 2, name: '拍摄' },
|
|
||||||
{ id: 3, name: '坝体巡检' },
|
|
||||||
{ id: 4, name: '溢洪道下泄巡查' },
|
|
||||||
{ id: 5, name: '界桩巡检01' },
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="tab-content-wrapper">
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={data}
|
|
||||||
pagination={false}
|
|
||||||
size="middle"
|
|
||||||
rowKey="id"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
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: () => <span className="action-link">回放</span>
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<div className="tab-content-wrapper">
|
|
||||||
<div className="search-bar" style={{ marginBottom: 16, display: 'flex', alignItems: 'center' }}>
|
|
||||||
<span style={{ marginRight: 8, color: '#fff' }}>任务时间:</span>
|
|
||||||
<RangePicker
|
|
||||||
value={dates}
|
|
||||||
onChange={setDates}
|
|
||||||
style={{ width: 300, marginRight: 16 }}
|
|
||||||
/>
|
|
||||||
<Button type="primary" className="ant-btn-ghost-blue">查询</Button>
|
|
||||||
</div>
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
dataSource={data}
|
|
||||||
pagination={false}
|
|
||||||
size="middle"
|
|
||||||
rowKey="id"
|
|
||||||
className="custom-dark-table"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
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 (
|
|
||||||
<div style={{ height: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', color: 'rgba(255,255,255,0.5)' }}>
|
|
||||||
暂无直播画面
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
case '2':
|
|
||||||
return <FlightTasks />;
|
|
||||||
case '3':
|
|
||||||
return <History />;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="uav-modal-container">
|
|
||||||
{renderContent()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
.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%;
|
|
||||||
}
|
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
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=<VideoCameraFilled />
|
|
||||||
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 (
|
|
||||||
<div className='flex videoList'>
|
|
||||||
<div className={['treeRight', (selectList && selectList.type == 1) ? 'ptz-visible' : ''].join(' ')}>
|
|
||||||
<TreeData size={size} selectedKeys={selectedKeys} treeListData={treeListData} videoArr={videoArr} />
|
|
||||||
{
|
|
||||||
selectList && selectList.type == 1 ?
|
|
||||||
<div style={{ position: "absolute", bottom: 0, left: 0 }}>
|
|
||||||
<VideoControler
|
|
||||||
selectItem={selectList}
|
|
||||||
onOperation={onOperation}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
: null
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div className='treeLeft'><SplitScreen count={count} videoArr={videoArr} clickIndex={clickIndex} getType={getType} /></div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default VideoList
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
.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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
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<any>([]);
|
|
||||||
const [videoList, setVideoList] = useState<any>([]);
|
|
||||||
const [num, setNum] = useState<any>(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 (
|
|
||||||
<div className='splitScreen'>
|
|
||||||
{/* <Tabs
|
|
||||||
onChange={onChange}
|
|
||||||
defaultActiveKey='4'
|
|
||||||
type="card"
|
|
||||||
items={items}
|
|
||||||
/> */}
|
|
||||||
{/* {props.videoArr} */}
|
|
||||||
<div className={['flex', 'flexwarp', 'borderF'].join(' ')} style={{ position: 'relative', height: '100%' }}>
|
|
||||||
{list.map((item: any, index: any) => {
|
|
||||||
return <div onClick={() => 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]&&<div style={{height:'100%',width:'100%'}}>
|
|
||||||
<div style={{ height: 'calc(100% - 20px)',width:'100%' }}>
|
|
||||||
{videoList.length&&<HFivePlayer size={size} wsUrl={videoList[index]} playerID={index} />}
|
|
||||||
</div>
|
|
||||||
<div style={{ textAlign: 'center' }}>{videoList[index]?.name}</div>
|
|
||||||
</div>}
|
|
||||||
{!props.videoArr[index]&&<div className='borderType'>
|
|
||||||
<img src={`${process.env.PUBLIC_URL}/assets/images/no-video.png`} alt=""/>
|
|
||||||
<div className='text'>暂无视频数据</div>
|
|
||||||
</div>}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
export default SplitScreen
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
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 <img src={`${process.env.PUBLIC_URL}/assets/images/qiujiG.png`} alt='' className='iconSelect'/>
|
|
||||||
|
|
||||||
} else if(type ==2) {
|
|
||||||
return <img
|
|
||||||
src={`${process.env.PUBLIC_URL}/assets/images/jk.png`}
|
|
||||||
alt=''
|
|
||||||
className='iconSelect'
|
|
||||||
style={{width:17,height:18}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (a.data.isLeaf) {
|
|
||||||
// if(a.data.type ==1)
|
|
||||||
// return <VideoCameraFilled className='iconSelect'/>
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
useEffect(()=>{
|
|
||||||
setCheckNode(() => {
|
|
||||||
const newA = props.videoArr.map((item:any)=>{
|
|
||||||
return item?.id
|
|
||||||
})
|
|
||||||
return newA
|
|
||||||
})
|
|
||||||
// setCheckNode(selectedKeys)
|
|
||||||
},[props.videoArr])
|
|
||||||
// useEffect(()=>{})
|
|
||||||
return <div style={{height:'100%',overflowY:'scroll'}}>
|
|
||||||
{props.treeListData.length !== 0 &&
|
|
||||||
<Tree
|
|
||||||
treeData={props.treeListData}
|
|
||||||
fieldNames={{ title: 'name', key: 'id' }}
|
|
||||||
defaultExpandAll={true}
|
|
||||||
multiple
|
|
||||||
showIcon
|
|
||||||
key='id'
|
|
||||||
// height={600}
|
|
||||||
onSelect={onSelect}
|
|
||||||
icon={iconSelect}
|
|
||||||
selectedKeys={checkNode}
|
|
||||||
/> }
|
|
||||||
</div>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TreeData;
|
|
||||||
|
|
@ -69,7 +69,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
justify-content: space-between;
|
|
||||||
.label {
|
.label {
|
||||||
color: #CCF3FF;
|
color: #CCF3FF;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,8 @@ import MonitoringElements from './components/MonitoringElements';
|
||||||
import ReservoirAreaElements from './components/ModalComponents/ReservoirAreaElements';
|
import ReservoirAreaElements from './components/ModalComponents/ReservoirAreaElements';
|
||||||
import EngineeringElements from './components/ModalComponents/EngineeringElements';
|
import EngineeringElements from './components/ModalComponents/EngineeringElements';
|
||||||
import DownstreamElements from './components/ModalComponents/DownstreamElements';
|
import DownstreamElements from './components/ModalComponents/DownstreamElements';
|
||||||
import AllWeatherControl from './components/AllWeatherControl';
|
// import ManagementElements from './components/ModalComponents/ManagementElements';
|
||||||
import ManagementCycle from './components/ManagementCycle';
|
// import AllWeatherMastery from './components/AllWeatherMastery';
|
||||||
import CycleArchive from './components/ModalComponents/CycleArchive';
|
|
||||||
import AllWeatherModal from './components/ModalComponents/AllWeatherModal';
|
|
||||||
import CommonModal from '../../UI/CommonModal';
|
import CommonModal from '../../UI/CommonModal';
|
||||||
import { httppost } from '@/utils/request';
|
import { httppost } from '@/utils/request';
|
||||||
import apiurl from '@/service/apiurl';
|
import apiurl from '@/service/apiurl';
|
||||||
|
|
@ -18,7 +16,6 @@ import './index.less';
|
||||||
|
|
||||||
const SiQuan = () => {
|
const SiQuan = () => {
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
const [modalVisible, setModalVisible] = useState(false);
|
||||||
const [modalType, setModalType] = useState('monitor'); // 'monitor' | 'cycle' | 'allweather'
|
|
||||||
const [infos, setInfos] = useState({});
|
const [infos, setInfos] = useState({});
|
||||||
const [activeTab, setActiveTab] = useState('kqys'); // Default active tab
|
const [activeTab, setActiveTab] = useState('kqys'); // Default active tab
|
||||||
|
|
||||||
|
|
@ -26,12 +23,7 @@ const SiQuan = () => {
|
||||||
{ label: '库区要素', value: 'kqys' },
|
{ label: '库区要素', value: 'kqys' },
|
||||||
{ label: '工程要素', value: 'gcys' },
|
{ label: '工程要素', value: 'gcys' },
|
||||||
{ label: '下游要素', value: 'xyys' },
|
{ label: '下游要素', value: 'xyys' },
|
||||||
];
|
// { label: '管理要素', value: 'glys' },
|
||||||
const tabsAllWeather = [
|
|
||||||
{ label: '雨情监测', value: 'rain' },
|
|
||||||
{ label: '水库水情', value: 'reservoir' },
|
|
||||||
{ label: '出入库流量', value: 'flow' },
|
|
||||||
{ label: '安全监测', value: 'safety' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const getInfo = async () => {
|
const getInfo = async () => {
|
||||||
|
|
@ -51,17 +43,6 @@ const SiQuan = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleOpenModal = () => {
|
const handleOpenModal = () => {
|
||||||
setModalType('monitor');
|
|
||||||
setModalVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenCycleModal = () => {
|
|
||||||
setModalType('cycle');
|
|
||||||
setModalVisible(true);
|
|
||||||
};
|
|
||||||
const handleOpenAllWeatherModal = () => {
|
|
||||||
setModalType('allweather');
|
|
||||||
setActiveTab('rain');
|
|
||||||
setModalVisible(true);
|
setModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -83,7 +64,7 @@ const SiQuan = () => {
|
||||||
<SupervisionCoverage data={infos} />
|
<SupervisionCoverage data={infos} />
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard
|
<CommonCard
|
||||||
title="掌握全要素"
|
title="监测全要素"
|
||||||
className="panel-card card-2"
|
className="panel-card card-2"
|
||||||
headerExtra={<ThreeDots onClick={handleOpenModal} />}
|
headerExtra={<ThreeDots onClick={handleOpenModal} />}
|
||||||
>
|
>
|
||||||
|
|
@ -96,16 +77,12 @@ const SiQuan = () => {
|
||||||
<CommonCard
|
<CommonCard
|
||||||
title="管控全天候"
|
title="管控全天候"
|
||||||
className="panel-card card-1"
|
className="panel-card card-1"
|
||||||
headerExtra={<ThreeDots onClick={handleOpenAllWeatherModal} />}
|
headerExtra={<ThreeDots onClick={() => console.log('管控全天候 clicked')} />}
|
||||||
>
|
>
|
||||||
<AllWeatherControl />
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard
|
<CommonCard title="管理全周期" className="panel-card card-3">
|
||||||
title="管理全周期"
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
className="panel-card card-3"
|
|
||||||
headerExtra={<ThreeDots onClick={handleOpenCycleModal} />}
|
|
||||||
>
|
|
||||||
<ManagementCycle />
|
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -113,23 +90,17 @@ const SiQuan = () => {
|
||||||
<CommonModal
|
<CommonModal
|
||||||
visible={modalVisible}
|
visible={modalVisible}
|
||||||
onClose={handleCloseModal}
|
onClose={handleCloseModal}
|
||||||
title={modalType === 'monitor' ? "掌握全要素" : (modalType === 'cycle' ? "全周期档案" : "管控全天候")}
|
title="掌握全要素"
|
||||||
tabs={modalType === 'monitor' ? tabs : (modalType === 'allweather' ? tabsAllWeather : [])}
|
tabs={tabs}
|
||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
onTabChange={handleTabChange}
|
onTabChange={handleTabChange}
|
||||||
width={modalType === 'cycle' ? '70%':modalType === 'allweather' ? '90%': undefined}
|
|
||||||
>
|
>
|
||||||
{/* Content changes based on activeTab */}
|
{/* Content changes based on activeTab */}
|
||||||
<div style={{color: '#fff', height: '100%' }}>
|
<div style={{color: '#fff', height: '100%' }}>
|
||||||
{modalType === 'monitor' && (
|
|
||||||
<>
|
|
||||||
{activeTab === 'kqys' && <ReservoirAreaElements />}
|
{activeTab === 'kqys' && <ReservoirAreaElements />}
|
||||||
{activeTab === 'gcys' && <EngineeringElements data={infos} />}
|
{activeTab === 'gcys' && <EngineeringElements data={infos} />}
|
||||||
{activeTab === 'xyys' && <DownstreamElements />}
|
{activeTab === 'xyys' && <DownstreamElements />}
|
||||||
</>
|
{/* {activeTab === 'glys' && <ManagementElements />} */}
|
||||||
)}
|
|
||||||
{modalType === 'cycle' && <CycleArchive />}
|
|
||||||
{modalType === 'allweather' && <AllWeatherModal active={activeTab} />}
|
|
||||||
</div>
|
</div>
|
||||||
</CommonModal>
|
</CommonModal>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-part {
|
.right-part {
|
||||||
.card-1 { flex: 5; }
|
.card-1 { flex: 4; }
|
||||||
.card-2 { flex: 3; }
|
.card-2 { flex: 3; }
|
||||||
.card-3 { flex: 2; }
|
.card-3 { flex: 1; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { Table, Input, Button } from 'antd';
|
|
||||||
import { SearchOutlined } from '@ant-design/icons';
|
|
||||||
import CommonModal from '@/views/Home/components/UI/CommonModal';
|
|
||||||
import usePageTable from '@/components/crud/usePageTable';
|
|
||||||
import { createCrudService } from '@/components/crud/_';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
|
|
||||||
const OrgnizeModal = ({ visible, onClose,title }) => {
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ title: '序号', dataIndex: 'inx', key: 'inx', width: 60, align: 'center' },
|
|
||||||
{ title: '用户姓名', dataIndex: 'spec', key: 'spec', align: 'center' },
|
|
||||||
{ title: '手机号码', dataIndex: 'phone', key: 'phone', align: 'center' },
|
|
||||||
{ title: '部门', dataIndex: 'contactPerson', key: 'contactPerson', align: 'center' },
|
|
||||||
];
|
|
||||||
const { tableProps, search} = usePageTable(createCrudService(apiurl.sz.jqjz.wzPage).find);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
search()
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CommonModal
|
|
||||||
visible={visible}
|
|
||||||
onClose={onClose}
|
|
||||||
title={title}
|
|
||||||
width="70%"
|
|
||||||
>
|
|
||||||
<div className="material-modal-content">
|
|
||||||
<div className="table-wrapper">
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
{...tableProps}
|
|
||||||
className="custom-table"
|
|
||||||
size="middle"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CommonModal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default OrgnizeModal;
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { UserOutlined, BankOutlined, ApartmentOutlined } from '@ant-design/icons';
|
|
||||||
import textBg from '@/assets/images/card/textbg.png';
|
|
||||||
import arrowIcon from '@/assets/images/card/arrow.png';
|
|
||||||
import OrgnizeModal from './OrgnizeModal'
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const PerfectSystem = ({ data }) => {
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
|
||||||
const [modalTitle, setModalTitle] = useState('');
|
|
||||||
const managementInfo = [
|
|
||||||
{ label: '管理单位', value: data?.managName ?? '-', icon: <BankOutlined />, type: 'unit' },
|
|
||||||
{ label: '负责人', value: data?.chargePerson ?? '-', icon: <UserOutlined />, type: 'person' },
|
|
||||||
{ label: '归口管理部门', value: data?.admDep ?? '-', icon: <ApartmentOutlined />, type: 'dept' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const leftOrg = [
|
|
||||||
{ name: '工程科', count: 3 },
|
|
||||||
{ name: '办公室', count: 2 },
|
|
||||||
{ name: '财务科', count: 1 },
|
|
||||||
{ name: '...', count: null },
|
|
||||||
];
|
|
||||||
|
|
||||||
const rightOrg = [
|
|
||||||
{ name: '后勤保障', subName: '中心', count: 5 },
|
|
||||||
{ name: '...', count: null },
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleCardClick = (item) => {
|
|
||||||
setModalTitle(item.name);
|
|
||||||
setModalVisible(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="perfect-system">
|
|
||||||
<div className="section">
|
|
||||||
<div className="section-title">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span>管理单位</span>
|
|
||||||
</div>
|
|
||||||
<div className="info-list">
|
|
||||||
{managementInfo.map((item, index) => (
|
|
||||||
<div key={index} className="info-item" style={{ backgroundImage: `url(${textBg})` }}>
|
|
||||||
<div className={`icon-box ${item.type}`}>
|
|
||||||
{item.icon}
|
|
||||||
</div>
|
|
||||||
<div className="info-content">
|
|
||||||
<span className="label">{item.label}:</span>
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="section mt-15">
|
|
||||||
<div className="section-title">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span>水库组织机构</span>
|
|
||||||
</div>
|
|
||||||
<div className="org-chart">
|
|
||||||
<div className="side-column left">
|
|
||||||
{leftOrg.map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={`org-node ${item.count === null ? 'placeholder' : ''}`}
|
|
||||||
style={{cursor:'pointer'}}
|
|
||||||
onClick={() => handleCardClick(item)}
|
|
||||||
>
|
|
||||||
<span className="node-text" style={{borderBottom:"1px solid #00a0e9"}}>{item.name}{item.count !== null ? `(${item.count})` : ''}</span>
|
|
||||||
<div className="connector-line"></div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="center-column">
|
|
||||||
<div className="main-node">
|
|
||||||
<span className="vertical-text">双石水库管理处</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="side-column right">
|
|
||||||
{rightOrg.map((item, idx) => (
|
|
||||||
<div
|
|
||||||
key={idx}
|
|
||||||
className={`org-node ${item.count === null ? 'placeholder' : ''}`}
|
|
||||||
style={{cursor:'pointer'}}
|
|
||||||
onClick={() => handleCardClick(item)}
|
|
||||||
>
|
|
||||||
<div className="connector-line"></div>
|
|
||||||
<span className="node-text" style={{borderBottom:"1px solid #00a0e9"}}>
|
|
||||||
{item.name}
|
|
||||||
{item.subName && <><br />{item.subName}</>}
|
|
||||||
{item.count !== null ? `(${item.count})` : ''}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
{/* Fill empty space to match left side height roughly */}
|
|
||||||
<div className="org-node placeholder" style={{ opacity: 0 }}>
|
|
||||||
<div className="connector-line"></div>
|
|
||||||
</div>
|
|
||||||
<div className="org-node placeholder" style={{ opacity: 0 }}>
|
|
||||||
<div className="connector-line"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<OrgnizeModal
|
|
||||||
visible={modalVisible}
|
|
||||||
title={modalTitle}
|
|
||||||
onClose={() => setModalVisible(false)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PerfectSystem;
|
|
||||||
|
|
@ -1,206 +0,0 @@
|
||||||
.perfect-system {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: #fff;
|
|
||||||
padding: 5px;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
// Scrollbar hidden
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
&.mt-15 {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 8px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 5px rgba(0, 160, 233, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-list {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 5px;
|
|
||||||
|
|
||||||
.info-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px 10px;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
.icon-box {
|
|
||||||
width: 18px;
|
|
||||||
height: 18px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-right: 15px;
|
|
||||||
font-size: 18px;
|
|
||||||
|
|
||||||
&.unit {
|
|
||||||
background: linear-gradient(135deg, #1890ff 0%, #0050b3 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.person {
|
|
||||||
background: linear-gradient(135deg, #00eaff 0%, #006d75 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.dept {
|
|
||||||
background: linear-gradient(135deg, #722ed1 0%, #391085 100%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-content {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex: 1;
|
|
||||||
font-size: 14px;
|
|
||||||
justify-content: space-between;
|
|
||||||
.label {
|
|
||||||
color: #CCF3FF;
|
|
||||||
margin-right: 10px;
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
color: #CCF3FF;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.org-chart {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: stretch;
|
|
||||||
padding: 0 5px;
|
|
||||||
height: 200px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.center-column {
|
|
||||||
flex: 0 0 60px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
z-index: 2;
|
|
||||||
|
|
||||||
.main-node {
|
|
||||||
background: url('../../../../../../../assets/images/card/ognize.png');
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
padding: 10px 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.vertical-text {
|
|
||||||
writing-mode: vertical-rl;
|
|
||||||
font-size: 16px;
|
|
||||||
letter-spacing: 4px;
|
|
||||||
color: #fff;
|
|
||||||
text-orientation: upright;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-column {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 5px 0;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.org-node {
|
|
||||||
position: relative;
|
|
||||||
background: url('../../../../../../../assets/images/card/smallCard.png');
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 5px;
|
|
||||||
text-align: center;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 13px;
|
|
||||||
min-height: 32px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 90px;
|
|
||||||
box-shadow: inset 0 0 10px rgba(0, 160, 233, 0.2);
|
|
||||||
|
|
||||||
&.placeholder {
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
box-shadow: none;
|
|
||||||
|
|
||||||
.node-text {
|
|
||||||
font-size: 20px;
|
|
||||||
color: rgba(255, 255, 255, 0.5);
|
|
||||||
// border-bottom: 1px solid #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.connector-line {
|
|
||||||
border-top-color: rgba(0, 160, 233, 0.3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.connector-line {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
height: 1px;
|
|
||||||
border-top: 1px dashed #00a0e9;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.left {
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
.org-node {
|
|
||||||
.connector-line {
|
|
||||||
left: 100%;
|
|
||||||
width: 500px; // Large width to ensure it reaches center
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.right {
|
|
||||||
align-items: flex-end;
|
|
||||||
|
|
||||||
.org-node {
|
|
||||||
.connector-line {
|
|
||||||
right: 100%;
|
|
||||||
width: 500px; // Large width to ensure it reaches center
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import CommonModal from '@/views/Home/components/UI/CommonModal';
|
|
||||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const GalleryModal = ({ visible, onClose, title, data = [] }) => {
|
|
||||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
setSelectedIndex(0);
|
|
||||||
}
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
if (!visible) return null;
|
|
||||||
|
|
||||||
const currentItem = data[selectedIndex] || {};
|
|
||||||
|
|
||||||
const handlePrev = () => {
|
|
||||||
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : data.length - 1));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNext = () => {
|
|
||||||
setSelectedIndex((prev) => (prev < data.length - 1 ? prev + 1 : 0));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CommonModal
|
|
||||||
visible={visible}
|
|
||||||
onClose={onClose}
|
|
||||||
title={title}
|
|
||||||
width={'60%'}
|
|
||||||
|
|
||||||
>
|
|
||||||
<div className="gallery-modal-content">
|
|
||||||
<div className="main-image-container">
|
|
||||||
<img src={currentItem.url} alt={currentItem.name} className="main-image" />
|
|
||||||
</div>
|
|
||||||
<div className="image-info">
|
|
||||||
图片名称:{currentItem.name}
|
|
||||||
</div>
|
|
||||||
<div className="thumbnail-strip">
|
|
||||||
<div className="scroll-btn left" onClick={handlePrev}><LeftOutlined /></div>
|
|
||||||
<div className="thumbnails-wrapper">
|
|
||||||
{data.map((item, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className={`thumbnail-item ${index === selectedIndex ? 'thumb-active' : ''}`}
|
|
||||||
onClick={() => setSelectedIndex(index)}
|
|
||||||
>
|
|
||||||
<img src={item.url} alt={item.name} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="scroll-btn right" onClick={handleNext}><RightOutlined /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CommonModal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default GalleryModal;
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
.gallery-modal-content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 700px;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.main-image-container {
|
|
||||||
flex: 1;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
background: #000;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.main-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
// object-fit: contain;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
// background: rgba(0, 0, 0, 0.5);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 20px;
|
|
||||||
color: #fff;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 160, 233, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.prev { left: 20px; }
|
|
||||||
&.next { right: 20px; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-info {
|
|
||||||
height: 40px;
|
|
||||||
line-height: 40px;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnail-strip {
|
|
||||||
height: 100px;
|
|
||||||
// background: #111;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0 10px;
|
|
||||||
border-top: 1px solid #0181e6;
|
|
||||||
|
|
||||||
.scroll-btn {
|
|
||||||
width: 30px;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 18px;
|
|
||||||
&:hover { color: #00a0e9; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnails-wrapper {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
overflow-x: auto;
|
|
||||||
padding: 10px;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 4px;
|
|
||||||
background: #333;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background: #666;
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thumbnail-item {
|
|
||||||
width: 120px;
|
|
||||||
height: 70px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
transition: all 0.2s;
|
|
||||||
user-select: none;
|
|
||||||
outline: none;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.thumb-active {
|
|
||||||
border-color: #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: rgba(0, 160, 233, 0.6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { Table, Input, Button } from 'antd';
|
|
||||||
import { SearchOutlined } from '@ant-design/icons';
|
|
||||||
import CommonModal from '@/views/Home/components/UI/CommonModal';
|
|
||||||
import usePageTable from '@/components/crud/usePageTable';
|
|
||||||
import { createCrudService } from '@/components/crud/_';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const MaterialModal = ({ visible, onClose }) => {
|
|
||||||
const unitType = {
|
|
||||||
1:'个',
|
|
||||||
2:'件',
|
|
||||||
3:'米',
|
|
||||||
4:'把',
|
|
||||||
5:'台',
|
|
||||||
6:'套',
|
|
||||||
7:'副',
|
|
||||||
8:'箱',
|
|
||||||
9:'卷',
|
|
||||||
10:'立方米',
|
|
||||||
11:'平方米',
|
|
||||||
}
|
|
||||||
const [searchText, setSearchText] = useState('');
|
|
||||||
|
|
||||||
const columns = [
|
|
||||||
{ title: '序号', dataIndex: 'inx', key: 'inx', width: 60, align: 'center' },
|
|
||||||
{
|
|
||||||
title: '物资名称',
|
|
||||||
dataIndex: 'goodsName',
|
|
||||||
key: 'goodsName',
|
|
||||||
align: 'center',
|
|
||||||
sorter: (a, b) => (a.goodsName || '').localeCompare(b.goodsName || ''),
|
|
||||||
showSorterTooltip: { title: '整理' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '物资类型', dataIndex: 'goodsType', key: 'goodsType', align: 'center',
|
|
||||||
render: (_, record) => <span>{ record.goodsType === 1 ? "抢险物资" : "救生器材"}</span>
|
|
||||||
},
|
|
||||||
{ title: '规格', dataIndex: 'spec', key: 'spec', align: 'center' },
|
|
||||||
{
|
|
||||||
title: '单位', dataIndex: 'unit', key: 'unit', align: 'center',
|
|
||||||
render: (v) => <span>{unitType[v]}</span>
|
|
||||||
},
|
|
||||||
{ title: '库存数量', dataIndex: 'storeQuantity', key: 'storeQuantity', align: 'center' },
|
|
||||||
{
|
|
||||||
title: '仓库地点',
|
|
||||||
dataIndex: 'storeLocation',
|
|
||||||
key: 'storeLocation',
|
|
||||||
align: 'center',
|
|
||||||
sorter: (a, b) => (a.storeLocation || '').localeCompare(b.storeLocation || ''),
|
|
||||||
showSorterTooltip: { title: '整理' }
|
|
||||||
},
|
|
||||||
{ title: '联系人', dataIndex: 'contactPerson', key: 'contactPerson', align: 'center' },
|
|
||||||
{ title: '联系电话', dataIndex: 'phone', key: 'phone', align: 'center' },
|
|
||||||
];
|
|
||||||
const { tableProps, search} = usePageTable(createCrudService(apiurl.sz.jqjz.wzPage).find);
|
|
||||||
|
|
||||||
const handleSearch = () => {
|
|
||||||
let params = {
|
|
||||||
search: {
|
|
||||||
goodsName:searchText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
search(params);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (visible) {
|
|
||||||
handleSearch()
|
|
||||||
}
|
|
||||||
setSearchText('')
|
|
||||||
}, [visible]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CommonModal
|
|
||||||
visible={visible}
|
|
||||||
onClose={onClose}
|
|
||||||
title="防汛物资"
|
|
||||||
width="70%"
|
|
||||||
>
|
|
||||||
<div className="material-modal-content">
|
|
||||||
<div className="search-bar">
|
|
||||||
<span className="label">物资名称:</span>
|
|
||||||
<Input
|
|
||||||
value={searchText}
|
|
||||||
onChange={e => setSearchText(e.target.value)}
|
|
||||||
placeholder="请输入物资名称"
|
|
||||||
style={{ width: 240 }}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
<Button type="primary" icon={<SearchOutlined />} onClick={handleSearch} className="search-btn ant-btn-ghost-blue">查询</Button>
|
|
||||||
</div>
|
|
||||||
<div className="table-wrapper">
|
|
||||||
<Table
|
|
||||||
columns={columns}
|
|
||||||
{...tableProps}
|
|
||||||
className="custom-table"
|
|
||||||
size="middle"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CommonModal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MaterialModal;
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
.material-modal-content {
|
|
||||||
padding: 10px;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
.search-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
margin-right: 10px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.search-btn {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,187 +0,0 @@
|
||||||
import React, { useState,useEffect } from 'react';
|
|
||||||
import moment from 'moment';
|
|
||||||
import arrowIcon from '@/assets/images/card/arrow.png';
|
|
||||||
import smallCard from '@/assets/images/card/smallCard.png';
|
|
||||||
import homeImg from '@/assets/images/home.png'; // Placeholder for gallery
|
|
||||||
import jingfeiIcon from '@/assets/images/card/jingfei.png';
|
|
||||||
import lightBg from '@/assets/images/card/light.png';
|
|
||||||
import GalleryModal from './GalleryModal';
|
|
||||||
import MaterialModal from './MaterialModal';
|
|
||||||
import YearSelect from '@/views/Home/components/UI/YearSelect';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import { httpget } from '@/utils/request';
|
|
||||||
import { config } from '@/config';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
const SoundMechanism = () => {
|
|
||||||
const [year, setYear] = useState(moment().format('YYYY'));
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
|
||||||
const [materialVisible, setMaterialVisible] = useState(false);
|
|
||||||
const [modalTitle, setModalTitle] = useState('');
|
|
||||||
const [galleryData, setGalleryData] = useState([]);
|
|
||||||
|
|
||||||
|
|
||||||
const [manageInfo, setManageInfo] = useState({}) //管理设施
|
|
||||||
const [budgetInfo, setBudgetInfo] = useState({}) //经费
|
|
||||||
// Mock data for gallery
|
|
||||||
const houseImages = [
|
|
||||||
{ name: '管理用房.jpg', url: homeImg },
|
|
||||||
{ name: '监控室.jpg', url: homeImg },
|
|
||||||
{ name: '水库全景.jpg', url: homeImg },
|
|
||||||
{ name: '会议室.jpg', url: homeImg },
|
|
||||||
{ name: '值班室.jpg', url: homeImg },
|
|
||||||
{ name: '物资仓库.jpg', url: homeImg },
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Facility Data (Mocked as per UI)
|
|
||||||
const facilities = [
|
|
||||||
{ value: manageInfo?.managementHousing??'-', unit: 'm²', label: '管理用房', underline: true, clickable: true},
|
|
||||||
{ value: manageInfo?.rainWaterCount??'-', unit: '个', label: '雨水情测报' },
|
|
||||||
{ value: manageInfo?.safeCheckCount??'-', unit: '个', label: '安全监测设施' },
|
|
||||||
{ value: manageInfo?.cctvCount??'-', unit: '个', label: '视频监控设施' },
|
|
||||||
{ value: manageInfo?.goodsTypeCount??'-', unit: '项', label: '防汛物资种类', underline: true, clickable: true, type: 'material' },
|
|
||||||
{ value: manageInfo?.roadLength??'-', unit: '米', label: '防汛道路' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const handleCardClick = (item) => {
|
|
||||||
if (!item.clickable) return;
|
|
||||||
if (item.label == '管理用房') {
|
|
||||||
getManagePic()
|
|
||||||
} else if (item.type === 'material') {
|
|
||||||
setMaterialVisible(true);
|
|
||||||
} else {
|
|
||||||
setModalTitle(item.label);
|
|
||||||
setModalVisible(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Funding Data (Mocked as per UI)
|
|
||||||
const fundingData = [
|
|
||||||
{ label: '年度收入预算', value: budgetInfo?.annualExpenditureBudget ?? '-', unit: '万元' },
|
|
||||||
{ label: '年度支出预算', value: budgetInfo?.annualIncomeBudget ?? '-', unit: '万元' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// 获取管理设施
|
|
||||||
const getManage= async () => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sz.jqjz.manageInfo)
|
|
||||||
if (result.code == 200) {
|
|
||||||
setManageInfo(result?.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取管理用房图片
|
|
||||||
const getManagePic= async () => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sz.jqjz.managePic)
|
|
||||||
if (result.code == 200) {
|
|
||||||
const files = result.data?.files || [];
|
|
||||||
if (files.length > 0) {
|
|
||||||
setGalleryData(files.map(item=>({name:item.fileName,url:config.minioIp +item.filePath})))
|
|
||||||
setModalTitle('管理用房');
|
|
||||||
setModalVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取年度预算
|
|
||||||
const getBudget = async (params) => {
|
|
||||||
try {
|
|
||||||
const result = await httpget(apiurl.sz.jqjz.budgetInfo + params)
|
|
||||||
if (result.code == 200) {
|
|
||||||
setBudgetInfo(result.data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getBudget(year)
|
|
||||||
}, [year])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
getManage()
|
|
||||||
}, [])
|
|
||||||
return (
|
|
||||||
<div className="sound-mechanism">
|
|
||||||
{/* Section 1: Management Facilities */}
|
|
||||||
<div className="section">
|
|
||||||
<div className="section-title">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span>管理设施</span>
|
|
||||||
</div>
|
|
||||||
<div className="facility-grid">
|
|
||||||
{facilities.map((item, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className={`facility-card ${item.clickable ? 'clickable' : ''}`}
|
|
||||||
style={{ backgroundImage: `url(${smallCard})` }}
|
|
||||||
onClick={() => handleCardClick(item)}
|
|
||||||
>
|
|
||||||
<div className={`value-wrapper ${item.underline ? 'underlined' : ''}`}>
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
<span className="unit">{item.unit}</span>
|
|
||||||
</div>
|
|
||||||
<div className="label">{item.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<GalleryModal
|
|
||||||
visible={modalVisible}
|
|
||||||
title={modalTitle}
|
|
||||||
onClose={() => setModalVisible(false)}
|
|
||||||
data={galleryData}
|
|
||||||
/>
|
|
||||||
<MaterialModal
|
|
||||||
visible={materialVisible}
|
|
||||||
onClose={() => setMaterialVisible(false)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Section 2: Funding Guarantee */}
|
|
||||||
<div className="section mt-15">
|
|
||||||
<div className="section-header">
|
|
||||||
<div className="title-wrapper">
|
|
||||||
<img src={arrowIcon} alt="arrow" className="arrow-icon" />
|
|
||||||
<span>经费保障</span>
|
|
||||||
</div>
|
|
||||||
<YearSelect
|
|
||||||
value={year}
|
|
||||||
onChange={setYear}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="funding-container">
|
|
||||||
{fundingData.map((item, index) => (
|
|
||||||
<div key={index} className="funding-item">
|
|
||||||
<div className="icon-wrapper" style={{width:40}}>
|
|
||||||
<img src={jingfeiIcon} alt="icon" className="jingfei-icon" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="content"
|
|
||||||
style={{ backgroundImage: `url(${lightBg})` }}
|
|
||||||
>
|
|
||||||
<div className="value-row">
|
|
||||||
<span className="value">{item.value}</span>
|
|
||||||
<span className="unit">{item.unit}</span>
|
|
||||||
</div>
|
|
||||||
<div className="label">{item.label}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SoundMechanism;
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
.sound-mechanism {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
color: #fff;
|
|
||||||
padding: 5px;
|
|
||||||
overflow-y: hidden;
|
|
||||||
|
|
||||||
// Scrollbar hidden
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section {
|
|
||||||
margin-bottom: 0px;
|
|
||||||
&.mt-15 { margin-top: 5px; }
|
|
||||||
|
|
||||||
.section-title, .section-header .title-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
.arrow-icon {
|
|
||||||
width: 20px;
|
|
||||||
height: 18px;
|
|
||||||
margin-right: 8px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
text-shadow: 0 0 5px rgba(0, 160, 233, 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.facility-grid {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.facility-card {
|
|
||||||
width: calc((100% - 20px) / 3);
|
|
||||||
height: 70px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
margin-right: 10px;
|
|
||||||
&:nth-child(3n) { margin-right: 0; }
|
|
||||||
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
&.clickable {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-wrapper {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
&.underlined {
|
|
||||||
border-bottom: 1px solid #00a0e9;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.value {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00D8FF;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
.unit {
|
|
||||||
font-size: 12px;
|
|
||||||
color: rgba(255, 255, 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: rgba(255, 255, 255, 0.9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.funding-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 0 10px;
|
|
||||||
margin-top: 15px;
|
|
||||||
|
|
||||||
.funding-item {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 15px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
// Add a glow effect at the bottom like in the UI
|
|
||||||
&::after {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
bottom: -10px;
|
|
||||||
left: 20%;
|
|
||||||
width: 60%;
|
|
||||||
height: 10px;
|
|
||||||
background: radial-gradient(ellipse at center, rgba(0, 160, 233, 0.4) 0%, rgba(0,0,0,0) 70%);
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-wrapper {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.jingfei-icon {
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
object-fit: contain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: 1;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
justify-content: center;
|
|
||||||
padding-left: 10px;
|
|
||||||
width: 140px;
|
|
||||||
height: 60px;
|
|
||||||
|
|
||||||
.value-row {
|
|
||||||
.value {
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #00D8FF;
|
|
||||||
margin-right: 4px;
|
|
||||||
}
|
|
||||||
.unit {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.label {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #fff;
|
|
||||||
margin-top: 2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,36 +1,16 @@
|
||||||
import React, { useState, useEffect } from 'react';
|
import React from 'react';
|
||||||
import CommonCard from '../../UI/CommonCard';
|
import CommonCard from '../../UI/CommonCard';
|
||||||
import PerfectSystem from './components/PerfectSystem';
|
|
||||||
import SoundMechanism from './components/SoundMechanism';
|
|
||||||
import { httppost } from '@/utils/request';
|
|
||||||
import apiurl from '@/service/apiurl';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const SiZhi = () => {
|
const SiZhi = () => {
|
||||||
const [infos, setInfos] = useState({});
|
|
||||||
|
|
||||||
const getInfo = async () => {
|
|
||||||
try {
|
|
||||||
const result = await httppost(apiurl.sq.qfg.info);
|
|
||||||
if (result.code == 200) {
|
|
||||||
const info = result.data[0];
|
|
||||||
setInfos(info);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
getInfo();
|
|
||||||
}, []);
|
|
||||||
return (
|
return (
|
||||||
<div className="sizhi-view">
|
<div className="sizhi-view">
|
||||||
<div className="side-panel left">
|
<div className="side-panel left">
|
||||||
<CommonCard title="完善体系" className="panel-card card-1">
|
<CommonCard title="完善体系" className="panel-card card-1">
|
||||||
<PerfectSystem data={infos}/>
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
<CommonCard title="健全机制" className="panel-card card-2">
|
<CommonCard title="健全机制" className="panel-card card-2">
|
||||||
<SoundMechanism />
|
<div className="placeholder-content">内容填充区域</div>
|
||||||
</CommonCard>
|
</CommonCard>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { CloseOutlined } from '@ant-design/icons';
|
||||||
import titleBg from '@/assets/images/modal/title.png';
|
import titleBg from '@/assets/images/modal/title.png';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const CommonModal = ({ visible, onClose, title, children, width, tabs = [], activeTab, onTabChange, bodyStyle = {} }) => {
|
const CommonModal = ({ visible, onClose, title, children, width, tabs = [], activeTab, onTabChange }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
//当弹框打开时,如果不加这行代码,用户滚动鼠标滚轮时,背后的页面(Home 页)也会跟着滚动
|
//当弹框打开时,如果不加这行代码,用户滚动鼠标滚轮时,背后的页面(Home 页)也会跟着滚动
|
||||||
|
|
@ -45,7 +45,7 @@ const CommonModal = ({ visible, onClose, title, children, width, tabs = [], acti
|
||||||
<CloseOutlined />
|
<CloseOutlined />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-body" style={bodyStyle}>
|
<div className="modal-body">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,34 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import CommonModal from '../CommonModal';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { config } from "@/config";
|
|
||||||
const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
||||||
console.log(url);
|
if (!visible) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommonModal
|
<div style={{
|
||||||
visible={visible}
|
position: 'fixed',
|
||||||
title={title}
|
top: 0,
|
||||||
onClose={onClose}
|
left: 0,
|
||||||
width="60%"
|
width: '100vw',
|
||||||
bodyStyle={{ padding: 0, overflow: 'hidden' }}
|
height: '100vh',
|
||||||
>
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
zIndex: 2000,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
}}>
|
||||||
|
<div style={{ width: '60%', height: '80vh', display: 'flex', flexDirection: 'column', position: 'relative' }}>
|
||||||
|
<CloseOutlined
|
||||||
|
onClick={onClose}
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
top: '-30px',
|
||||||
|
right: 0,
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: '18px',
|
||||||
|
cursor: 'pointer'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<iframe
|
<iframe
|
||||||
style={{
|
style={{
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
|
@ -22,7 +39,8 @@ const PdfView = ({ visible, title, onClose, url, fileId }) => {
|
||||||
src={`${process.env.PUBLIC_URL}/static/pdf/web/viewer.html?file=${encodeURIComponent(url + fileId)}`}
|
src={`${process.env.PUBLIC_URL}/static/pdf/web/viewer.html?file=${encodeURIComponent(url + fileId)}`}
|
||||||
title={title}
|
title={title}
|
||||||
/>
|
/>
|
||||||
</CommonModal>
|
</div>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default PdfView
|
export default PdfView
|
||||||
|
|
@ -1,27 +1,22 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DatePicker } from 'antd';
|
import { Select } from 'antd';
|
||||||
import moment from 'moment';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const YearSelect = ({ value, onChange, className, style, ...props }) => {
|
const { Option } = Select;
|
||||||
const handleChange = (date, dateString) => {
|
|
||||||
if (onChange) {
|
|
||||||
onChange(dateString);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const YearSelect = ({ defaultValue = "2025", style, className, ...props }) => (
|
||||||
<DatePicker
|
<Select
|
||||||
picker="year"
|
defaultValue={defaultValue}
|
||||||
value={value ? moment(value, 'YYYY') : null}
|
style={{ width: 80, color: '#fff',marginRight:15,marginTop:5, ...style }}
|
||||||
onChange={handleChange}
|
bordered={false}
|
||||||
className={`custom-year-select ${className || ''}`}
|
dropdownClassName="year-select-dropdown"
|
||||||
dropdownClassName="custom-year-select-dropdown"
|
className={`year-select ${className || ''}`}
|
||||||
style={style}
|
|
||||||
allowClear={false}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
);
|
<Option value="2025">2025</Option>
|
||||||
};
|
<Option value="2024">2024</Option>
|
||||||
|
<Option value="2023">2023</Option>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
|
||||||
export default YearSelect;
|
export default YearSelect;
|
||||||
|
|
|
||||||
|
|
@ -1,81 +1,23 @@
|
||||||
.custom-year-select {
|
.year-select {
|
||||||
background: transparent !important;
|
color: #fff;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3) !important; // Added border
|
.ant-select-selector {
|
||||||
border-radius: 4px; // Optional: rounded corners for better look
|
|
||||||
width: 90px;
|
|
||||||
|
|
||||||
input {
|
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
font-size: 14px;
|
background-color: transparent !important;
|
||||||
font-weight: normal; // Changed from bold to normal
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
.ant-select-arrow {
|
||||||
&:hover {
|
|
||||||
border-color: #00a0e9 !important; // Highlight border on hover
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-suffix {
|
|
||||||
color: #00a0e9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-clear {
|
|
||||||
background: transparent;
|
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.ant-picker-focused {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-year-select-dropdown {
|
// Global styles for dropdown (since it renders in body)
|
||||||
background-color: rgba(0, 40, 70, 0.95) !important;
|
.year-select-dropdown {
|
||||||
border: 1px solid #00a0e9;
|
background-color: rgba(0, 20, 50, 0.9) !important;
|
||||||
|
border: 1px solid rgba(0, 160, 233, 0.3);
|
||||||
|
|
||||||
.ant-picker-header {
|
.ant-select-item {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
&:hover, &.ant-select-item-option-selected {
|
||||||
|
background-color: rgba(0, 160, 233, 0.3);
|
||||||
button {
|
|
||||||
color: #fff;
|
|
||||||
&:hover { color: #00a0e9; }
|
|
||||||
}
|
|
||||||
.ant-picker-header-super-prev-btn, .ant-picker-header-super-next-btn {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-body {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-content {
|
|
||||||
th, td { color: #fff; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell {
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
&:hover .ant-picker-cell-inner {
|
|
||||||
background-color: rgba(0, 160, 233, 0.3) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell-in-view {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell-selected .ant-picker-cell-inner {
|
|
||||||
background-color: #00a0e9 !important;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-year-panel {
|
|
||||||
.ant-picker-cell-inner {
|
|
||||||
color: #fff;
|
|
||||||
&:hover {
|
|
||||||
background: rgba(0, 160, 233, 0.3);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||