316 lines
8.7 KiB
Vue
316 lines
8.7 KiB
Vue
|
|
<!-- SidePanelItem.vue -->
|
|||
|
|
<template>
|
|||
|
|
<SidePanelItem title="水温监测" :select="select" :datetimePicker="datetimePicker">
|
|||
|
|
<div ref="chartRef" class="water-temp-chart"></div>
|
|||
|
|
</SidePanelItem>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
|||
|
|
import * as echarts from 'echarts';
|
|||
|
|
import type { EChartsOption } from 'echarts';
|
|||
|
|
import SidePanelItem from '@/components/SidePanelItem/index.vue';
|
|||
|
|
|
|||
|
|
// 定义组件名
|
|||
|
|
defineOptions({
|
|||
|
|
name: 'qixidishuiwenbianhua'
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// ==================== 响应式变量定义 ====================
|
|||
|
|
|
|||
|
|
// 图表容器引用
|
|||
|
|
const chartRef = ref<HTMLDivElement>();
|
|||
|
|
let chartInstance: echarts.ECharts | null = null;
|
|||
|
|
|
|||
|
|
// 选择器配置
|
|||
|
|
const select = ref({
|
|||
|
|
show: true,
|
|||
|
|
value: undefined,
|
|||
|
|
options: [],
|
|||
|
|
picker: undefined,
|
|||
|
|
format: undefined
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 日期选择器配置
|
|||
|
|
const datetimePicker = ref({
|
|||
|
|
show: true,
|
|||
|
|
value: undefined,
|
|||
|
|
format: 'YYYY',
|
|||
|
|
picker: 'year' as const,
|
|||
|
|
options: []
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 图表数据
|
|||
|
|
const echartsData = ref<any[]>([]);
|
|||
|
|
|
|||
|
|
// 单位配置
|
|||
|
|
const unita = ref('℃');
|
|||
|
|
|
|||
|
|
// 颜色配置 - 使用HSL色彩空间生成舒适的颜色
|
|||
|
|
const Color = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4'];
|
|||
|
|
|
|||
|
|
// ==================== 工具函数 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成模拟数据 - 季节性水温变化
|
|||
|
|
*/
|
|||
|
|
const generateMockData = () => {
|
|||
|
|
const data = [];
|
|||
|
|
const baseTemp = 15; // 基础温度
|
|||
|
|
|
|||
|
|
for (let i = 0; i < 12; i++) {
|
|||
|
|
const month = String(i + 1).padStart(2, '0');
|
|||
|
|
// 模拟季节性变化:夏季高,冬季低
|
|||
|
|
// 正弦波模拟:峰值在7-8月,谷值在1-2月
|
|||
|
|
const temp = baseTemp + Math.sin((i / 12) * Math.PI * 2 - Math.PI / 2) * 10 + Math.random() * 2;
|
|||
|
|
|
|||
|
|
data.push({
|
|||
|
|
dt: `2024-${month}`,
|
|||
|
|
wt: parseFloat(temp.toFixed(1))
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return data;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 初始化图表
|
|||
|
|
*/
|
|||
|
|
const initChart = () => {
|
|||
|
|
if (!chartRef.value) {
|
|||
|
|
console.warn('图表容器未找到');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 销毁已存在的实例
|
|||
|
|
if (chartInstance) {
|
|||
|
|
chartInstance.dispose();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建新实例
|
|||
|
|
chartInstance = echarts.init(chartRef.value);
|
|||
|
|
|
|||
|
|
// 更新图表
|
|||
|
|
updateChart();
|
|||
|
|
|
|||
|
|
// 监听窗口大小变化
|
|||
|
|
window.addEventListener('resize', handleResize);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新图表配置
|
|||
|
|
*/
|
|||
|
|
const updateChart = () => {
|
|||
|
|
if (!chartInstance) return;
|
|||
|
|
|
|||
|
|
const _legendData = [`水温(${unita.value})`];
|
|||
|
|
|
|||
|
|
const option: EChartsOption = {
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|||
|
|
borderColor: 'transparent',
|
|||
|
|
textStyle: {
|
|||
|
|
color: '#ffffff'
|
|||
|
|
},
|
|||
|
|
formatter: function (params: any) {
|
|||
|
|
if (!params || params.length === 0) return '';
|
|||
|
|
|
|||
|
|
let res = `${params[0].name} <br/>`;
|
|||
|
|
|
|||
|
|
for (const item of params) {
|
|||
|
|
const seriesName = item.seriesName ?? '';
|
|||
|
|
let regx = /\(([^()]+?)\)/;
|
|||
|
|
let unit = seriesName.match(regx);
|
|||
|
|
const finalValue = item.value !== undefined && item.value !== null ? item.value : '-';
|
|||
|
|
|
|||
|
|
if (item.value !== undefined && item.value !== null) {
|
|||
|
|
res += `<span style="background: ${item.color}; height:10px; width: 10px; border-radius: 50%;display: inline-block;margin-right:10px;"></span> ${item.seriesName} ${finalValue}${unit?.[1] || ''} <br/>`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return res;
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// axisPointer 配置:鼠标经过时显示阴影块
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow',
|
|||
|
|
label: {
|
|||
|
|
show: false
|
|||
|
|
},
|
|||
|
|
shadowStyle: {
|
|||
|
|
color: 'rgba(84, 112, 198, 0.2)'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
color: Color,
|
|||
|
|
legend: {
|
|||
|
|
data: _legendData,
|
|||
|
|
top: 10,
|
|||
|
|
left: 'center',
|
|||
|
|
selectedMode: 'multiple'
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
top: 45,
|
|||
|
|
left: '10%',
|
|||
|
|
right: '10px',
|
|||
|
|
bottom: '10%',
|
|||
|
|
containLabel: true
|
|||
|
|
},
|
|||
|
|
dataZoom: {
|
|||
|
|
type: 'inside',
|
|||
|
|
start: 0,
|
|||
|
|
end: 100
|
|||
|
|
},
|
|||
|
|
xAxis: [
|
|||
|
|
{
|
|||
|
|
type: 'category',
|
|||
|
|
data: echartsData.value?.map((item: any) => item.dt) || [],
|
|||
|
|
boundaryGap: true,
|
|||
|
|
// X轴网格线(垂直方向)
|
|||
|
|
splitLine: {
|
|||
|
|
show: true,
|
|||
|
|
lineStyle: {
|
|||
|
|
type: 'solid',
|
|||
|
|
color: '#e0e0e0'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
// X轴的axisPointer配置:鼠标经过时显示阴影块
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow',
|
|||
|
|
shadowStyle: {
|
|||
|
|
color: 'rgba(84, 112, 198, 0.2)'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
yAxis: [
|
|||
|
|
{
|
|||
|
|
type: 'value',
|
|||
|
|
name: _legendData[0],
|
|||
|
|
// 确保显示刻度和标签
|
|||
|
|
axisLabel: {
|
|||
|
|
show: true,
|
|||
|
|
color: '#333' // 刻度标签颜色
|
|||
|
|
},
|
|||
|
|
axisTick: {
|
|||
|
|
show: true
|
|||
|
|
},
|
|||
|
|
axisLine: {
|
|||
|
|
show: true,
|
|||
|
|
lineStyle: {
|
|||
|
|
color: '#333' // Y轴轴线颜色,与刻度标签一致
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
splitLine: {
|
|||
|
|
show: true,
|
|||
|
|
lineStyle: {
|
|||
|
|
type: 'solid' // 虚线改为实线
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
splitNumber: 3
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: _legendData[0],
|
|||
|
|
data: echartsData.value?.map((item: any) => item.wt) || [],
|
|||
|
|
type: 'line',
|
|||
|
|
smooth: true,
|
|||
|
|
symbol: 'circle',
|
|||
|
|
symbolSize: 6,
|
|||
|
|
lineStyle: {
|
|||
|
|
width: 2
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 使用完全替换模式更新配置
|
|||
|
|
chartInstance.setOption(option, true);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理窗口大小变化
|
|||
|
|
*/
|
|||
|
|
const handleResize = () => {
|
|||
|
|
if (chartInstance) {
|
|||
|
|
chartInstance.resize();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 加载模拟数据
|
|||
|
|
*/
|
|||
|
|
const loadData = async () => {
|
|||
|
|
try {
|
|||
|
|
// 生成模拟数据
|
|||
|
|
echartsData.value = generateMockData();
|
|||
|
|
|
|||
|
|
// 数据加载完成后更新图表
|
|||
|
|
await nextTick();
|
|||
|
|
updateChart();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载水温数据失败:', error);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ==================== 生命周期钩子 ====================
|
|||
|
|
|
|||
|
|
// 页面加载时执行
|
|||
|
|
onMounted(() => {
|
|||
|
|
// 双重等待确保DOM和样式计算完成
|
|||
|
|
nextTick(() => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
// 校验容器尺寸
|
|||
|
|
if (chartRef.value) {
|
|||
|
|
const rect = chartRef.value.getBoundingClientRect();
|
|||
|
|
if (rect.width > 0 && rect.height > 0) {
|
|||
|
|
initChart();
|
|||
|
|
loadData();
|
|||
|
|
} else {
|
|||
|
|
console.warn('图表容器尺寸为0,启动重试机制');
|
|||
|
|
// 指数退避重试
|
|||
|
|
let retryCount = 0;
|
|||
|
|
const maxRetries = 5;
|
|||
|
|
const retryInit = () => {
|
|||
|
|
if (retryCount < maxRetries) {
|
|||
|
|
retryCount++;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const newRect = chartRef.value?.getBoundingClientRect();
|
|||
|
|
if (newRect && newRect.width > 0 && newRect.height > 0) {
|
|||
|
|
initChart();
|
|||
|
|
loadData();
|
|||
|
|
} else {
|
|||
|
|
retryInit();
|
|||
|
|
}
|
|||
|
|
}, 50 * Math.pow(2, retryCount));
|
|||
|
|
} else {
|
|||
|
|
console.error('图表容器初始化失败,已达到最大重试次数');
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
retryInit();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}, 50);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 组件卸载时清理
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
// 移除事件监听
|
|||
|
|
window.removeEventListener('resize', handleResize);
|
|||
|
|
|
|||
|
|
// 销毁图表实例
|
|||
|
|
if (chartInstance) {
|
|||
|
|
chartInstance.dispose();
|
|||
|
|
chartInstance = null;
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="scss" scoped>
|
|||
|
|
.water-temp-chart {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 231px;
|
|||
|
|
min-height: 231px;
|
|||
|
|
}
|
|||
|
|
</style>
|