Browse Source

feat:洪水预报

master
waibao2 1 month ago
parent
commit
0960514dd2
  1. 371
      src/views/flood/index.vue

371
src/views/flood/index.vue

@ -1,63 +1,61 @@
<template> <template>
<div class="app-container app-container-bg"> <div class="app-container app-container-bg">
<el-card class="first-card" ref='firstCard' shadow="always"> <el-card class="first-card" ref="firstCard" shadow="always">
<el-form :model="queryParams" ref="queryForm" :inline="true" @submit.native.prevent> <el-form :model="queryForm" ref="queryFormRef" :inline="true" @submit.native.prevent>
<el-form-item label="测站选择:"> <el-form-item label="测站选择:">
<el-select v-model="queryForm.station" placeholder="测站选择" style="width: 240px;" @change="changeStation"> <el-select v-model="queryForm.station" placeholder="测站选择" style="width: 240px;" @change="changeStation">
<el-option v-for="item in stationList" :key="item.stnmId" :label="item.name" :value="item.stnmId" /> <el-option v-for="item in stationList" :key="item.stnmId" :label="item.name" :value="item.stnmId" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="起止时段:"> <el-form-item label="起止时段:">
<el-date-picker v-model="queryForm.dateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="yyyy-MM-dd HH:mm:00"> <el-date-picker v-model="queryForm.dateRange" type="datetimerange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD HH:mm:00">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" @click="drawChart">查询</el-button> <el-button type="primary" @click="drawChart">查询</el-button>
</el-form-item> </el-form-item>
<!-- <el-button type="primary" @click="showSetting = true">设置</el-button> -->
</el-form> </el-form>
<el-row class="info-bar"> <el-row class="info-bar">
<el-col :xs="24" :sm="24" :md="10" :lg="10" :xl="10"> <span>起报时间</span><span>{{qbTime}}</span></el-col> <el-col :xs="24" :sm="24" :md="10" :lg="10" :xl="10">
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6"> <span>预报时段</span><span>1小时</span></el-col> <span>起报时间</span><span>{{ qbTime }}</span>
</el-col>
<el-col :xs="24" :sm="24" :md="6" :lg="6" :xl="6">
<span>预报时段</span><span>1小时</span>
</el-col>
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8"> <el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
<el-descriptions class="margin-top" :column="2" border> <el-descriptions class="margin-top" :column="2" border>
<el-descriptions-item> <el-descriptions-item>
<template slot="label"> <template #label>
预测最高值 预测最高值
</template> </template>
{{ predict.maxValue }} {{ predict.maxValue }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template slot="label"> <template #label>
预测最低值 预测最低值
</template> </template>
{{ predict.minValue }} {{ predict.minValue }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template slot="label"> <template #label>
时间 时间
</template> </template>
{{ predict.maxTime }} {{ predict.maxTime }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item> <el-descriptions-item>
<template slot="label"> <template #label>
时间 时间
</template> </template>
{{ predict.minTime }} {{ predict.minTime }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
</el-col> </el-col>
</el-row> </el-row>
</el-card> </el-card>
<splitpanes class="el-card-p card-shadow carder-border mt10 pad10 default-theme container-box"> <splitpanes class="el-card-p card-shadow carder-border mt10 pad10 default-theme container-box">
<pane size="30" min-size="30" max-size="30" style="height: 100%;"> <pane size="30" min-size="30" max-size="30" style="height: 100%;">
<el-table v-table-height :data="tableData" border v-loading="loading"> <el-table v-table-height :data="tableData" border v-loading="loading">
<el-table-column prop="date" label="日期"> <el-table-column prop="date" label="日期"></el-table-column>
</el-table-column>
<el-table-column prop="rain" label="雨量(mm)"> <el-table-column prop="rain" label="雨量(mm)">
<template #default="scope"> <template #default="scope">
<el-input v-if="isEditing" v-model="scope.row.rain" size="small" /> <el-input v-if="isEditing" v-model="scope.row.rain" size="small" />
@ -73,180 +71,231 @@
</el-table> </el-table>
</pane> </pane>
<pane size="70" min-size="70" max-size="70" class="ml10 pad20" v-loading="loading"> <pane size="70" min-size="70" max-size="70" class="ml10 pad20" v-loading="loading">
<div v-table-height id="main" style="width: 100%; "></div> <div v-table-height id="main" style="width: 100%; height: 100%;"></div>
</pane> </pane>
</splitpanes> </splitpanes>
</div> </div>
</template> </template>
<script>
import * as echarts from "echarts";
import request from "@/utils/request";
import {
Splitpanes,
Pane
} from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
<script setup name="Flood">
import { ref, reactive, onMounted, onUnmounted, nextTick } from 'vue'
import * as echarts from "echarts"
import request from "@/utils/request"
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'
import dayjs from 'dayjs' import dayjs from 'dayjs'
export default {
name: 'Flood', //
components: { const loading = ref(false)
Splitpanes, const showSetting = ref(false)
Pane const tableData = ref([])
}, const stationList = ref([])
data() { const isEditing = ref(false)
const endTime = dayjs(new Date()); const qbTime = ref('')
const startTime = endTime.subtract(10, 'hour').format('YYYY-MM-DD HH:mm:00'); const chartInstance = ref(null)
const endTimeFormatted = dayjs(new Date()).format('YYYY-MM-DD HH:mm:00'); const firstCard = ref(null)
return { const queryFormRef = ref(null)
loading: false,
showSetting: false,
tableData: [],
stationList: [],
queryForm: {
station: null,
dateRange: [startTime, endTimeFormatted]
},
isEditing: false,
//
qbTime: '',
// //
predict: { const predict = reactive({
maxValue: '', maxValue: '',
minValue: '', minValue: '',
maxTime: '', maxTime: '',
minTime: '' minTime: ''
})
//
const endTime = dayjs(new Date())
const startTime = endTime.subtract(10, 'hour').format('YYYY-MM-DD HH:mm:00')
const endTimeFormatted = endTime.format('YYYY-MM-DD HH:mm:00')
const queryForm = reactive({
station: null,
dateRange: [startTime, endTimeFormatted]
})
//
const chartData = ref({})
// resize
let resizeHandler = null
//
onMounted(() => {
getStationList()
// resize
resizeHandler = () => {
if (chartInstance.value) {
chartInstance.value.resize()
} }
} }
},
created() {
this.getStationList()
},
mounted() {
},
methods: { //
window.addEventListener('resize', resizeHandler)
})
//
onUnmounted(() => {
//
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler)
}
// echarts
if (chartInstance.value) {
chartInstance.value.dispose()
chartInstance.value = null
}
})
// //
async getStationList() { const getStationList = async () => {
let res = await request({ try {
const res = await request({
url: "/history/data/getStationList", url: "/history/data/getStationList",
method: 'post', method: 'post',
}); })
if (res.code == 0) {
let data = res.data;
this.stationList = data;
this.queryForm.station = this.stationList[0].stnmId;
this.drawChart()
if (res.code == 0) {
stationList.value = res.data
queryForm.station = stationList.value[0].stnmId
drawChart()
} }
}, } catch (error) {
async drawChart() { console.error('获取测站列表失败:', error)
this.loading = true }
let params = { }
startTime: this.queryForm.dateRange[0],
endTime: this.queryForm.dateRange[1], //
stnmId: this.queryForm.station, const drawChart = async () => {
loading.value = true
const params = {
startTime: queryForm.dateRange[0],
endTime: queryForm.dateRange[1],
stnmId: queryForm.station,
stationType: 'A', stationType: 'A',
stcd: '' stcd: ''
} }
let res = await request({
try {
const res = await request({
url: "/history/data/newHistorydata", url: "/history/data/newHistorydata",
method: 'post', method: 'post',
params: params params: params
}); })
if (res.code == 0) { if (res.code == 0) {
this.chartData = res.data; chartData.value = res.data
let rainData = res.data.A.data || []; const rainData = res.data.A?.data || []
let waterData = res.data.C.data || []; const waterData = res.data.C?.data || []
if (waterData && waterData.length > 0) { if (waterData && waterData.length > 0) {
const actualStartTime = dayjs(waterData[0][0]).format('YYYY-MM-DD HH:mm:ss'); const actualStartTime = dayjs(waterData[0][0]).format('YYYY-MM-DD HH:mm:ss')
const actualEndTime = dayjs(waterData[waterData.length - 1][0]).format('YYYY-MM-DD HH:mm:ss'); const actualEndTime = dayjs(waterData[waterData.length - 1][0]).format('YYYY-MM-DD HH:mm:ss')
this.queryForm.dateRange = [actualStartTime, actualEndTime] queryForm.dateRange = [actualStartTime, actualEndTime]
} }
let predictData = res.data.Z.data || [];
// 👇 const predictData = res.data.Z?.data || []
const waterDataForTable = waterData.length > 0 ? waterData.slice(0, -1) : [];
//
const waterDataForTable = waterData.length > 0 ? waterData.slice(0, -1) : []
// + // +
const tableData = waterDataForTable.map(waterItem => { const tableDataValue = waterDataForTable.map(waterItem => {
const [timestamp, water] = waterItem; const [timestamp, water] = waterItem
const rainItem = rainData.find(r => r[0] === timestamp); const rainItem = rainData.find(r => r[0] === timestamp)
const date = dayjs(timestamp).format('YYYY/MM/DD HH:mm:ss'); const date = dayjs(timestamp).format('YYYY/MM/DD HH:mm:ss')
return { return {
date, date,
rain: rainItem ? rainItem[1]?.toString() : '', rain: rainItem ? rainItem[1]?.toString() : '',
water: water?.toString() water: water?.toString()
}; }
}); })
this.tableData = tableData;
this.initEcharts() // 使 waterData predictData tableData.value = tableDataValue
this.loading = false // 使 nextTick DOM
await nextTick()
initEcharts() // 使 waterData predictData
}
} catch (error) {
console.error('获取图表数据失败:', error)
} finally {
loading.value = false
}
} }
},
initEcharts() {
// 1. AV
const rainArr = this.chartData?.A?.data || [];
const waterArr = this.chartData?.C?.data || [];
const predictArr = this.chartData?.Z?.data || [];
// const filteredRain = waterArr //
// let Xdata = [...filteredRain, ...predictArr] const initEcharts = () => {
const filteredRain = waterArr; // AV
const rainArr = chartData.value?.A?.data || []
const waterArr = chartData.value?.C?.data || []
const predictArr = chartData.value?.Z?.data || []
const filteredRain = waterArr
// //
let Xdata; let Xdata
if (waterArr.length > 0 && predictArr.length > 0 && waterArr[waterArr.length - 1][0] === predictArr[0][0]) { if (waterArr.length > 0 && predictArr.length > 0 && waterArr[waterArr.length - 1][0] === predictArr[0][0]) {
Xdata = [...waterArr, ...predictArr.slice(1)]; Xdata = [...waterArr, ...predictArr.slice(1)]
} else { } else {
Xdata = [...waterArr, ...predictArr]; Xdata = [...waterArr, ...predictArr]
} }
// 3. timeDatarainfallData
// timeDatarainfallData
const timeData = Xdata.map(item => { const timeData = Xdata.map(item => {
// //
return dayjs(item[0]).format('MM月DD日 HH:mm'); return dayjs(item[0]).format('MM月DD日 HH:mm')
}); })
const timeDataLength = timeData.length;
const startIndex = Math.max(0, timeDataLength - 12); // 12 const timeDataLength = timeData.length
this.qbTime = dayjs(Xdata[startIndex][0]).format('YYYY-MM-DD HH:mm:ss') const startIndex = Math.max(0, timeDataLength - 12) // 12
// const rainfallData = filteredRain.map(item => item[1]); qbTime.value = dayjs(Xdata[startIndex][0]).format('YYYY-MM-DD HH:mm:ss')
// //
const rainfallData = filteredRain.map(item => { const rainfallData = filteredRain.map(item => {
const waterItem = rainArr.find(w => w[0] === item[0]); const waterItem = rainArr.find(w => w[0] === item[0])
return waterItem ? waterItem[1] : null; return waterItem ? waterItem[1] : null
}); })
// //
const rainData = filteredRain.map(item => { const rainData = filteredRain.map(item => {
const waterItem = waterArr.find(w => w[0] === item[0]); const waterItem = waterArr.find(w => w[0] === item[0])
return waterItem ? waterItem[1] : null; return waterItem ? waterItem[1] : null
}); })
const levelData = rainData const levelData = rainData
// 5.
//
const waterForecast = Xdata.map(item => { const waterForecast = Xdata.map(item => {
const waterItem = predictArr.find(w => w[0] === item[0]); const waterItem = predictArr.find(w => w[0] === item[0])
return waterItem ? waterItem[1] : null; return waterItem ? waterItem[1] : null
}); })
// //
const validForecast = waterForecast.map((value, index) => ({ value, index })).filter(item => item.value !== null); const validForecast = waterForecast.map((value, index) => ({ value, index })).filter(item => item.value !== null)
if (validForecast.length > 0) { if (validForecast.length > 0) {
const maxItem = validForecast.reduce((max, current) => const maxItem = validForecast.reduce((max, current) =>
(current.value > max.value ? current : max), validForecast[0]); (current.value > max.value ? current : max), validForecast[0])
const minItem = validForecast.reduce((min, current) => const minItem = validForecast.reduce((min, current) =>
(current.value < min.value ? current : min), validForecast[0]); (current.value < min.value ? current : min), validForecast[0])
this.predict.maxValue = maxItem.value; predict.maxValue = maxItem.value
this.predict.minValue = minItem.value; predict.minValue = minItem.value
this.predict.maxTime = timeData[maxItem.index]; predict.maxTime = timeData[maxItem.index]
this.predict.minTime = timeData[minItem.index]; predict.minTime = timeData[minItem.index]
} }
// 15线 // 15线
const splitLineColors = [ const splitLineColors = [
'#000', '#ccc', '#ccc', '#ccc', '#ccc', '#000', '#ccc', '#ccc', '#ccc', '#ccc',
'#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc', '#ccc',
'#ccc', '#ccc', '#ccc', '#ccc', '#000' '#ccc', '#ccc', '#ccc', '#ccc', '#000'
]; ]
let ycColor = '#1EA1CE'
const ycColor = '#1EA1CE'
function getSplitLine(index) { function getSplitLine(index) {
return { return {
show: true, show: true,
@ -256,12 +305,14 @@
} }
} }
} }
// y // y
const yAxisRainMin = Math.min(...rainfallData.filter(v => v !== undefined && v !== null)); const yAxisRainMin = Math.min(...rainfallData.filter(v => v !== undefined && v !== null))
const yAxisRainMax = Math.max(...rainfallData.filter(v => v !== undefined && v !== null)); const yAxisRainMax = Math.max(...rainfallData.filter(v => v !== undefined && v !== null))
const allWaterData = levelData.concat(waterForecast).filter(v => v !== undefined && v !== null); const allWaterData = levelData.concat(waterForecast).filter(v => v !== undefined && v !== null)
const yAxisWaterMin = allWaterData.length ? Math.min(...allWaterData) : 0; const yAxisWaterMin = allWaterData.length ? Math.min(...allWaterData) : 0
const yAxisWaterMax = allWaterData.length ? Math.max(...allWaterData) : 10; let that = this const yAxisWaterMax = allWaterData.length ? Math.max(...allWaterData) : 10
const option = { const option = {
backgroundColor: '#fff', backgroundColor: '#fff',
tooltip: { tooltip: {
@ -269,18 +320,18 @@
axisPointer: { type: 'cross' }, axisPointer: { type: 'cross' },
// tooltip.formatter // tooltip.formatter
formatter: function (params) { formatter: function (params) {
let dataIndex = params[0].dataIndex; let dataIndex = params[0].dataIndex
let html = `<div><strong>${timeData[dataIndex]}</strong></div>`; let html = `<div><strong>${timeData[dataIndex]}</strong></div>`
// //
const isForecast = dataIndex >= filteredRain.length; const isForecast = dataIndex >= filteredRain.length
// //
if (!isForecast || rainfallData[dataIndex] !== undefined) { if (!isForecast || rainfallData[dataIndex] !== undefined) {
html += `<div style="margin-top:4px;"> html += `<div style="margin-top:4px;">
<span style="display:inline-block;width:10px;height:10px;background:#1075FD;margin-right:4px;"></span> <span style="display:inline-block;width:10px;height:10px;background:#1075FD;margin-right:4px;"></span>
雨量: ${rainfallData[dataIndex] ?? '-'} mm 雨量: ${rainfallData[dataIndex] ?? '-'} mm
</div>`; </div>`
} }
// //
@ -288,7 +339,7 @@
html += `<div> html += `<div>
<span style="display:inline-block;width:10px;height:10px;background:#EE6666;margin-right:4px;"></span> <span style="display:inline-block;width:10px;height:10px;background:#EE6666;margin-right:4px;"></span>
水位: ${levelData[dataIndex] ?? '-'} m 水位: ${levelData[dataIndex] ?? '-'} m
</div>`; </div>`
} }
// //
@ -296,10 +347,10 @@
html += `<div> html += `<div>
<span style="display:inline-block;width:10px;height:10px;background:#6f46fd;margin-right:4px;"></span> <span style="display:inline-block;width:10px;height:10px;background:#6f46fd;margin-right:4px;"></span>
预测水位: ${waterForecast[dataIndex] ?? '-'} m 预测水位: ${waterForecast[dataIndex] ?? '-'} m
</div>`; </div>`
} }
return html; return html
} }
}, },
legend: { legend: {
@ -326,8 +377,8 @@
axisLabel: { axisLabel: {
rotate: 0, rotate: 0,
formatter: function (value) { formatter: function (value) {
const [date, time] = value.split(' '); const [date, time] = value.split(' ')
return `${time}\n${date}`; return `${time}\n${date}`
} }
} }
}, },
@ -434,7 +485,6 @@
} }
] ]
} }
}, },
{ {
name: '预测水位', name: '预测水位',
@ -462,29 +512,28 @@
} }
} }
] ]
}; }
const chartDom = document.getElementById('main')
if (!chartDom) return
const chartDom = document.getElementById('main'); //
if (!chartDom) return; if (chartInstance.value) {
if (this.chartInstance) { chartInstance.value.dispose()
this.chartInstance.dispose();
} }
this.chartInstance = echarts.init(chartDom);
this.chartInstance.setOption(option);
// // echarts
window.addEventListener('resize', () => { chartInstance.value = echarts.init(chartDom)
if (this.chartInstance) { chartInstance.value.setOption(option)
this.chartInstance.resize();
} }
});
},
// //
changeStation(val) { const changeStation = (val) => {
this.queryForm.station = val queryForm.station = val
this.drawChart() drawChart()
},
}
} }
</script> </script>
<style scoped>
/* 如果需要添加样式可以在这里添加 */
</style>
Loading…
Cancel
Save