- 需求背景
- 解决效果
- ISQQW代码地址
- index.vue
需求背景
实现水利计算模型-雨量,流量,时间分割线
解决效果
ISQQW代码地址
链接
index.vue
<!--/**
* @author: liuk
* @date: 2024/06/13
* @describe: 洪水预报结果图表
*/-->
<template>
<div ref="chatDom" class="sectionalResultChart"></div>
</template>
<script lang="ts" setup>
import {ref, onMounted, watch, nextTick, defineProps} from "vue"
import * as echarts from 'echarts'
import moment from 'moment'
import {formatToFixed} from "@/utils/dictionary"
// Props
const props = defineProps(['data'])
import {usefloodForecastStore} from "@/store/modules/floodForecast";
const floodForecastStore = usefloodForecastStore()
let myChart = null // Vue3 使用 proxy 对象代理,而 echarts 则使用了大量的全等(===), 对比失败从而导致了bug。
const chatDom = ref(null)
const isWrank = ref(false) // 最大值是否超出1w
watch(() => props.data, (data) => {
const temp = data.map(item => {
const {historyFlow, hdlflow, ddrmflow, xajflow, siflow, lstmflow} = item
return Object.values({historyFlow, hdlflow, ddrmflow, xajflow, siflow, lstmflow})
}).flat()
const maxValue = Math.max.apply(null, temp)
isWrank.value = maxValue >= 1e4
nextTick(() => {
drawChart()
myChart.showLoading({
text: '加载中...', // 加载提示文本
color: '#fff', // 加载动画颜色
textColor: '#fff', // 文字颜色
maskColor: 'rgba(41, 12, 12, .5)', // 遮罩颜色
zlevel: 0 // z轴层级
})
if (data.length) {
myChart.hideLoading();
const option = myChart.getOption()
myChart.clear()
myChart.setOption(renderFn(option, data))
}
})
}, {immediate: true})
const renderFn = (option, data) => {
const curForecastTime = floodForecastStore.curSelectObj.forecastTime || ''
const len = data.length // 数据总长度
let curDivisionHourNum = Math.max(data.findIndex(item => moment(item.time).valueOf() === moment(curForecastTime).valueOf()), 0) // 分割线位置
debugger
option.legend[0].data = ['面雨量', '实测流量', '混合深度学习', 'DDRM', '新安江', '时空异构', 'LSTM']
option.xAxis[0].data = data.map(item => item.time)
option.yAxis[0].name = isWrank.value ? '流量 万m³/s' : '流量 m³/s'
option.yAxis[1].max = Math.max.apply(null,data.map(item => formatToFixed(item.rainfallValue) || 0)) * 3
option.series[0].data = data.map(item => formatToFixed(item.rainfallValue) || 0)//面雨量
option.series[1].data = data.map(item => formatToFixed(item.historyFlow) || 0)//实测流量
option.series[2].data = data.slice(0, curDivisionHourNum).fill('-').concat(data.slice(curDivisionHourNum).map(item => formatToFixed(item.hdlflow) || 0))//混合深度学习
option.series[3].data = data.slice(0, curDivisionHourNum).fill('-').concat(data.slice(curDivisionHourNum).map(item => formatToFixed(item.ddrmflow) || 0))//DDRM
option.series[4].data = data.slice(0, curDivisionHourNum).fill('-').concat(data.slice(curDivisionHourNum).map(item => formatToFixed(item.xajflow) || 0))//新安江
option.series[5].data = data.slice(0, curDivisionHourNum).fill('-').concat(data.slice(curDivisionHourNum).map(item => formatToFixed(item.siflow) || 0))//时空异构
option.series[6].data = data.slice(0, curDivisionHourNum).fill('-').concat(data.slice(curDivisionHourNum).map(item => formatToFixed(item.lstmflow) || 0))//LSTM
// ------- 虚线
option.series[7].data = data.slice(0, curDivisionHourNum + 1).map(item => formatToFixed(item.hdlflow) || 0)//混合深度学习
option.series[8].data = data.slice(0, curDivisionHourNum + 1).map(item => formatToFixed(item.ddrmflow) || 0)//DDRM
option.series[9].data = data.slice(0, curDivisionHourNum + 1).map(item => formatToFixed(item.xajflow) || 0)//新安江
option.series[10].data = data.slice(0, curDivisionHourNum + 1).map(item => formatToFixed(item.siflow) || 0)//时空异构
option.series[11].data = data.slice(0, curDivisionHourNum + 1).map(item => formatToFixed(item.lstmflow) || 0)//LSTM
// ----- 定位分割线
option.series[12].data = new Array(len).fill(0.1)
option.series[12].data[curDivisionHourNum] = 0
const curHeight = parseInt(window.getComputedStyle(chatDom.value).height)
switch (true) {
case curHeight >= 600:
option.series[12].markPoint.symbolSize = [1.5, 515]
option.series[12].markPoint.symbolOffset = [0, -257]
break
case curHeight > 460:
option.series[12].markPoint.symbolSize = [1.5, 385]
option.series[12].markPoint.symbolOffset = [0, -192]
break
default:
option.series[12].markPoint.symbolSize = [1.5, 200]
option.series[12].markPoint.symbolOffset = [0, -100]
}
return option
}
onMounted(() => {
window.addEventListener('resize', () => {
drawChart()
const option = myChart.getOption()
myChart.clear()
myChart.setOption(renderFn(option, props.data,))
}, {passive: true});
})
const drawChart = () => {
let chartDom = chatDom.value
if (chartDom == null) {
return
}
echarts.dispose(chartDom)
myChart = echarts.init(chartDom)
const option = {
color: ['rgba(46, 165, 255, 1)', 'rgba(0, 0, 255, 1)', 'rgba(0, 207, 19, 1)', 'rgba(254, 254, 62, 1)', 'rgba(244, 106, 87, 1)', 'rgba(255, 162, 0, 1)', 'rgba(247, 0, 237, 1)'],
tooltip: {
trigger: 'axis',
padding: [0, 10, 10, 10],
axisPointer: {
type: 'shadow',
label: {
show: true,
},
},
formatter: function (param) {
let data = param.filter(item => item.data !== '-' && item.seriesName !== "定位分割线")
data = data.filter((item, i) => data.findIndex(x => x.seriesName === item.seriesName) === i)
return `
<div class="sectionalResultChart-popup">
<p class="top">
<span>${param[0]?.axisValue} </span>
</p>
${
data.map(item => {
let unit
switch (true) {
case ['面雨量'].includes(item.seriesName):
unit = 'mm';
break
default:
unit = 'm³/s'
break
}
return `
<p class="item">
<i class="icon" style="background-color:${item.color}"></i>
<span class="name">${item.seriesName}</span>
<span class="value"><b>${item.data || 0}</b>${item.data !== '--' ? unit : ''}</span>
</p>`
}).join("")
}
</div>
`
}
},
dataZoom: [
{
type: 'inside'
}
],
grid: {
left: 45,
top: 40,
right: 40,
bottom: 40,
splitLine: {
show: true,
lineStyle: {
color: 'rgba(0, 0, 0, 0.1)',
type: 'dashed'
}
}
},
legend: {
data: [],
top: 5,
icon: 'circle',
textStyle: {
color: '#fff'
},
},
xAxis: {
type: 'category',
data: ['2021/06/25', '2021/06/25', '2021/06/25', '2021/06/25', '2021/06/25', '2021/06/25', '2021/06/25',],
alignTicks: true,
axisLine: {
lineStyle: {
color: '#ccc'
}
},
splitLine: {
show: false,
lineStyle: {
width: 1,
color: '#eee',
},
},
axisTick: {
alignWithLabel: true,
},
axisLabel: {
margin: 10,
textStyle: {
color: '#eee',
fontSize: 14,
},
formatter: (val) => moment(val).format('HH:mm')
},
},
yAxis: [
{
type: 'value',
name: '流量 m³/s',
axisTick: {
show: false,
},
axisLine: {
show: false,
},
nameTextStyle: {
color: '#fff'
},
splitLine: {
show: false
},
axisLabel: {
color: '#fff',
formatter: (val) => isWrank.value ? formatToFixed(val / 1e4, 1) : val
},
},
{
type: 'value',
name: '面雨量 mm',
nameLocation: 'start',
inverse: true,
nameTextStyle: {
color: '#fff'
},
splitLine: {
show: false
},
axisLabel: {
color: '#fff',
},
}
],
series: [
{
name: '面雨量',
type: 'bar',
data: [],
yAxisIndex: 1,
itemStyle: {
borderRadius: [0, 0, 5, 5]
},
},
{
name: '实测流量',
smooth: true,
type: 'line',
showSymbol: false,
barWidth: '30%',
data: [],
yAxisIndex: 0,
},
{
name: '混合深度学习',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
yAxisIndex: 0,
},
{
name: 'DDRM',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
yAxisIndex: 0,
},
{
name: '新安江',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
yAxisIndex: 0,
},
{
name: '时空异构',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
yAxisIndex: 0,
},
{
name: 'LSTM',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
yAxisIndex: 0,
},
// 虚线 -------------
{
name: '混合深度学习',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
lineStyle: {
type: 'dashed'
},
yAxisIndex: 0,
},
{
name: 'DDRM',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
lineStyle: {
type: 'dashed'
},
yAxisIndex: 0,
},
{
name: '新安江',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
lineStyle: {
type: 'dashed'
},
yAxisIndex: 0,
},
{
name: '时空异构',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
lineStyle: {
type: 'dashed'
},
yAxisIndex: 0,
},
{
name: 'LSTM',
type: 'line',
smooth: true,
showSymbol: false,
barWidth: '30%',
data: [],
lineStyle: {
type: 'dashed'
},
yAxisIndex: 0,
markLine: {
silent: true,
lineStyle: {
type: 'solid',
color: 'red',
},
data: 2
},
},
//定位分割线 ---------------
{
name: '定位分割线',
smooth: true,
type: 'line',
showSymbol: false,
barWidth: '30%',
color: 'transparent',
data: ['0.1', '0.1', 0.1, 0.1, 0.1, 0.1, 0.1, 0, 0.1],
yAxisIndex: 0,
markPoint: {
symbol: 'rect',
symbolSize: [1.5, 600],
symbolOffset: [0, -215],
data: [
{
type: 'min',
itemStyle: {
color: 'red'
},
label: {
show: true,
color: 'red',
position: 'top',
formatter: '预报作业时间',
fontSize: 12,
},
emphasis: {
disabled: true
}
},
],
},
},
],
};
option && myChart.setOption(option)
}
</script>
<style lang="scss" scoped>
.sectionalResultChart {
width: 100%;
height: 100%;
max-height: 600px;
}
</style>
<style lang="scss">
.sectionalResultChart-popup {
overflow: hidden;
//margin: 3px 10px;
.top {
//margin-bottom: 16px;
font-weight: bold;
}
.item {
display: flex;
align-items: center;
margin: 10px 0;
&:last-child {
margin-bottom: 0;
}
.icon {
display: inline-block;
width: 12px;
height: 12px;
margin-right: 10px;
border-radius: 50%;
}
.name {
width: 90px;
margin-right: 10px;
}
.value {
flex: 1;
text-align: right;
}
}
}
</style>