feat(): ai洪水预报修改

test1
李神峰 2025-04-01 16:50:36 +08:00
parent 9b5187625b
commit 0ac52e4e9e
4 changed files with 214 additions and 34 deletions

View File

@ -29,7 +29,8 @@ module.exports = function (app) {
app.use( app.use(
'/gunshiApp/dcpj', '/gunshiApp/dcpj',
createProxyMiddleware({ createProxyMiddleware({
target: 'http://local.gunshiiot.com:18083', // target: 'http://local.gunshiiot.com:18083',
target: 'http://192.168.66.30:24107',
changeOrigin: true, changeOrigin: true,
}) })
); );

View File

@ -0,0 +1,7 @@
import React from 'react'
export default function RenderForm() {
return (
<div>RenderForm</div>
)
}

View File

@ -48,18 +48,34 @@ export default function TestLine() {
align: 'center', align: 'center',
fixed:'left' fixed:'left'
}, },
{
title: '预测降雨量(mm)',
dataIndex: 'predictRainfall',
key: 'predictRainfall',
align: 'center',
width: 120,
// render: (text, record,index) => (
// <InputNumber
// defaultValue={text}
// min={0}
// precision={1}
// onChange={(value) => handlePredictRainfallChange(value, index)}
// style={{ width: '100%' }}
// />
// )
},
{ {
title: '实测雨量(mm)', title: '实测雨量(mm)',
dataIndex: 'rain', dataIndex: 'rains',
key: 'rain', key: 'rains',
width: 100, width: 120,
align: 'center', align: 'center',
render: (v) => <span>{ handleEmptyValue(v)}</span> render: (v) => <span>{ handleEmptyValue(v)}</span>
}, },
{ {
title: '实测水位(m)', title: '实测水位(m)',
dataIndex: 'water', dataIndex: 'waters',
key: 'water', key: 'waters',
width: 100, width: 100,
align: 'center', align: 'center',
render: (v) => <span>{ handleEmptyValue(v)}</span> render: (v) => <span>{ handleEmptyValue(v)}</span>
@ -78,12 +94,90 @@ export default function TestLine() {
key: 'cz', key: 'cz',
width: 90, width: 90,
align: 'center', align: 'center',
render: (v, r) => <span>{(r.predict && r.water) ? Math.abs((r.predict - r.water)).toFixed(2) : '-'}</span> render: (v, r) => <span>{(r.predict && r.waters) ? Math.abs((r.predict - r.waters)).toFixed(2) : '-'}</span>
}, },
] ]
/**
* 替换数组末尾指定数量的元素
* @param {Array} arr - 原数组
* @param {Array} newValues - 新值数组
* @param {number} tn - 替换数量
* @returns {Array} 新数组
*/
const replaceLastItems = (arr, newValues, tn = 1) => {
if (!Array.isArray(arr)) {
return newValues;
}
const newArr = [...arr];
const startIndex = Math.max(0, newArr.length - tn);
for (let i = 0; i < tn; i++) {
if (startIndex + i < newArr.length) {
newArr[startIndex + i] = newValues[i];
}
}
return newArr;
};
/**
* 收集预测雨量数据
* @param {Array} data - 表格数据
* @param {number} tn - 预测时段数
* @returns {Array} 预测雨量数组
*/
const collectPredictRainfall = (data, tn = 1) => {
if (!Array.isArray(data) || data.length === 0) return [];
const result = [];
data.slice(-tn).forEach(item => {
result.push((item.predictRainfall === '' || item.predictRainfall==null || item.predictRainfall == undefined) ? item.rains : item.predictRainfall);
});
return result;
};
/**
* 处理预测雨量的变化
* @param {Array} data - 原始数据
* @param {Object} changes - 变化的数据 {index: value}
* @returns {Array} 处理后的数据
*/
const handlePredictRainfallChanges =(data, value, index) => {
if (!Array.isArray(data)) return [];
const newData = [...data];
if (newData[index]) {
newData[index] = {
...newData[index],
predictRainfall: value,
isModified: true // 标记该项已被修改
};
}
return newData;
};
const [predictRainfalling, setPredictRainfalling] = useState({});
const [tableUpdata, setTableUpdata] = useState([])
const tableUpdataRef = useRef(null);
tableUpdataRef.current = tableUpdata;
const handleRainfallChange = (value, index) => {
setPredictRainfalling(prev => ({
...prev,
[index]: value
}));
const updatedData = handlePredictRainfallChanges(tableList, value, index);
setTableList(updatedData)
setTableUpdata(updatedData)
tableUpdataRef.current = updatedData
};
const getHistoryData = async (params) => { const getHistoryData = async (params) => {
setLoading(true)
params.stcd = obj[params.code]; params.stcd = obj[params.code];
params.predictRainfallList = tableUpdataRef.current;
if (tableUpdataRef.current.length && tableUpdataRef.current.some(item => item.predictRainfall === '' || item.predictRainfall == null || item.predictRainfall == undefined)) {
message.warning(`请输入${searchVal.time}个预测雨量值`)
return
}
setLoading(true)
try { try {
const result = await httppost2(apiurl.test.find1, params); const result = await httppost2(apiurl.test.find1, params);
if (result.code == 200) { if (result.code == 200) {
@ -94,20 +188,26 @@ export default function TestLine() {
} }
setHistoryData(responseData) setHistoryData(responseData)
const allBatches = getAllHydroBatches(responseData); const allBatches = getAllHydroBatches(responseData);
const res = await processPredictions(allBatches, params.code) let needData = responseData;
let type = tableUpdataRef.current.length > 0 ? 2 :1
if (tableUpdataRef.current.length > 0) {
needData = tableUpdataRef.current;
}
// debugger
const res = await processPredictions(allBatches, params.code, needData, type)
if (res.length > 0) { if (res.length > 0) {
setLoading(false) setLoading(false)
setPredictData(res.map(item => ({ ...item, predict: item.predict.toFixed(2) }))) setPredictData(res.map(item => ({ ...item, predict: item.predict.toFixed(2) })))
const tableData = res.map(item => { const tableData = res.map(item => {
const obj = responseData.find(it => it.tm == item.tm) const obj = needData.find(it => it.tm == item.tm)
return { return {
...item, ...item,
predict: item.predict ? item.predict.toFixed(2) : '', predict: item.predict ? item.predict.toFixed(2) : '',
water: obj?.waters, waters: obj?.waters ,
rain:obj?.rains rains: obj?.rains,
predictRainfall:type == 2 ? obj?.predictRainfall:''
} }
}) })
setTableList(tableData) setTableList(tableData)
} }
} }
@ -129,12 +229,20 @@ export default function TestLine() {
* @param {Array} batches - 批次数据 * @param {Array} batches - 批次数据
* @param {Array} predictions - 预测结果数组 * @param {Array} predictions - 预测结果数组
* @returns {Array} - 返回处理后的预测结果 * @returns {Array} - 返回处理后的预测结果
*/ const processPredictions = async (batches, name) => { */ const processPredictions = async (batches, name, responseArr, type) => {
let arr = [];
if (type == 1) {
arr = JSON.parse(JSON.stringify(responseArr));
arr.slice(-searchVal.time);
} else {
arr=responseArr
}
const predictRainArr = collectPredictRainfall(arr, searchVal.time) //收集的输入预测雨量数组
const results = []; const results = [];
for (const batch of batches) { for (const batch of batches) {
const prediction = await httppost2('http://202.96.165.23:10100/api/v1/bot/water_infer', { const prediction = await httppost2('http://202.96.165.23:10100/api/v1/bot/water_infer', {
rains: replaceLastItem(batch.rains,searchVal.predictRain), // rains: replaceLastItem(batch.rains,searchVal.predictRain),
rains:replaceLastItems(batch.rains,predictRainArr,searchVal.time),
waters: batch.waters, waters: batch.waters,
name, name,
tn:searchVal.time tn:searchVal.time
@ -176,9 +284,9 @@ export default function TestLine() {
} }
const summaryVal = useMemo(() => { const summaryVal = useMemo(() => {
if (tableList.length > 0) { if (tableList.length > 0) {
const sum = tableList.reduce((total, cur) => total + Math.abs((cur.predict - cur.water)), 0); const sum = tableList.reduce((total, cur) => total + Math.abs((cur.predict - cur.waters)), 0);
const newArr = JSON.parse(JSON.stringify(tableList)); const newArr = JSON.parse(JSON.stringify(tableList));
const resultMaxOrMin = findMinMaxRain(newArr.map(item => ({ ...item, diff: Math.abs((item.predict - item.water)).toFixed(2) }))) const resultMaxOrMin = findMinMaxRain(newArr.map(item => ({ ...item, diff: Math.abs((item.predict - item.waters)).toFixed(2) })))
return { return {
avergVal: (sum / tableList.length).toFixed(2), avergVal: (sum / tableList.length).toFixed(2),
maxVal: resultMaxOrMin?.max, maxVal: resultMaxOrMin?.max,
@ -187,7 +295,29 @@ export default function TestLine() {
} else { } else {
return {} return {}
} }
},[tableList]) }, [tableList])
useEffect(() => {
if (searchVal.time) {
setTableUpdata([])
tableUpdataRef.current = []
}
}, [searchVal.time])
// useEffect(() => {
// if (searchVal.code) {
// setTableUpdata([])
// tableUpdataRef.current = []
// }
// }, [searchVal.code])
// useEffect(() => {
// if (searchVal.tm) {
// setTableUpdata([])
// tableUpdataRef.current = []
// }
// }, [searchVal.tm])
useEffect(() => { useEffect(() => {
if (searchVal) { if (searchVal) {
getHistoryData(searchVal) getHistoryData(searchVal)
@ -195,6 +325,33 @@ export default function TestLine() {
}, [searchVal]) }, [searchVal])
// 渲染预测雨量输入表单
const renderRainfallInputs = () => {
const inputs = [];
for (let i = 0; i < searchVal.time; i++) {
inputs.push(
<Form.Item
key={i}
label={`${i + 1}h`}
style={{ marginBottom: 16 }}
>
<InputNumber
value={predictRainfalling[i]}
onChange={(value) => handleRainfallChange(value, i)}
min={0}
precision={1}
placeholder={`请输入雨量`}
style={{ width: 100 }}
/>
</Form.Item>
);
}
return inputs;
};
return ( return (
<> <>
<div className='content-root clearFloat xybm' style={{ paddingBottom: "0" }}> <div className='content-root clearFloat xybm' style={{ paddingBottom: "0" }}>
@ -202,8 +359,16 @@ export default function TestLine() {
<Card className='nonebox'> <Card className='nonebox'>
<ToolBar <ToolBar
setSearchVal={setSearchVal} setSearchVal={setSearchVal}
setPredictRainfalling={setPredictRainfalling}
/> />
</Card> </Card>
{
searchVal.time && <div style={{ marginTop: 16,marginLeft:10,marginRight:10 }}>
<Form layout="inline">
{renderRainfallInputs()}
</Form>
</div>
}
{ {
historyData.length ? !loading ? historyData.length ? !loading ?
<div className="ant-card-body" style={{ padding: "20px 0 0 0"}}> <div className="ant-card-body" style={{ padding: "20px 0 0 0"}}>
@ -212,12 +377,12 @@ export default function TestLine() {
<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: 600, marginRight: 30 }}> <div style={{ width: 700, marginRight: 30 }}>
<Table <Table
columns={columns} columns={columns}
dataSource={tableList} dataSource={tableList}
rowKey='tm' rowKey='tm'
pagination={true} pagination={false}
scroll={{ x: 600, y: 'calc(100vh - 300px)' }} scroll={{ x: 600, y: 'calc(100vh - 300px)' }}
summary={() => { summary={() => {
return ( return (
@ -225,7 +390,7 @@ export default function TestLine() {
<Table.Summary.Row> <Table.Summary.Row>
<Table.Summary.Cell index={12} align='center' colSpan={1} >差值最大值时间点</Table.Summary.Cell> <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={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={2} align='center' colSpan={2}>差值最大值</Table.Summary.Cell>
<Table.Summary.Cell index={3} align='center' colSpan={1}>{summaryVal?.maxVal?.diff !='NaN'?summaryVal?.maxVal?.diff:"-"}</Table.Summary.Cell> <Table.Summary.Cell index={3} align='center' colSpan={1}>{summaryVal?.maxVal?.diff !='NaN'?summaryVal?.maxVal?.diff:"-"}</Table.Summary.Cell>
</Table.Summary.Row> </Table.Summary.Row>
{/* <Table.Summary.Row> {/* <Table.Summary.Row>
@ -236,7 +401,7 @@ export default function TestLine() {
</Table.Summary.Row> */} </Table.Summary.Row> */}
<Table.Summary.Row> <Table.Summary.Row>
<Table.Summary.Cell index={4} align='center' colSpan={1} >差值平均值</Table.Summary.Cell> <Table.Summary.Cell index={4} align='center' colSpan={1} >差值平均值</Table.Summary.Cell>
<Table.Summary.Cell index={5} align='center' colSpan={4}>{summaryVal?.avergVal!='NaN'?summaryVal?.avergVal: '-'}</Table.Summary.Cell> <Table.Summary.Cell index={5} align='center' colSpan={6}>{summaryVal?.avergVal!='NaN'?summaryVal?.avergVal: '-'}</Table.Summary.Cell>
</Table.Summary.Row> </Table.Summary.Row>
</> </>

View File

@ -4,7 +4,7 @@ import NormalSelect from '../../components/Form/NormalSelect';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
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,setPredictRainfalling }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const types = [ const types = [
// { // {
@ -72,15 +72,22 @@ const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
return current && (current > maxDate); return current && (current > maxDate);
}; };
const onValuesChange = (val, r) => {
if ('time' in val) {
setSearchVal({ ...r, time: val.time, etm: moment(r.tm).format("YYYY-MM-DD HH:mm:ss") })
setPredictRainfalling({})
}
}
const jump = () => { const jump = () => {
navigate('/'); navigate('/');
} }
useEffect(() => { useEffect(() => {
const stm = moment('2024-01-01').format('YYYY-MM-DD 00:00:00') const stm = moment('2024-01-01').format('YYYY-MM-DD 00:00:00')
const etm = moment('2024-07-28 11:00:00').format('YYYY-MM-DD 00:00:00') const etm = moment('2024-07-28 11:00:00').format('YYYY-MM-DD HH:00:00')
const params = { const params = {
code: "cq", code: "cq",
time: 24, time: 6,
predictRain:11, predictRain:11,
// stm, // stm,
etm etm
@ -97,16 +104,16 @@ const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
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} > <Form form={form} className='toolbarBox' layout="inline" onFinish={onFinish} onValuesChange={onValuesChange}>
<Form.Item label="水库" name="code"> <Form.Item label="水库" name="code">
<NormalSelect allowClear style={{ width: '150px' }} options={types} /> <NormalSelect allowClear={false} 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={false} style={{ width: '150px' }} options={timeType} />
</Form.Item> </Form.Item>
<Form.Item label="预测降雨量" name="predictRain"> {/* <Form.Item label="" name="predictRain">
<InputNumber min={0} style={{ width: '150px' }}/> <InputNumber min={0} style={{ width: '150px' }}/>
</Form.Item> </Form.Item> */}
<Form.Item label="时间" name="tm" <Form.Item label="时间" name="tm"
> >
<DatePicker <DatePicker
@ -114,7 +121,7 @@ const ToolBar = ({ setSearchVal, setType, save, form1 }) => {
disabledDate={disabledFutureDate} disabledDate={disabledFutureDate}
style={{ width: "250px" }} style={{ width: "250px" }}
format="YYYY-MM-DD HH:00:00" format="YYYY-MM-DD HH:00:00"
// allowClear={false} allowClear={false}
/> />
</Form.Item> </Form.Item>