feat(): 预测水位优化
parent
2d60b17fbe
commit
475e670b7c
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,13 @@ module.exports = function (app) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//卫星云图,雷达回波需要
|
||||||
|
app.use(
|
||||||
|
'/gunshiApp/dcpj',
|
||||||
|
createProxyMiddleware({
|
||||||
|
target: 'http://local.gunshiiot.com:18083',
|
||||||
|
changeOrigin: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,23 @@
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import moment, { min } from 'moment';
|
import moment, { min } from 'moment';
|
||||||
|
|
||||||
export default function drpOption(predict = [], history = []) {
|
export default function drpOption(predict=[],history=[],type) {
|
||||||
console.log("predict", predict);
|
const LimitWater = type == 'cq' ? 112 :
|
||||||
|
type == 'sxs' ? 72.01 :
|
||||||
|
type == 'wpc' ? 112 : 0;
|
||||||
// 水位
|
// 水位
|
||||||
const minRz = Math.floor(Math.min(...history.map(item => item.waters), ...predict.map(item => item.predict)))
|
const minRz = Math.floor(Math.min(...history.map(item => item.waters),LimitWater, ...predict.map(item => item.predict)))
|
||||||
const maxRz = Math.ceil(Math.max(...history.map(item => item.waters), ...predict.map(item => item.predict)))
|
const maxRz = Math.ceil(Math.max(...history.map(item => item.waters), LimitWater,...predict.map(item => item.predict)))
|
||||||
// 雨量
|
// 雨量
|
||||||
const mindrp = Math.floor(Math.min(...history.map(item => item.rains)))
|
const mindrp = Math.floor(Math.min(...history.map(item => item.rains)))
|
||||||
const maxdrp = Math.ceil(Math.max(...history.map(item => item.rains)))
|
const maxdrp = Math.ceil(Math.max(...history.map(item => item.rains)))
|
||||||
|
|
||||||
|
// // 水位
|
||||||
|
// const minRz = Math.floor(Math.min(...data.map(item => item.water),...data.map(item => item.predict)))
|
||||||
|
// const maxRz = Math.ceil(Math.max(...data.map(item => item.water),...data.map(item => item.predict)))
|
||||||
|
// // 雨量
|
||||||
|
// const mindrp = Math.floor(Math.min(...data.map(item => item.rain)))
|
||||||
|
// const maxdrp = Math.ceil(Math.max(...data.map(item => item.rain)))
|
||||||
console.log(minRz, maxRz);
|
console.log(minRz, maxRz);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -30,7 +38,7 @@ export default function drpOption(predict = [], history = []) {
|
||||||
toolbox: {
|
toolbox: {
|
||||||
show: true,
|
show: true,
|
||||||
top: "15%",
|
top: "15%",
|
||||||
right: "1%",
|
right: "0%",
|
||||||
orient: 'vertical',
|
orient: 'vertical',
|
||||||
feature: {
|
feature: {
|
||||||
dataZoom: {
|
dataZoom: {
|
||||||
|
|
@ -104,7 +112,7 @@ export default function drpOption(predict = [], history = []) {
|
||||||
],
|
],
|
||||||
dataZoom: [{
|
dataZoom: [{
|
||||||
type: 'inside',
|
type: 'inside',
|
||||||
start: 0,
|
start: (600 / history.length) * 100,
|
||||||
end: 100
|
end: 100
|
||||||
}, {
|
}, {
|
||||||
start: 0,
|
start: 0,
|
||||||
|
|
@ -120,22 +128,23 @@ export default function drpOption(predict = [], history = []) {
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
series: [
|
series: [
|
||||||
{
|
|
||||||
name: "实时水位",
|
|
||||||
type: "line",
|
|
||||||
showSymbol: false,
|
|
||||||
data: history.map(item => [item.tm, item.waters]),
|
|
||||||
smooth: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "实时雨量",
|
name: "实时雨量",
|
||||||
type: "line",
|
type: "bar",
|
||||||
color: '#F59A23',
|
color: '#F59A23',
|
||||||
yAxisIndex: 1,
|
yAxisIndex: 1,
|
||||||
showSymbol: false,
|
showSymbol: false,
|
||||||
data: history.map(item => [item.tm, item.rains]),
|
data: history.map(item => [item.tm, item.rains]),
|
||||||
smooth: true,
|
smooth: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "实时水位",
|
||||||
|
type: "line",
|
||||||
|
showSymbol: false,
|
||||||
|
data: history.map(item => [item.tm, item.waters]),
|
||||||
|
smooth: true,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "预测水位",
|
name: "预测水位",
|
||||||
type: "line",
|
type: "line",
|
||||||
|
|
@ -145,53 +154,16 @@ export default function drpOption(predict = [], history = []) {
|
||||||
data: predict.map(item => [item.tm, item.predict]),
|
data: predict.map(item => [item.tm, item.predict]),
|
||||||
smooth: true,
|
smooth: true,
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// name: "预测水量",
|
name: LimitWater != 0 ? "汛限水位" :'',
|
||||||
// yAxisIndex: 0,
|
type: "line",
|
||||||
// type: "line",
|
yAxisIndex: 0,
|
||||||
// showSymbol: true,
|
color:"#85ea2d",
|
||||||
// data:[...data2],
|
showSymbol: false,
|
||||||
// smooth: true,
|
data:LimitWater != 0 ? history.map(item => [item.tm, LimitWater]) : [],
|
||||||
// itemStyle: { normal: { label: { show: true } } }
|
smooth: true,
|
||||||
// },
|
},
|
||||||
// {
|
|
||||||
// name: "预测雨量",
|
|
||||||
// yAxisIndex: 1,
|
|
||||||
// type: "line",
|
|
||||||
// showSymbol: true,
|
|
||||||
// data: data4,
|
|
||||||
// smooth: true,
|
|
||||||
// itemStyle: { normal: { label: { show: true } } }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// type: "line",
|
|
||||||
// data: [],
|
|
||||||
// markLine: {
|
|
||||||
// symbol: "none",
|
|
||||||
// lineStyle: {
|
|
||||||
// type: "solid",
|
|
||||||
// width: 2,
|
|
||||||
// },
|
|
||||||
// data: [
|
|
||||||
// // 下面绿色线
|
|
||||||
// [
|
|
||||||
// // 下面半截绿色的线
|
|
||||||
// {
|
|
||||||
// xAxis: 599,
|
|
||||||
// yAxis: minRz,
|
|
||||||
// lineStyle: {
|
|
||||||
// color: "rgba(46, 224, 85, 1)",
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// xAxis: 599,
|
|
||||||
// yAxis: splitLineData[1],
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
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'
|
||||||
|
|
@ -7,298 +7,104 @@ import drpOption from './drpOption.js'
|
||||||
import { httppost2 } from '../../utils/request';
|
import { httppost2 } from '../../utils/request';
|
||||||
import TestApp from './createData.js'
|
import TestApp from './createData.js'
|
||||||
import { getAllHydroBatches, responseData } from './watersTools'
|
import { getAllHydroBatches, responseData } from './watersTools'
|
||||||
const TableE = ({ count, setEditData, tableData }) => {
|
import apiurl from '../../service/apiurl';
|
||||||
const EditableCell = ({
|
import moment from 'moment';
|
||||||
editing,
|
export default function TestLine() {
|
||||||
dataIndex,
|
const obj = {
|
||||||
title,
|
'hjw': '60906600',
|
||||||
inputType,
|
'sxs': '60917600',
|
||||||
record,
|
'szl': '60918000',
|
||||||
index,
|
"wpc": 'ZH201606',
|
||||||
children,
|
'cq':'61013270'
|
||||||
...restProps
|
|
||||||
}) => {
|
|
||||||
const inputNode = <InputNumber 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 editingKey.includes(record.key);
|
|
||||||
}
|
}
|
||||||
|
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>
|
|
||||||
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '差值(m)',
|
||||||
|
dataIndex: 'cz',
|
||||||
|
key: 'cz',
|
||||||
|
width: 90,
|
||||||
|
align: 'center',
|
||||||
|
render: (v, r) => <span>{(r.predict && r.water) ? Math.abs((r.predict - r.water)).toFixed(2) : ''}</span>
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const [form1] = Form.useForm()
|
|
||||||
const edit1 = (record) => {
|
|
||||||
form1.setFieldsValue({
|
|
||||||
drp: '',
|
|
||||||
...record,
|
|
||||||
});
|
|
||||||
setEditingKey(record.key);
|
|
||||||
};
|
|
||||||
const save = async (key) => {
|
|
||||||
try {
|
|
||||||
const row = await form1.validateFields();
|
|
||||||
const newData = [...details];
|
|
||||||
const index = newData.findIndex((item) => key === item.key);
|
|
||||||
if (index > -1) {
|
|
||||||
const item = newData[index];
|
|
||||||
newData.splice(index, 1, {
|
|
||||||
...item,
|
|
||||||
...row,
|
|
||||||
});
|
|
||||||
setDetails(newData);
|
|
||||||
setEditData(newData);
|
|
||||||
setEditingKey('');
|
|
||||||
} else {
|
|
||||||
newData.push(row);
|
|
||||||
setDetails(newData);
|
|
||||||
setEditData(newData);
|
|
||||||
setEditingKey('');
|
|
||||||
}
|
|
||||||
} catch (errInfo) {
|
|
||||||
console.log('Validate Failed:', errInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (tableData.length > 0) {
|
|
||||||
setDetails(tableData)
|
|
||||||
}
|
|
||||||
}, [tableData])
|
|
||||||
|
|
||||||
|
|
||||||
const mergedColumns = columns.map((col) => {
|
|
||||||
if (!col.editable) {
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...col,
|
|
||||||
onCell: (record) => ({
|
|
||||||
record,
|
|
||||||
dataIndex: col.dataIndex,
|
|
||||||
title: col.title,
|
|
||||||
editing: isEditing(record),
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleAddRow = (count) => {
|
|
||||||
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.map(item => item.key));
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (count) {
|
|
||||||
handleAddRow(count)
|
|
||||||
}
|
|
||||||
}, [count])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Form form={form1} component={false}>
|
|
||||||
<Table
|
|
||||||
rowKey="key"
|
|
||||||
components={{
|
|
||||||
body: {
|
|
||||||
// row: EditableRow,
|
|
||||||
cell: EditableCell,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
columns={mergedColumns}
|
|
||||||
dataSource={details}
|
|
||||||
scroll={{ x: 200, y: 'calc( 100vh - 400px )' }}
|
|
||||||
pagination={false}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default function TestLine() {
|
|
||||||
const [form] = Form.useForm()
|
|
||||||
const [searchVal, setSearchVal] = useState(false)
|
|
||||||
const [editData, setEditData] = useState([])
|
|
||||||
const [type, setType] = useState({})
|
|
||||||
const [tmCount, setTmCount] = useState('')
|
|
||||||
const [tableForm, setTableForm] = useState({})
|
|
||||||
const [tableData, setTableData] = useState([])
|
|
||||||
const [historyData, setHistoryData] = useState(responseData)
|
|
||||||
const [predictData, setPredictData] = useState([])
|
|
||||||
// const options = useMemo(() => {
|
|
||||||
// return drpOption(tableForm.time || '1h', tableForm.tabArr || [])
|
|
||||||
// }, [tableForm])
|
|
||||||
|
|
||||||
const options = useMemo(() => {
|
|
||||||
return drpOption(predictData,historyData)
|
|
||||||
}, [predictData,historyData])
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// if (searchVal) {
|
|
||||||
// getPrejectRain(searchVal.code)
|
|
||||||
// }
|
|
||||||
// }, [searchVal])
|
|
||||||
|
|
||||||
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 {
|
|
||||||
count = 1
|
|
||||||
setTmCount('')
|
|
||||||
}
|
|
||||||
}, [type])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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 () => {
|
|
||||||
// const all = editData.some(item => !item.drp)
|
|
||||||
// if (all) {
|
|
||||||
// message.error("所有雨量不能为空")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
try {
|
|
||||||
const newTabl = editData.map(item => ({
|
|
||||||
...item,
|
|
||||||
rz: rowObj[item.tm],
|
|
||||||
}))
|
|
||||||
setTableForm({ tabArr: newTabl, time: type.time })
|
|
||||||
setTableData(newTabl)
|
|
||||||
|
|
||||||
} catch (errInfo) {
|
|
||||||
console.log('Validate Failed:', errInfo);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// 测试
|
|
||||||
|
|
||||||
const getHistoryData = async (params) => {
|
const getHistoryData = async (params) => {
|
||||||
|
setLoading(true)
|
||||||
|
params.stcd = obj[params.code];
|
||||||
try {
|
try {
|
||||||
|
const result = await httppost2(apiurl.test.find, params);
|
||||||
|
if (result.code == 200) {
|
||||||
|
const responseData = result.data.map(item => ({ ...item, tm: moment(item.tm).format('YYYY-MM-DD HH:00:00') }))
|
||||||
|
if (!responseData.length) {
|
||||||
|
setHistoryData(responseData)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setHistoryData(responseData)
|
||||||
const allBatches = getAllHydroBatches(responseData);
|
const allBatches = getAllHydroBatches(responseData);
|
||||||
const res = await processPredictions(allBatches, params.code)
|
const res = await processPredictions(allBatches, params.code)
|
||||||
if (res.length > 0) {
|
if (res.length > 0) {
|
||||||
setPredictData(res)
|
setLoading(false)
|
||||||
|
setPredictData(res.map(item => ({...item,predict:item.predict.toFixed(2)})))
|
||||||
|
const tableData = res.map(item => {
|
||||||
|
const obj = responseData.find(it => it.tm == item.tm)
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
predict: item.predict ? item.predict.toFixed(2) : '',
|
||||||
|
water: obj.waters,
|
||||||
|
rain:obj.rains
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setTableList(tableData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// try {
|
|
||||||
// const res = await httppost2('/api/hydrology/history');
|
|
||||||
// if (res.code == 200) {
|
|
||||||
// const allBatches = getAllHydroBatches(res.data);
|
|
||||||
// if (allBatches.length) {
|
|
||||||
// processPredictions(allBatches,params.code)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// const allBatches = getAllHydroBatches(responseData);
|
|
||||||
// }
|
|
||||||
// } catch (error) {
|
|
||||||
// console.log(error);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* 处理预测结果
|
* 处理预测结果
|
||||||
|
|
@ -323,6 +129,40 @@ export default function TestLine() {
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const findMinMaxRain = (arr) => {
|
||||||
|
|
||||||
|
if (arr.length === 0) {
|
||||||
|
return { min: null, max: null }; // 如果数组为空,返回 null
|
||||||
|
}
|
||||||
|
|
||||||
|
let min = arr[0]; // 初始化最小值为第一个元素
|
||||||
|
let max = arr[0]; // 初始化最大值为第一个元素
|
||||||
|
|
||||||
|
for (let i = 1; i < arr.length; i++) {
|
||||||
|
if (arr[i].diff < min.diff) {
|
||||||
|
min = arr[i]; // 更新最小值
|
||||||
|
}
|
||||||
|
if (arr[i].diff > max.diff) {
|
||||||
|
max = arr[i]; // 更新最大值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { min, max };
|
||||||
|
}
|
||||||
|
const summaryVal = useMemo(() => {
|
||||||
|
if (tableList.length > 0) {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},[tableList])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (searchVal) {
|
if (searchVal) {
|
||||||
getHistoryData(searchVal)
|
getHistoryData(searchVal)
|
||||||
|
|
@ -333,27 +173,100 @@ export default function TestLine() {
|
||||||
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 className="ant-card-body" style={{ padding: "20px 0 0 0"}}>
|
||||||
|
|
||||||
<div style={{ display: 'flex', columnGap: 20 }}>
|
<div style={{ display: 'flex', }}>
|
||||||
<div style={{ flex: 1, height: 'calc( 100vh - 400px )', padding: 10 }}>
|
<div style={{ flex: 1, height: 'calc( 100vh - 400px )', padding: 10 }}>
|
||||||
<ReactEcharts option={options} style={{ width: "100%", height: '100%' }} notMerge={true} />
|
<ReactEcharts option={options} style={{ width: "100%", height: '100%' }} notMerge={true} />
|
||||||
</div>
|
</div>
|
||||||
{/* <div style={{ width: 430, marginRight: 30 }}>
|
<div style={{ width: 600, marginRight: 30 }}>
|
||||||
<Button type="primary" onClick={save} style={{float:'right',marginBottom:10}}>生成</Button>
|
<Table
|
||||||
<TestApp count={tmCount} tableData={tableData} setEditData={setEditData}/>
|
columns={columns}
|
||||||
</div> */}
|
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>
|
||||||
|
</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: '100%', height: 'calc(100vh - 60px)', display: 'flex', justifyContent: 'center', alignItems: 'center' }}><Spin size="large" /></div>
|
||||||
|
} */}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
.line-box{
|
.line-box{
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
}
|
}
|
||||||
|
.ant-table-summary {
|
||||||
|
background-color: #f2f9fd !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
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';
|
||||||
|
|
@ -7,24 +7,24 @@ const { RangePicker } = DatePicker;
|
||||||
const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
|
const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
|
||||||
|
|
||||||
const types = [
|
const types = [
|
||||||
|
// {
|
||||||
|
// label: "黄家湾水库",
|
||||||
|
// value: "hjw"
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
label: "hjw",
|
label: "车桥水库",
|
||||||
value: "hjw"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "cq",
|
|
||||||
value: "cq"
|
value: "cq"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "sxs",
|
label: "三星寺水库",
|
||||||
value: "sxs"
|
value: "sxs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "szl",
|
label: "石子岭水库",
|
||||||
value: "szl"
|
value: "szl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "wpc",
|
label: "乌盆冲",
|
||||||
value: "wpc"
|
value: "wpc"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
@ -58,65 +58,139 @@ const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
|
||||||
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, start: dateTimeRangeSo.start,end: dateTimeRangeSo.end});
|
setSearchVal({...values, stm: dateTimeRangeSo.start,etm: dateTimeRangeSo.end});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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) => {
|
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) {
|
if ('code' in val) {
|
||||||
setSearchVal(o)
|
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" })
|
// setType({ time: "1h" })
|
||||||
// form.setFieldValue('time','1h')
|
// form.setFieldValue('time','1h')
|
||||||
// form1.resetFields()
|
// form1.resetFields()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const start = moment().subtract(7, 'days').format('YYYY-MM-DD 00:00:00')
|
const stm = moment('2024-01-01').format('YYYY-MM-DD 00:00:00')
|
||||||
const end = moment().format('YYYY-MM-DD 23:59:59')
|
const etm = moment('2024-01-10').format('YYYY-MM-DD 00:00:00')
|
||||||
const params = {
|
const params = {
|
||||||
code: "hjw",
|
code: "cq",
|
||||||
time: '1h',
|
time: '1h',
|
||||||
start,
|
stm,
|
||||||
end
|
etm
|
||||||
}
|
}
|
||||||
form.setFieldValue('name', params.code);
|
form.setFieldValue('code', params.code);
|
||||||
form.setFieldValue('tm', [moment(start), moment(end)]);
|
form.setFieldValue('tm', [moment('2024-01-01'), moment('2024-01-10')]);
|
||||||
setSearchVal(params)
|
setSearchVal(params)
|
||||||
setType({ time: "1h" })
|
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
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> */}
|
||||||
<Form.Item label="时间" name="tm">
|
<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
|
<RangePicker
|
||||||
allowClear
|
|
||||||
showTime
|
showTime
|
||||||
|
disabledDate={disabledDate}
|
||||||
|
onCalendarChange={handleCalendarChange}
|
||||||
style={{ width: "330px" }}
|
style={{ width: "330px" }}
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:00:00"
|
||||||
|
// allowClear={false}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{/* <Form.Item>
|
<Form.Item>
|
||||||
<Button type="primary" htmlType="submit">生成</Button>
|
<Button type="primary" htmlType="submit">查询</Button>
|
||||||
</Form.Item> */}
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ export const getAllHydroBatches = (data) => {
|
||||||
// 每次向后移动1个位置
|
// 每次向后移动1个位置
|
||||||
startIndex += 1;
|
startIndex += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return batches;
|
return batches;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue