Compare commits

...

5 Commits
master ... test

Author SHA1 Message Date
李神峰 475e670b7c feat(): 预测水位优化 2025-03-06 09:41:36 +08:00
李神峰 2d60b17fbe feat(): 预测水位功能开发 2025-03-03 17:37:19 +08:00
李神峰 c6fd00bb26 fix(): 表单校验 2024-12-27 10:55:33 +08:00
李神峰 79d8a0d0a1 fix(): 图标优化 2024-12-27 10:34:43 +08:00
李神峰 01204ca96d feat(): 接口对接 2024-12-26 09:11:53 +08:00
13 changed files with 701 additions and 542 deletions

View File

@ -1 +1 @@
PUBLIC_URL=/ykz PUBLIC_URL=/test

View File

@ -1,2 +1,2 @@
GENERATE_SOURCEMAP=false GENERATE_SOURCEMAP=false
PUBLIC_URL=/ykz PUBLIC_URL=/test

View File

@ -29,7 +29,7 @@
<script type="text/javascript" src="%PUBLIC_URL%/popmotion.xl.min.js"></script> <script type="text/javascript" src="%PUBLIC_URL%/popmotion.xl.min.js"></script>
<script type="text/javascript" src="%PUBLIC_URL%/imouplayer.js"></script> <script type="text/javascript" src="%PUBLIC_URL%/imouplayer.js"></script>
<script src="%PUBLIC_URL%/h5Player/h5player.min.js"></script> <script src="%PUBLIC_URL%/h5Player/h5player.min.js"></script>
<title>盐卡引水闸信息化系统</title> <title>测试</title>
<style> <style>
.lf{ .lf{

View File

@ -187,8 +187,12 @@ export async function loadMenu(): Promise<MenuItem[]> {
const tree = buildTree(data,0) const tree = buildTree(data,0)
const tree2 = tree?.filter((item:any) =>(item.menuId != "1" && item.menuId != "2" && item.menuId != "3")) const tree2 = tree?.filter((item:any) =>(item.menuId != "1" && item.menuId != "2" && item.menuId != "3"))
handelTreeData(tree2,id) handelTreeData(tree2,id)
return tree2 // return tree2
return [
{ id: id(), title: '测试', path: '/testLine', icon: 'yzt' },
]
// const id = idgen() // const id = idgen()
// return [ // return [

View File

@ -3,7 +3,12 @@
const service_fxdd = '/gunshiApp/tsg' const service_fxdd = '/gunshiApp/tsg'
const service_xyt = '/gunshiApp/tsg'//登陆先用小玉潭 const service_xyt = '/gunshiApp/tsg'//登陆先用小玉潭
const service_ykz = '/gunshiApp/ykz' const service_ykz = '/gunshiApp/ykz'
const service_test = '/gunshiApp/dcpj'
const apiurl = { const apiurl = {
test: {
find: service_test + '/stPRHisData/getHisData'
},
setMenu: service_fxdd + '/visitMenuLog/insert', setMenu: service_fxdd + '/visitMenuLog/insert',
setPassword: service_fxdd + '/user/updateSecretKey', setPassword: service_fxdd + '/user/updateSecretKey',
login: { login: {

View File

@ -3,9 +3,9 @@ const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) { module.exports = function (app) {
//咸丰调度系统 //咸丰调度系统
app.use( app.use(
'/gunshiApp', '/api',
createProxyMiddleware({ createProxyMiddleware({
target: 'http://local.gunshiiot.com:18083/',//测试 target: 'http://202.103.169.18:10100/',//测试
// target: 'http://192.168.66.7:24106/',//正式 // target: 'http://192.168.66.7:24106/',//正式
// target: 'http://36.139.207.50:18083/',//移动云 // target: 'http://36.139.207.50:18083/',//移动云
// target: 'http://192.168.66.49:24105/',//移动云 // target: 'http://192.168.66.49:24105/',//移动云
@ -24,4 +24,13 @@ module.exports = function (app) {
}, },
}) })
); );
//卫星云图,雷达回波需要
app.use(
'/gunshiApp/dcpj',
createProxyMiddleware({
target: 'http://local.gunshiiot.com:18083',
changeOrigin: true,
})
);
}; };

View File

@ -26,6 +26,8 @@ import MenuM from './SystemMangant/menuM'
import TestLine from './TestLine' import TestLine from './TestLine'
// const HomePage = lazy(() => import('./Home')) // const HomePage = lazy(() => import('./Home'))
const AppRouters: React.FC = () => { const AppRouters: React.FC = () => {
const dispatch = useDispatch<Dispatch>() const dispatch = useDispatch<Dispatch>()
@ -37,7 +39,8 @@ const AppRouters: React.FC = () => {
}, [dispatch]) }, [dispatch])
let element = useRoutes([ let element = useRoutes([
{ path: '/', element: <LoginPage /> }, // { path: '/', element: <LoginPage /> },
{ path: '/', element: <TestLine /> },
{ path: '/home', element: <HomePage /> }, { path: '/home', element: <HomePage /> },
{ {
path: '/mgr', path: '/mgr',

View File

@ -1,21 +1,176 @@
import moment from "moment"; import { Button, Form, Input, InputNumber, Popconfirm, Table } from 'antd';
export default function data(type) { import React, { useContext, useEffect, useRef, useState } from 'react';
const now = moment(); // 获取当前时间 const EditableContext = React.createContext(null);
const startOfDay = now.clone().startOf('day'); // 获取今天的凌晨 const EditableRow = ({ index, ...props }) => {
const [form] = Form.useForm();
return (
<Form form={form} component={false}>
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
</Form>
);
};
const EditableCell = ({
title,
editable,
children,
dataIndex,
record,
handleSave,
...restProps
}) => {
const [editing, setEditing] = useState(true);
const inputRef = useRef(null);
const form = useContext(EditableContext);
// useEffect(() => {
// if (editing) {
// inputRef.current?.focus();
// }
// }, [editing]);
const toggleEdit = (title) => {
if (title == '雨量') {
// setEditing(!editing);
if (record[dataIndex]) {
form.setFieldsValue({
[dataIndex]: record[dataIndex],
});
// 初始化数组,第一个元素是今天的凌晨 }
let timeArray = [startOfDay]; };
// 生成接下来的时间点并添加到数组中,间隔为两个小时,且不超过当前时间 }
let nextTime = startOfDay.clone().add(1, 'hours'); const save = async () => {
while (nextTime <= now) { try {
timeArray.push(nextTime.format('MM-DD HH:mm')); const values = await form.validateFields();
nextTime = nextTime.clone().add(1, 'hours'); toggleEdit(title);
handleSave({
...record,
...values,
});
} catch (errInfo) {
console.log('Save failed:', errInfo);
} }
};
let childNode = children;
if (editable) {
childNode = editing ? (
<Form.Item
style={{
margin: 0,
}}
name={dataIndex}
rules={[
{
required: true,
message: `${title}必填`,
},
]}
>
<InputNumber min={0} ref={inputRef} onPressEnter={save} onBlur={save} />
</Form.Item>
) : (
<div
className="editable-cell-value-wrap"
style={{
paddingRight: 24,
}}
>
{children}
</div>
);
}
return <td {...restProps} onClick={() => toggleEdit(title)}>{childNode}</td>;
};
const App = ({ count, tableData, setEditData }) => {
const [dataSource, setDataSource] = useState([]);
const defaultColumns = [
{
title: '预见期',
dataIndex: 'tm',
width: '30%',
},
{
title: '雨量',
dataIndex: 'drp',
width: '30%',
editable: true,
align: 'center'
},
{
title: '水位',
dataIndex: 'rz',
width: '20%',
},
];
const handleAdd = () => {
const newArr = Array(count).fill(0).map((item, i) => ({
key: (i + 1).toString(),
tm: `${i + 1}小时`,
drp: '',
rz: '',
}))
console.log(timeArray); setDataSource([...newArr]);
let result; setEditData([...newArr])
};
const handleSave = (row) => {
const newData = [...dataSource];
const index = newData.findIndex((item) => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
console.log('newData', newData);
setDataSource(newData);
setEditData(newData)
};
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const columns = defaultColumns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: (record) => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave,
}),
};
});
return result useEffect(() => {
} if (count) {
handleAdd(count)
}
}, [count])
useEffect(() => {
if (tableData.length > 0) {
setDataSource(tableData)
setEditData(tableData)
}
}, [tableData])
return (
<div>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
scroll={{ x: 200, y: 'calc( 100vh - 400px )' }}
pagination={false}
/>
</div>
);
};
export default App;

View File

@ -1,226 +1,75 @@
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import moment from 'moment'; import moment, { min } from 'moment';
export default function drpOption(time = '',tabArr) { export default function drpOption(predict=[],history=[],type) {
const LimitWater = type == 'cq' ? 112 :
const h1DrpData = tabArr.find(item => item.tm == '1小时'); type == 'sxs' ? 72.01 :
type == 'wpc' ? 112 : 0;
// 水量 // 水位
let water = [ const minRz = Math.floor(Math.min(...history.map(item => item.waters),LimitWater, ...predict.map(item => item.predict)))
{ time: "12-24 00:00", value: 480 }, const maxRz = Math.ceil(Math.max(...history.map(item => item.waters), LimitWater,...predict.map(item => item.predict)))
{ time: "12-24 01:00", value: 500 },
{ time: "12-24 02:00", value: 450 },
{ time: "12-24 03:00", value: 520 },
{ time: "12-24 04:00", value: 490 },
{ time: "12-24 05:00", value: 550 },
{ time: "12-24 06:00", value: 580 },
{ time: "12-24 07:00", value: 560 },
{ time: "12-24 08:00", value: 530 },
{ time: "12-24 09:00", value: 590 },
{ time: "12-24 10:00", value: 540 },
{ time: "12-24 11:00", value: 600 },
{ time: "12-24 12:00", value: 620 },
{ time: "12-24 13:00", value: 650 },
{ time: "12-24 14:00", value: 680 },
{ time: "12-24 15:00", value: 630 },
];
// 雨量 // 雨量
let rain = [ const mindrp = Math.floor(Math.min(...history.map(item => item.rains)))
{ time: "12-24 00:00", value: 3.8 }, const maxdrp = Math.ceil(Math.max(...history.map(item => item.rains)))
{ time: "12-24 01:00", value: 4 },
{ time: "12-24 02:00", value: 3.5 },
{ time: "12-24 03:00", value: 4.2 },
{ time: "12-24 04:00", value: 3.9 },
{ time: "12-24 05:00", value: 4.5 },
{ time: "12-24 06:00", value: 4.8 },
{ time: "12-24 07:00", value: 4.6 },
{ time: "12-24 08:00", value: 4.3 },
{ time: "12-24 09:00", value: 4.9 },
{ time: "12-24 10:00", value: 4.4 },
{ time: "12-24 11:00", value: 5.0 },
{ time: "12-24 12:00", value: 5.2 },
{ time: "12-24 13:00", value: 5.5 },
{ time: "12-24 14:00", value: 5.8 },
{ time: "12-24 15:00", value: 5.3 },
];
const data = JSON.parse(JSON.stringify(water)); // // 水位
const ydata = JSON.parse(JSON.stringify(rain)); // const minRz = Math.floor(Math.min(...data.map(item => item.water),...data.map(item => item.predict)))
if (time == "1h") { // const maxRz = Math.ceil(Math.max(...data.map(item => item.water),...data.map(item => item.predict)))
data.push({ time: "12-24 16:00", value: 420, predict: true }) // // 雨量
ydata.push({ time: "12-24 16:00", value: h1DrpData?.drp, predict: true }) // const mindrp = Math.floor(Math.min(...data.map(item => item.rain)))
} else if (time == "3h") { // const maxdrp = Math.ceil(Math.max(...data.map(item => item.rain)))
const wData = [ console.log(minRz, maxRz);
{ time: "12-24 16:00", value: 420, predict: true },
{ time: "12-24 17:00", value: 450, predict: true },
{ time: "12-24 18:00", value: 480, predict: true },
]
const rData = [
{ time: "12-24 16:00", value: tabArr.find( item =>item.tm == '1小时')?.drp, predict: true },
{ time: "12-24 17:00", value: tabArr.find(item =>item.tm == '2小时')?.drp, predict: true },
{ time: "12-24 18:00", value: tabArr.find(item =>item.tm == '3小时')?.drp, predict: true },
]
data.push(...wData)
ydata.push(...rData)
} else if (time == "6h") {
const h6WData = [
{ time: "12-24 16:00", value: 420, predict: true },
{ time: "12-24 17:00", value: 450, predict: true },
{ time: "12-24 18:00", value: 480, predict: true },
{ time: "12-24 19:00", value: 510, predict: true },
{ time: "12-24 20:00", value: 540, predict: true },
{ time: "12-24 21:00", value: 480, predict: true },
]
const h6RData = [
{ time: "12-24 16:00", value: tabArr.find( item =>item.tm == '1小时')?.drp, predict: true },
{ time: "12-24 17:00", value: tabArr.find( item =>item.tm == '2小时')?.dr, predict: true },
{ time: "12-24 18:00", value: tabArr.find( item =>item.tm == '3小时')?.dr, predict: true },
{ time: "12-24 19:00", value: tabArr.find( item =>item.tm == '4小时')?.dr, predict: true },
{ time: "12-24 20:00", value: tabArr.find( item =>item.tm == '5小时')?.dr, predict: true },
{ time: "12-24 21:00", value: tabArr.find( item =>item.tm == '6小时')?.dr, predict: true },
]
data.push(...h6WData)
ydata.push(...h6RData)
} else if(time == "12h") {
const h12WData = [
{ time: "12-24 16:00", value: 420, predict: true },
{ time: "12-24 17:00", value: 450, predict: true },
{ time: "12-24 18:00", value: 480, predict: true },
{ time: "12-24 19:00", value: 510, predict: true },
{ time: "12-24 20:00", value: 540, predict: true },
{ time: "12-24 21:00", value: 480, predict: true },
{ time: "12-24 22:00", value: 420, predict: true },
{ time: "12-24 23:00", value: 450, predict: true },
{ time: "12-24 24:00", value: 480, predict: true },
{ time: "12-25 00:00", value: 510, predict: true },
{ time: "12-25 01:00", value: 540, predict: true },
{ time: "12-25 02:00", value: 480, predict: true },
]
const h12RData = [
{ time: "12-24 16:00", value: tabArr.find( item =>item.tm == '1小时')?.drp, predict: true },
{ time: "12-24 17:00", value: tabArr.find( item =>item.tm == '2小时')?.drp, predict: true },
{ time: "12-24 18:00", value: tabArr.find( item =>item.tm == '3小时')?.drp, predict: true },
{ time: "12-24 19:00", value: tabArr.find( item =>item.tm == '4小时')?.drp, predict: true },
{ time: "12-24 20:00", value: tabArr.find( item =>item.tm == '5小时')?.drp, predict: true },
{ time: "12-24 21:00", value: tabArr.find( item =>item.tm == '6小时')?.drp, predict: true },
{ time: "12-24 22:00", value: tabArr.find( item =>item.tm == '7小时')?.drp, predict: true },
{ time: "12-24 23:00", value: tabArr.find( item =>item.tm == '8小时')?.drp, predict: true },
{ time: "12-24 24:00", value: tabArr.find( item =>item.tm == '9小时')?.drp, predict: true },
{ time: "12-25 00:00", value: tabArr.find( item =>item.tm == '10小时')?.drp, predict: true },
{ time: "12-25 01:00", value: tabArr.find( item =>item.tm == '11小时')?.drp, predict: true },
{ time: "12-25 02:00", value: tabArr.find( item =>item.tm == '12小时')?.drp, predict: true },
]
data.push(...h12WData)
ydata.push(...h12RData)
} else if (time == "24h") {
const h24WData = [
{ time: "12-24 16:00", value: 420, predict: true },
{ time: "12-24 17:00", value: 450, predict: true },
{ time: "12-24 18:00", value: 480, predict: true },
{ time: "12-24 19:00", value: 510, predict: true },
{ time: "12-24 20:00", value: 540, predict: true },
{ time: "12-24 21:00", value: 480, predict: true },
{ time: "12-24 22:00", value: 420, predict: true },
{ time: "12-24 23:00", value: 450, predict: true },
{ time: "12-24 24:00", value: 480, predict: true },
{ time: "12-25 00:00", value: 510, predict: true },
{ time: "12-25 01:00", value: 540, predict: true },
{ time: "12-25 02:00", value: 480, predict: true },
{ time: "12-25 03:00", value: 420, predict: true },
{ time: "12-25 04:00", value: 450, predict: true },
{ time: "12-25 05:00", value: 480, predict: true },
{ time: "12-25 06:00", value: 510, predict: true },
{ time: "12-25 07:00", value: 540, predict: true },
{ time: "12-25 08:00", value: 480, predict: true },
{ time: "12-25 09:00", value: 420, predict: true },
{ time: "12-25 10:00", value: 450, predict: true },
{ time: "12-25 11:00", value: 480, predict: true },
{ time: "12-25 12:00", value: 510, predict: true },
{ time: "12-25 13:00", value: 540, predict: true },
{ time: "12-25 14:00", value: 480, predict: true },
]
const h24RData = [
{ time: "12-24 16:00", value: tabArr.find( item =>item.tm == '1小时')?.drp, predict: true },
{ time: "12-24 17:00", value: tabArr.find( item =>item.tm == '2小时')?.drp, predict: true },
{ time: "12-24 18:00", value: tabArr.find( item =>item.tm == '3小时')?.drp, predict: true },
{ time: "12-24 19:00", value: tabArr.find( item =>item.tm == '4小时')?.drp, predict: true },
{ time: "12-24 20:00", value: tabArr.find( item =>item.tm == '5小时')?.drp, predict: true },
{ time: "12-24 21:00", value: tabArr.find( item =>item.tm == '6小时')?.drp, predict: true },
{ time: "12-24 22:00", value: tabArr.find( item =>item.tm == '7小时')?.drp, predict: true },
{ time: "12-24 23:00", value: tabArr.find( item =>item.tm == '8小时')?.drp, predict: true },
{ time: "12-24 24:00", value: tabArr.find( item =>item.tm == '9小时')?.drp, predict: true },
{ time: "12-25 00:00", value: tabArr.find( item =>item.tm == '10小时')?.drp, predict: true },
{ time: "12-25 01:00", value: tabArr.find( item =>item.tm == '11小时')?.drp, predict: true },
{ time: "12-25 02:00", value: tabArr.find( item =>item.tm == '12小时')?.drp, predict: true },
{ time: "12-25 03:00", value: tabArr.find( item =>item.tm == '13小时')?.drp, predict: true },
{ time: "12-25 04:00", value: tabArr.find( item =>item.tm == '14小时')?.drp, predict: true },
{ time: "12-25 05:00", value: tabArr.find( item =>item.tm == '15小时')?.drp, predict: true },
{ time: "12-25 06:00", value: tabArr.find( item =>item.tm == '16小时')?.drp, predict: true },
{ time: "12-25 07:00", value: tabArr.find( item =>item.tm == '17小时')?.drp, predict: true },
{ time: "12-25 08:00", value: tabArr.find( item =>item.tm == '18小时')?.drp, predict: true },
{ time: "12-25 09:00", value: tabArr.find( item =>item.tm == '19小时')?.drp, predict: true },
{ time: "12-25 10:00", value: tabArr.find( item =>item.tm == '20小时')?.drp, predict: true },
{ time: "12-25 11:00", value: tabArr.find( item =>item.tm == '21小时')?.drp, predict: true },
{ time: "12-25 12:00", value: tabArr.find( item =>item.tm == '22小时')?.drp, predict: true },
{ time: "12-25 13:00", value: tabArr.find( item =>item.tm == '23小时')?.drp, predict: true },
{ time: "12-25 14:00", value: tabArr.find( item =>item.tm == '24小时')?.drp, predict: true },
]
data.push(...h24WData)
ydata.push(...h24RData)
}
let data1 = []; //历史水量数据
let data2 = []; //预测水量数据
let data3 = []; //历史雨量数据
let data4 = []; //预测雨量数据
data.forEach((item) => {
if (item.predict && time) {
data2.push([item.time, item.value]);
} else {
data1.push([item.time, item.value]);
}
});
ydata.forEach((item) => {
if (item.predict && time) {
data4.push([item.time, item.value]);
} else {
data3.push([item.time, item.value]);
}
});
const splitLineData1 = data1.at(-1);
const splitLineData = data1.at(-1);
const splitLineData2 = data3.at(-1);
// 如果是折线图,此处需要追加实际数据的最后一组数据,如果是柱状图,则不需要。
data2.unshift(splitLineData1);
data4.unshift(splitLineData2);
return { return {
grid: { grid: {
left: 0, left: 0,
top: "22%", top: "20%",
right: "0", right: "3%",
bottom: 0, bottom: "10%",
containLabel: true, containLabel: true,
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
}, },
legend: { legend: {
right: "0", right: "center",
top: "4%", top: "4%",
}, },
toolbox: {
show: true,
top: "15%",
right: "0%",
orient: 'vertical',
feature: {
dataZoom: {
show: true,
title: {
zoom: '区域缩放',
back: '区域缩放还原',
}
},
magicType: {
show: true,
title: {
line: '切换为折线图',
bar: '切换为柱状图',
},
type: ['line', 'bar']
},
restore: {
show: true,
title: '还原'
},
saveAsImage: {
show: true,
name: '日到报率详情',
title: '保存为图片'
}
}
},
xAxis: { xAxis: {
type: "category", type: "category",
data: data.map(item => item.time), // data: history.map(item => item.tm),
axisTick: { axisTick: {
show: false, show: false,
}, },
@ -229,6 +78,8 @@ export default function drpOption(time = '',tabArr) {
{ {
type: "value", type: "value",
name: '水位', name: '水位',
min: minRz,
max: maxRz,
splitLine: { splitLine: {
show: true, show: true,
}, },
@ -244,6 +95,8 @@ export default function drpOption(time = '',tabArr) {
{ {
type: "value", type: "value",
name: '雨量', name: '雨量',
min: mindrp,
max: maxdrp,
splitLine: { splitLine: {
show: true, show: true,
}, },
@ -257,70 +110,60 @@ export default function drpOption(time = '',tabArr) {
}, },
], ],
dataZoom: [{
type: 'inside',
start: (600 / history.length) * 100,
end: 100
}, {
start: 0,
end: 10,
handleIcon: 'M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4v1.3h1.3v-1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '80%',
handleStyle: {
color: '#fff',
shadowBlur: 3,
shadowColor: 'rgba(0, 0, 0, 0.6)',
shadowOffsetX: 2,
shadowOffsetY: 2
}
}],
series: [ series: [
{ {
name: "历史水量", name: "实时雨量",
type: "line", type: "bar",
showSymbol: false,
data: data1,
smooth: true,
},
{
name: "历史雨量",
type: "line",
color: '#F59A23', color: '#F59A23',
yAxisIndex: 1, yAxisIndex: 1,
showSymbol: false, showSymbol: false,
data: data3, data: history.map(item => [item.tm, item.rains]),
smooth: true, smooth: true,
}, },
{ {
name: "预测水量", name: "实时水位",
type: "line",
showSymbol: false,
data: history.map(item => [item.tm, item.waters]),
smooth: true,
},
{
name: "预测水位",
type: "line",
color: '#1fbcd2',
yAxisIndex: 0, yAxisIndex: 0,
type: "line", showSymbol: false,
showSymbol: true, data: predict.map(item => [item.tm, item.predict]),
data: data2,
smooth: true, smooth: true,
itemStyle : { normal: {label : {show: true}}}
}, },
{ {
name: "预测雨量", name: LimitWater != 0 ? "汛限水位" :'',
yAxisIndex: 1,
type: "line", type: "line",
showSymbol: true, yAxisIndex: 0,
data: data4, color:"#85ea2d",
showSymbol: false,
data:LimitWater != 0 ? history.map(item => [item.tm, LimitWater]) : [],
smooth: true, smooth: true,
itemStyle : { normal: {label : {show: true}}}
},
{
type: "line",
data: [],
markLine: {
symbol: "none",
lineStyle: {
type: "solid",
width: 2,
},
data: [
// 下面绿色线
[
// 下面半截绿色的线
{
xAxis: splitLineData[0],
yAxis: 0,
lineStyle: {
color: "rgba(46, 224, 85, 1)",
},
},
{
xAxis: splitLineData[0],
yAxis: splitLineData[1],
},
],
],
},
}, },
], ],
} }
} }

View File

@ -1,281 +1,272 @@
import React, { Fragment, useRef, useMemo, useEffect, useState } from 'react'; import React, { Fragment, useRef, useMemo, useEffect, useState } from 'react';
import { Table, Card, Modal, Form, Input, Button, Row, Col, Typography, message, Tabs, Image, InputNumber } from 'antd'; import { Table, Card, Modal, Form, Spin, Button, Row, Col, Typography, message, Tabs, Image, InputNumber, Descriptions } from 'antd';
import ToolBar from './toolbar' import ToolBar from './toolbar'
import ReactEcharts from 'echarts-for-react'; import ReactEcharts from 'echarts-for-react';
import './index.less' import './index.less'
import drpOption from './drpOption.js' import drpOption from './drpOption.js'
import { httppost2 } from '../../utils/request';
import TestApp from './createData.js'
const TableE = ({count,setEditData,tableData}) => { import { getAllHydroBatches, responseData } from './watersTools'
const EditableCell = ({ import apiurl from '../../service/apiurl';
editing, import moment from 'moment';
dataIndex, export default function TestLine() {
title, const obj = {
inputType, 'hjw': '60906600',
record, 'sxs': '60917600',
index, 'szl': '60918000',
children, "wpc": 'ZH201606',
...restProps 'cq':'61013270'
}) => {
const inputNode = <InputNumber min={0} style={{ textAlign: "center", flex: 1 }} />;
return (
<td {...restProps}>
{editing ? (
<Form.Item
name={dataIndex}
wrapperCol={[24]}
style={{
margin: 0,
}}
>
{inputNode}
</Form.Item>
) : (
children
)}
</td>
);
};
const [editingKey, setEditingKey] = useState('');
const [details, setDetails] = useState([])
const isEditing = (record) => {
return record.key === editingKey;
} }
const [searchVal, setSearchVal] = useState(false)
const [historyData, setHistoryData] = useState([])
const [predictData, setPredictData] = useState([])
const [tableList, setTableList] = useState([])
const [loading, setLoading] = useState(false)
const options = useMemo(() => {
if (searchVal.code) {
return drpOption(predictData, historyData,searchVal.code)
}
}, [predictData, historyData,searchVal])
// const options = useMemo(() => {
// return drpOption(tableList)
// }, [tableList])
// 测试
const columns = [ const columns = [
{ {
title: '预见期', title: '预测时间点',
dataIndex: 'tm', dataIndex: 'tm',
width: '30%', key: 'tm',
width: 175,
align: 'center',
fixed:'left'
}, },
{ {
title: '雨量', title: '实际雨量(mm)',
dataIndex: 'drp', dataIndex: 'rain',
width: '30%', key: 'rain',
editable: true, width: 100,
align:'center' align: 'center'
}, },
{ {
title: '水位', title: '实际水位(m)',
dataIndex: 'rz', dataIndex: 'water',
width: '20%', key: 'water',
width: 100,
align: 'center'
}, },
{ {
title: '操作', key: 'operation', width: "20%", fixed: 'right',align: 'center', title: '预测水位(m)',
render: (_, record) => { dataIndex: 'predict',
const editable = isEditing(record); key: 'predict',
return editable ? ( width: 100,
<span> align: 'center'
<Typography.Link
onClick={() => save(record.key)}
style={{
marginRight: 8,
}}
>
完成
</Typography.Link>
</span>
) : (
<div style={{display:"flex",justifyContent:"center",columnGap:10}}>
<Typography.Link disabled={editingKey !== ''} onClick={() => edit1(record)}>
编辑
</Typography.Link>
</div>
);
},
}, },
] {
const [form1] = Form.useForm() title: '差值(m)',
const edit1 = (record) => { dataIndex: 'cz',
form1.setFieldsValue({ key: 'cz',
drp: '', width: 90,
...record, align: 'center',
}); render: (v, r) => <span>{(r.predict && r.water) ? Math.abs((r.predict - r.water)).toFixed(2) : ''}</span>
setEditingKey(record.key); },
}; ]
const save = async (key) => { const getHistoryData = async (params) => {
setLoading(true)
params.stcd = obj[params.code];
try { try {
const row = await form1.validateFields(); const result = await httppost2(apiurl.test.find, params);
const newData = [...details]; if (result.code == 200) {
const index = newData.findIndex((item) => key === item.key); const responseData = result.data.map(item => ({ ...item, tm: moment(item.tm).format('YYYY-MM-DD HH:00:00') }))
if (index > -1) { if (!responseData.length) {
const item = newData[index]; setHistoryData(responseData)
newData.splice(index, 1, { return
...item, }
...row, setHistoryData(responseData)
}); const allBatches = getAllHydroBatches(responseData);
setDetails(newData); const res = await processPredictions(allBatches, params.code)
setEditData(newData); if (res.length > 0) {
setEditingKey(''); setLoading(false)
} else { setPredictData(res.map(item => ({...item,predict:item.predict.toFixed(2)})))
newData.push(row); const tableData = res.map(item => {
setDetails(newData); const obj = responseData.find(it => it.tm == item.tm)
setEditData(newData); return {
setEditingKey(''); ...item,
predict: item.predict ? item.predict.toFixed(2) : '',
water: obj.waters,
rain:obj.rains
}
})
setTableList(tableData)
}
} }
} catch (errInfo) { } catch (error) {
console.log('Validate Failed:', errInfo); console.log(error);
} }
}; }
/**
useEffect(() => { * 处理预测结果
if (tableData.length > 0) { * @param {Array} batches - 批次数据
setDetails(tableData) * @param {Array} predictions - 预测结果数组
} * @returns {Array} - 返回处理后的预测结果
}, [tableData]) */ const processPredictions = async (batches, name) => {
const results = [];
const mergedColumns = columns.map((col) => { for (const batch of batches) {
if (!col.editable) { const prediction = await httppost2('http://202.96.165.23:10100/api/v1/bot/water_infer', {
return col; rains: batch.rains,
waters: batch.waters,
name
});
results.push({
predict: prediction?.data?.water_predicts[0], // 预测水位
tm: batch.lastTm // 对应的时间点
});
} }
return {
...col,
onCell: (record) => ({
record,
inputType: col.dataIndex === 'age' ? 'number' :
col.dataIndex === 'sex' ? 'select' : "text",
dataIndex: col.dataIndex,
title: col.title,
editing: isEditing(record),
}),
};
});
const handleAddRow = (count) => { return results;
const newArr = Array(count).fill(0).map((item, i) => ({
key: (i + 1).toString(),
tm: `${i + 1}小时`,
drp: '',
rz: '',
}))
form1.setFieldsValue(newArr[0])
setDetails([...newArr]);
setEditData([...newArr]);
setEditingKey(newArr[0].key);
}; };
useEffect(() => { const findMinMaxRain = (arr) => {
if (count) {
handleAddRow(count) if (arr.length === 0) {
return { min: null, max: null }; // 如果数组为空,返回 null
} }
}, [count])
return ( let min = arr[0]; // 初始化最小值为第一个元素
<> let max = arr[0]; // 初始化最大值为第一个元素
<Form form={form1} component={false}>
<Table for (let i = 1; i < arr.length; i++) {
rowKey="index" if (arr[i].diff < min.diff) {
components={{ min = arr[i]; // 更新最小值
body: { }
// row: EditableRow, if (arr[i].diff > max.diff) {
cell: EditableCell, max = arr[i]; // 更新最大值
}, }
}} }
columns={mergedColumns}
dataSource={details} return { min, max };
scroll={{ x: 200,y: 'calc( 100vh - 400px )'}} }
pagination={false} const summaryVal = useMemo(() => {
/> if (tableList.length > 0) {
</Form> const sum = tableList.reduce((total, cur) => total + Math.abs((cur.predict - cur.water)), 0);
</> const newArr = JSON.parse(JSON.stringify(tableList));
) const resultMaxOrMin = findMinMaxRain(newArr.map(item=> ({...item,diff:Math.abs((item.predict - item.water)).toFixed(2)})))
} return {
avergVal: (sum / tableList.length).toFixed(2),
maxVal: resultMaxOrMin?.max,
minVal: resultMaxOrMin?.min,
export default function TestLine() { };
const [form] = Form.useForm()
const [searchVal, setSearchVal] = useState({})
const [editData, setEditData] = useState([])
const [type, setType] = useState({})
const [tmCount, setTmCount] = useState('')
useEffect(() => {
let count;
if (type?.time) {
count = type.time == '1h' ? 1 :
type.time == '3h' ? 3 :
type.time == '6h' ? 6 :
type.time == '12h' ? 12 :
type.time == '24h' ? 24 : '';
setTmCount(count)
} else { } else {
count = 1 return {}
setTmCount('')
} }
}, [type]) },[tableList])
const [tableForm, setTableForm] = useState({}) useEffect(() => {
const options = useMemo(() => { if (searchVal) {
return drpOption(tableForm.time,tableForm.tabArr || []) getHistoryData(searchVal)
}, [tableForm]) }
}, [searchVal])
const [tableData, setTableData] = useState([])
const rowObj = {
"1小时": 420,
"2小时": 450,
"3小时": 480,
"4小时": 510,
"5小时": 540,
"6小时": 480,
"7小时": 420,
"8小时": 450,
"9小时": 480,
"10小时": 510,
"11小时": 540,
"12小时": 480,
"13小时": 420,
"14小时": 450,
"15小时": 480,
"16小时": 510,
"17小时": 540,
"18小时": 480,
"19小时": 420,
"20小时": 450,
"21小时": 480,
"22小时": 510,
"23小时": 540,
"24小时": 480,
}
const save = async () => {
try {
setTableForm({ tabArr: editData, time: type.time })
const newTabl = editData.map(item => ({
...item,
rz: rowObj[item.tm],
}))
setTableData(newTabl)
} catch (errInfo) {
console.log('Validate Failed:', errInfo);
}
};
return ( return (
<> <>
<div className='content-root clearFloat xybm' style={{ paddingBottom: "0" }}> <div className='content-root clearFloat xybm' style={{ paddingBottom: "0" }}>
<div className='lf CrudAdcdTreeTableBox' style={{ width: "100%", overflowY: "auto" }}> <div className='lf' style={{ width: "100%", overflowY: "auto" }}>
<Card className='nonebox'> <Card className='nonebox'>
<ToolBar <ToolBar
setSearchVal={setSearchVal} setSearchVal={setSearchVal}
setType={setType}
save={save}
form1={form}
/> />
</Card> </Card>
<div className="ant-card-body" style={{ padding: "20px 0 0 0", height: 'calc( 100vh - 400px )' }}> {
historyData.length ? !loading ?
<div style={{ display: 'flex', columnGap: 20 }}> <div className="ant-card-body" style={{ padding: "20px 0 0 0"}}>
<div style={{ flex: 1,height: 'calc( 100vh - 400px )' }}>
<ReactEcharts option={options} style={{ width: "100%", height: '100%' }} notMerge={true} /> <div style={{ display: 'flex', }}>
<div style={{ flex: 1, height: 'calc( 100vh - 400px )', padding: 10 }}>
<ReactEcharts option={options} style={{ width: "100%", height: '100%' }} notMerge={true} />
</div>
<div style={{ width: 600, marginRight: 30 }}>
<Table
columns={columns}
dataSource={tableList}
rowKey='tm'
pagination={true}
scroll={{ x: 600, y: 'calc(100vh - 300px)' }}
summary={() => {
return (
<>
<Table.Summary.Row>
<Table.Summary.Cell index={12} align='center' colSpan={1} >差值最大值时间点</Table.Summary.Cell>
<Table.Summary.Cell index={1} align='center' colSpan={2}>{summaryVal?.maxVal?.tm}</Table.Summary.Cell>
<Table.Summary.Cell index={2} align='center' colSpan={1}>差值最大值</Table.Summary.Cell>
<Table.Summary.Cell index={3} align='center' colSpan={1}>{summaryVal?.maxVal?.diff}</Table.Summary.Cell>
</Table.Summary.Row>
{/* <Table.Summary.Row>
<Table.Summary.Cell index={4} align='center' colSpan={1} >差值最小值时间点</Table.Summary.Cell>
<Table.Summary.Cell index={5} align='center' colSpan={2}>{summaryVal?.minVal?.tm}</Table.Summary.Cell>
<Table.Summary.Cell index={6} align='center' colSpan={1}>差值最小值</Table.Summary.Cell>
<Table.Summary.Cell index={7} align='center' colSpan={1}>{summaryVal?.minVal?.diff}</Table.Summary.Cell>
</Table.Summary.Row> */}
<Table.Summary.Row>
<Table.Summary.Cell index={4} align='center' colSpan={1} >差值平均值</Table.Summary.Cell>
<Table.Summary.Cell index={5} align='center' colSpan={4}>{summaryVal?.avergVal}</Table.Summary.Cell>
</Table.Summary.Row>
</>
)
}}
/>
</div>
</div>
</div> : <div style={{ width: '100%', height: 'calc(100vh - 60px)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Spin size="large" /></div> :
<div style={{ width: '100%', height: 'calc(100vh - 60px)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}> <img alt='' src={`${process.env.PUBLIC_URL}/assets/noData.png`} /></div>}
{/* {!loading?
<div className="ant-card-body" style={{ padding: "20px 0 0 0", height: 'calc( 100vh - 400px )' }}>
<div style={{ display: 'flex', }}>
<div style={{ flex: 1, height: 'calc( 100vh - 400px )', padding: 10 }}>
<ReactEcharts option={options} style={{ width: "100%", height: '100%' }} notMerge={true} />
</div>
<div style={{ width: 600, marginRight: 30 }}>
<Table
columns={columns}
dataSource={tableList}
rowKey='tm'
pagination={true}
scroll={{x:600,y:'calc(100vh - 300px)'}}
summary={() => {
return (
<>
<Table.Summary.Row>
<Table.Summary.Cell index={12} align='center'colSpan={1} >差值最大值时间点</Table.Summary.Cell>
<Table.Summary.Cell index={1} align='center' colSpan={2}>{summaryVal?.maxVal?.tm}</Table.Summary.Cell>
<Table.Summary.Cell index={2} align='center' colSpan={1}>差值最大值</Table.Summary.Cell>
<Table.Summary.Cell index={3} align='center' colSpan={1}>{summaryVal?.maxVal?.diff}</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell index={4} align='center'colSpan={1} >差值最小值时间点</Table.Summary.Cell>
<Table.Summary.Cell index={5} align='center' colSpan={2}>{summaryVal?.minVal?.tm}</Table.Summary.Cell>
<Table.Summary.Cell index={6} align='center' colSpan={1}>差值最小值</Table.Summary.Cell>
<Table.Summary.Cell index={7} align='center' colSpan={1}>{summaryVal?.minVal?.diff}</Table.Summary.Cell>
</Table.Summary.Row>
<Table.Summary.Row>
<Table.Summary.Cell index={4} align='center'colSpan={1} >差值平均值</Table.Summary.Cell>
<Table.Summary.Cell index={5} align='center' colSpan={4}>{summaryVal?.avergVal}</Table.Summary.Cell>
</Table.Summary.Row>
</>
)
}}
/>
</div>
</div> </div>
<div style={{ width: 380 }}> </div> : <div style={{ width: '100%', height: 'calc(100vh - 60px)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Spin size="large" /></div>
<TableE count={tmCount} setEditData={setEditData} tableData={tableData} /> } */}
</div>
</div>
</div>
</div> </div>
</div> </div>
</> </>

View File

@ -1,3 +1,7 @@
.line-box{ .line-box{
background-color: #fff; background-color: #fff;
} }
.ant-table-summary {
background-color: #f2f9fd !important;
}

View File

@ -1,15 +1,33 @@
import React, { useEffect,useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Form, Input, Button, DatePicker, InputNumber } from 'antd'; import { Form, Input, Button, DatePicker, InputNumber, message } from 'antd';
import NormalSelect from '../../components/Form/NormalSelect'; import NormalSelect from '../../components/Form/NormalSelect';
import moment from 'moment'; import moment from 'moment';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const ToolBar = ({ setSearchVal, setType, save, form1 }) => { const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
const types = Array(5).fill(0).map((item,i) => ({ const types = [
label: `${i + 1}#水库`, // {
value: i + 1 // label: "黄家湾水库",
})) // value: "hjw"
// },
{
label: "车桥水库",
value: "cq"
},
{
label: "三星寺水库",
value: "sxs"
},
{
label: "石子岭水库",
value: "szl"
},
{
label: "乌盆冲",
value: "wpc"
},
]
const timeType = [ const timeType = [
{ {
@ -33,52 +51,145 @@ const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
value: "24h" value: "24h"
} }
] ]
const [form] = Form.useForm(); const [form] = Form.useForm();
const onFinish = (values) => { const onFinish = (values) => {
let dateTimeRangeSo; let dateTimeRangeSo;
if (values.tm) { if (values.tm) {
dateTimeRangeSo = { dateTimeRangeSo = {
start: moment(values.tm[0]).format('YYYY-MM-DD HH:mm:ss'), start: moment(values.tm[0]).format('YYYY-MM-DD HH:00:00'),
end: moment(values.tm[1]).format('YYYY-MM-DD HH:mm:ss') end: moment(values.tm[1]).format('YYYY-MM-DD HH:00:00')
} }
} else {
message.error('请选择时间范围')
} }
delete values.tm delete values.tm
// setSearchVal({...values, dateTimeRangeSo}); setSearchVal({...values, stm: dateTimeRangeSo.start,etm: dateTimeRangeSo.end});
save()
} }
const onValuesChange = (val) =>{
const isLastDayOfFebruary = (date) => {
return date.month() === 1 && date.date() === moment(date).endOf('month').date();
};
const disabledDate = (current) => {
if (!current) return false;
const start = moment('2024-01-01');
const end = moment('2025-02-28');
return current.isBefore(start, 'day') || current.isAfter(end, 'day');
}
// // 禁用不符合条件的日期
// const disabledDate = (current) => {
// const dateRange = form.getFieldValue('tm'); // 获取当前日期范围
// const startDate = dateRange?.[0]; // 获取开始日期
// // 限制年份为 2024 年
// if (current.year() !== 2024) {
// return true; // 禁用非 2024 年的日期
// }
// // 如果没有选择开始日期,不禁用任何日期
// if (!startDate) {
// return false;
// }
// // 如果当前日期与开始日期的差值超过 30 天,则禁用
// const diffInDays = Math.abs(current.diff(startDate, 'days'));
// return diffInDays > 30;
// };
// 当用户选择日期时触发
const handleCalendarChange = (dates) => {
if(!dates) return;
const [start, end] = dates;
if (start && end) {
const diffInDays = Math.abs(end.diff(start, 'days'));
if (diffInDays > 30) {
// 如果选择的日期范围超过 30 天,重置结束日期
form.setFieldsValue({
tm: [start, null], // 保留开始日期,清空结束日期
});
}
}
};
const onValuesChange = (val, o) => {
if ('time' in val) { if ('time' in val) {
setSearchVal({time: val.time}) setSearchVal({ time: val.time })
setType({time: val.time})
form1.resetFields() form1.resetFields()
} }
if ('code' in val) {
let dateTimeRangeSo;
if (o.tm) {
dateTimeRangeSo = {
start: moment(o.tm[0]).format('YYYY-MM-DD HH:00:00'),
end: moment(o.tm[1]).format('YYYY-MM-DD HH:00:00')
}
}
delete o.tm
setSearchVal({...o, stm: dateTimeRangeSo.start,etm: dateTimeRangeSo.end});
// setType({ time: "1h" })
// form.setFieldValue('time','1h')
// form1.resetFields()
}
} }
useEffect(() => { useEffect(() => {
const stm = moment('2024-01-01').format('YYYY-MM-DD 00:00:00')
const etm = moment('2024-01-10').format('YYYY-MM-DD 00:00:00')
const params = { const params = {
code: 1, code: "cq",
// time:'1h' time: '1h',
stm,
etm
} }
form.setFieldsValue(params) form.setFieldValue('code', params.code);
form.setFieldValue('tm', [moment('2024-01-01'), moment('2024-01-10')]);
setSearchVal(params) setSearchVal(params)
}, []) }, [])
return ( return (
<> <>
<div style={{display:'flex',justifyContent:'space-between'}}> <div style={{ display: 'flex', justifyContent: 'space-between' }}>
<Form form={form} className='toolbarBox' layout="inline" onFinish={onFinish} onValuesChange={onValuesChange}> <Form form={form} className='toolbarBox' layout="inline" onFinish={onFinish} >
<Form.Item label="水库" name="code"> <Form.Item label="水库" name="code">
<NormalSelect allowClear style={{ width: '150px' }} options={types} /> <NormalSelect allowClear style={{ width: '150px' }} options={types} />
</Form.Item> </Form.Item>
<Form.Item label="预见期" name="time"> {/* <Form.Item label="" name="time">
<NormalSelect allowClear style={{ width: '150px' }} options={timeType} /> <NormalSelect allowClear style={{ width: '150px' }} options={timeType} />
</Form.Item> */}
<Form.Item label="时间" name="tm"
rules={[
{
validator: (_, value) => {
const [start, end] = value || [];
if (!start || !end) {
return Promise.reject(new Error('请选择完整的日期范围'));
}
const diffInDays = Math.abs(end.diff(start, 'days'));
if (diffInDays > 30) {
return Promise.reject(new Error('日期范围不能超过一个月'));
}
return Promise.resolve();
},
},
]}
>
<RangePicker
showTime
disabledDate={disabledDate}
onCalendarChange={handleCalendarChange}
style={{ width: "330px" }}
format="YYYY-MM-DD HH:00:00"
// allowClear={false}
/>
</Form.Item> </Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">生成</Button> <Form.Item>
<Button type="primary" htmlType="submit">查询</Button>
</Form.Item> </Form.Item>
</Form> </Form>
</div> </div>

View File

@ -0,0 +1,34 @@
import moment from "moment";
/**
* 使用滑动窗口获取水文数据批次
* @param {Array} data - 后端返回的数据数组
* @returns {Array} - 返回数组每个元素包含600个点的数据
*/
export const getAllHydroBatches = (data) => {
if (!Array.isArray(data) || data.length < 600) {
return [];
}
const batches = [];
let startIndex = 0;
// 当剩余数据量大于等于600时继续处理
while (startIndex + 600 <= data.length) {
const slicedData = data.slice(startIndex, startIndex + 600);
batches.push({
rains: slicedData.map(item => item.rains),
waters: slicedData.map(item => item.waters),
lastTm: slicedData[599].tm // 记录第600个点的时间
});
// 每次向后移动1个位置
startIndex += 1;
}
return batches;
};
export const responseData = new Array(800).fill(0).map((item,index) => ({
rains: (Math.random() * 1).toFixed(2),
waters: (Math.random() * 100).toFixed(2),
tm:moment().clone().add(index, 'hours').format("YYYY-MM-DD HH:00:00")
}))