<template>
<div ref="chartRef" style="width: 800px; height: 500px;"></div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts';
const chartRef = ref(null);
const chartInstance = ref(null);
// 配置数据
const days = Array.from({ length: 15 }, (_, i) => `${i + 1}号`);
const actualData = [120, 135, null, null, null, null, null, 210, 175, 190, 205, null, 230, 185, 160];
// 计算无数据区间
const emptyRanges = computed(() => {
const ranges = [];
let start = null;
actualData.forEach((val, index) => {
if (val === null && start === null) {
start = index;
} else if (val !== null && start !== null) {
ranges.push({ start, end: index - 1 });
start = null;
}
});
if (start !== null) {
ranges.push({ start, end: actualData.length - 1 });
}
return ranges;
});
// 生成占位点(每个区间中心一个点)
const placeholderPoints = computed(() => {
return emptyRanges.value.map(range => {
const centerPos = Math.floor((range.start + range.end) / 2);
const dayRange = `${days[range.start]}至${days[range.end]}`;
return {
xIndex: centerPos,
info: `${dayRange}无数据\n原因:系统维护期间`
};
});
});
// 计算Y轴中点位置
const yMiddle = computed(() => {
const validData = actualData.filter(v => v !== null);
return validData.length ? (Math.max(...validData) * 0.6) : 50;
});
// 初始化图表
const initChart = () => {
if (!chartRef.value) return;
chartInstance.value = echarts.init(chartRef.value);
const option = {
grid: {
top: 30,
right: 30,
bottom: 30,
left: 50
},
xAxis: {
type: 'category',
data: days,
axisLabel: {
interval: 0,
rotate: 45
},
axisLine: {
lineStyle: {
color: '#999'
}
}
},
yAxis: {
type: 'value',
axisLine: {
show: true,
lineStyle: {
color: '#999'
}
},
splitLine: {
lineStyle: {
type: 'dashed'
}
}
},
tooltip: {
trigger: 'item',
formatter: (params) => {
if (params.seriesName === 'missing') {
return params.data.info.replace('\n', '<br/>');
}
return `${params.name}<br/>值:${params.value}`;
},
backgroundColor: 'rgba(50,50,50,0.9)',
borderColor: '#333',
textStyle: {
color: '#fff'
}
},
series: [
{
name: 'data',
type: 'line',
data: actualData,
lineStyle: {
color: '#4285F4',
width: 3
},
itemStyle: {
color: '#4285F4'
},
symbol: 'circle',
symbolSize: 8,
emphasis: {
itemStyle: {
color: '#FF7043',
borderColor: '#fff',
borderWidth: 2
}
}
},
{
name: 'data',
type: 'bar',
data: actualData,
lineStyle: {
color: '#4285F4',
width: 3
},
itemStyle: {
color: '#4285F4'
},
symbol: 'circle',
symbolSize: 8,
emphasis: {
itemStyle: {
color: '#FF7043',
borderColor: '#fff',
borderWidth: 2
}
}
},
{
name: 'missing',
type: 'scatter',
data: placeholderPoints.value.map(point => ({
value: [point.xIndex, yMiddle.value],
info: point.info
})),
symbol: 'circle',
symbolSize: 16,
itemStyle: {
color: '#FF5252',
opacity: 0.8,
borderColor: '#fff',
borderWidth: 2
},
emphasis: {
scale: 1.2,
itemStyle: {
opacity: 1
}
},
// label: {
// show: true,
// formatter: '无数据',
// color: '#FF5252',
// fontWeight: 'bold',
// position: 'top'
// }
}
]
};
chartInstance.value.setOption(option);
};
// 响应式调整
const handleResize = () => {
chartInstance.value?.resize();
};
onMounted(() => {
initChart();
window.addEventListener('resize', handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
chartInstance.value?.dispose();
});
</script>
<style scoped>
/* 增加图表容器阴影效果 */
div {
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
background: white;
padding: 10px;
}
</style>