WholeProcessPlatform/frontend/src/modules/qixidishuiwenbianhua/index.vue

316 lines
8.7 KiB
Vue
Raw Normal View History

2026-05-09 17:04:48 +08:00
<!-- 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>