2026-04-13 17:25:17 +08:00
|
|
|
<template>
|
|
|
|
|
<SidePanelItem title="水温年内分布">
|
|
|
|
|
<div class="water-temp-chart-container">
|
|
|
|
|
<div ref="chartRef" class="chart-wrapper"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- <div v-else>
|
|
|
|
|
<a-empty />
|
|
|
|
|
</div> -->
|
|
|
|
|
</SidePanelItem>
|
|
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-04-22 17:53:20 +08:00
|
|
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
2026-04-13 17:25:17 +08:00
|
|
|
import * as echarts from 'echarts'
|
|
|
|
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|
|
|
|
const data = [
|
|
|
|
|
{ monthInt: 1, actualTemp: 0.9, naturalTemp: 0 },
|
|
|
|
|
{ monthInt: 2, actualTemp: 0.9, naturalTemp: 0.1 },
|
|
|
|
|
{ monthInt: 3, actualTemp: 1.0, naturalTemp: 2.0 }
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
const chartRef = ref<HTMLElement | null>(null)
|
|
|
|
|
let chartInstance: echarts.ECharts | null = null
|
|
|
|
|
|
2026-04-22 17:53:20 +08:00
|
|
|
const transUnit = (value: number | null) => {
|
2026-04-13 17:25:17 +08:00
|
|
|
if (value === null) return null
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 17:53:20 +08:00
|
|
|
const getUnitConfigByCode = (_code: string, _type: string) => {
|
2026-04-13 17:25:17 +08:00
|
|
|
return {
|
|
|
|
|
unit: '℃'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 17:53:20 +08:00
|
|
|
const getColorByCodeAndType = (_code: string[], _typeKey: string[]) => {
|
2026-04-13 17:25:17 +08:00
|
|
|
return ['#4b79ab', '#78c300']
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const getChartOption = () => {
|
|
|
|
|
const { unit } = getUnitConfigByCode('Other', 'ACTUALTEMP')
|
|
|
|
|
const legendData = ['实测值', '天然']
|
|
|
|
|
const xData: string[] = []
|
|
|
|
|
const actualData: (number | null)[] = []
|
|
|
|
|
const naturalData: (number | null)[] = []
|
|
|
|
|
|
|
|
|
|
data.forEach((item: any) => {
|
|
|
|
|
xData.push(`${item.monthInt}月`)
|
2026-04-22 17:53:20 +08:00
|
|
|
actualData.push(item.actualTemp === null ? null : transUnit(item.actualTemp))
|
|
|
|
|
naturalData.push(item.naturalTemp === null ? null : transUnit(item.naturalTemp))
|
2026-04-13 17:25:17 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const code = ['Other']
|
|
|
|
|
const typeKey = ['ACTUALTEMP', 'NATURALTEMP']
|
|
|
|
|
const colors = getColorByCodeAndType(code, typeKey)
|
|
|
|
|
|
|
|
|
|
const options: any = {
|
|
|
|
|
color: colors,
|
|
|
|
|
tooltip: {
|
|
|
|
|
trigger: 'axis',
|
|
|
|
|
backgroundColor: 'rgba(51, 51, 51, 0.9)',
|
|
|
|
|
borderColor: 'transparent',
|
|
|
|
|
textStyle: {
|
|
|
|
|
color: '#ffffff',
|
|
|
|
|
fontSize: 14
|
|
|
|
|
},
|
2026-04-22 17:53:20 +08:00
|
|
|
formatter: function(params: any) {
|
2026-04-13 17:25:17 +08:00
|
|
|
if (!params || params.length === 0) return '';
|
|
|
|
|
let result = `<div style="font-weight: bold; margin-bottom: 8px;">${params[0].axisValue}</div>`;
|
2026-04-22 17:53:20 +08:00
|
|
|
params.forEach((item: any) => {
|
2026-04-13 17:25:17 +08:00
|
|
|
result += `<div style="display: flex; align-items: center; margin: 4px 0;">`;
|
|
|
|
|
result += `<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${item.color}; margin-right: 8px;"></span>`;
|
|
|
|
|
result += `<span>${item.seriesName} ${item.value}${unit}</span>`;
|
|
|
|
|
result += `</div>`;
|
|
|
|
|
});
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
xAxis: {
|
|
|
|
|
type: 'category',
|
|
|
|
|
boundaryGap: true,
|
|
|
|
|
axisPointer: {
|
|
|
|
|
type: 'shadow',
|
|
|
|
|
shadowStyle: {
|
|
|
|
|
color: '#007aff1a'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
axisLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#8f8f8f'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: false
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#bfbfbf',
|
|
|
|
|
type: 'solid'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
interval: 0,
|
|
|
|
|
color: '#000000',
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
rotate: 0,
|
|
|
|
|
margin: 8,
|
|
|
|
|
align: 'center'
|
|
|
|
|
},
|
|
|
|
|
data: xData
|
|
|
|
|
},
|
|
|
|
|
yAxis: [
|
|
|
|
|
{
|
|
|
|
|
name: '水温(℃)',
|
|
|
|
|
type: 'value',
|
|
|
|
|
splitNumber: 5,
|
|
|
|
|
nameTextStyle: {
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: '#000000'
|
|
|
|
|
},
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#6ca4f7',
|
|
|
|
|
width: 2
|
|
|
|
|
},
|
|
|
|
|
axisLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#000000'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
axisTick: {
|
|
|
|
|
show: true,
|
|
|
|
|
length: 3,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#000000'
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
axisLabel: {
|
|
|
|
|
color: '#333',
|
|
|
|
|
fontSize: 12
|
|
|
|
|
},
|
|
|
|
|
splitLine: {
|
|
|
|
|
show: true,
|
|
|
|
|
lineStyle: {
|
|
|
|
|
color: '#bfbfbf',
|
|
|
|
|
type: 'solid'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
legend: {
|
|
|
|
|
data: legendData
|
|
|
|
|
},
|
|
|
|
|
grid: {
|
|
|
|
|
top: 30,
|
|
|
|
|
bottom: 50,
|
|
|
|
|
right: 15,
|
|
|
|
|
left: 30
|
|
|
|
|
},
|
|
|
|
|
series: [
|
|
|
|
|
{
|
|
|
|
|
name: legendData[0],
|
|
|
|
|
data: actualData,
|
|
|
|
|
connectNulls: true,
|
|
|
|
|
smooth: true,
|
|
|
|
|
type: 'line',
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: colors[0]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: legendData[1],
|
|
|
|
|
data: naturalData,
|
|
|
|
|
connectNulls: true,
|
|
|
|
|
smooth: true,
|
|
|
|
|
type: 'line',
|
|
|
|
|
itemStyle: {
|
|
|
|
|
color: colors[1]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return options
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const initChart = async () => {
|
|
|
|
|
// 延迟初始化,确保容器已完成渲染
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
|
|
|
|
|
|
|
|
if (!chartRef.value) {
|
|
|
|
|
console.error('图表容器未找到')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
if (!chartInstance) {
|
|
|
|
|
chartInstance = echarts.init(chartRef.value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const option = getChartOption()
|
|
|
|
|
chartInstance.setOption(option, false)
|
|
|
|
|
|
|
|
|
|
// 强制重绘,确保图表尺寸正确
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
chartInstance?.resize()
|
|
|
|
|
}, 100)
|
|
|
|
|
|
|
|
|
|
window.addEventListener('resize', handleResize)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('图表初始化失败:', error)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleResize = () => {
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
chartInstance.resize()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
initChart()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
window.removeEventListener('resize', handleResize)
|
|
|
|
|
if (chartInstance) {
|
|
|
|
|
chartInstance.dispose()
|
|
|
|
|
chartInstance = null
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.water-temp-chart-container {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
min-height: 290px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.chart-wrapper {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
min-height: 290px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|