<template>
<div ref="chartRef" style="width: 100%; height: 700px;"></div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts';
const chartRef = ref(null);
let chartInstance = null;
// 定義區間配置
const axisConfigs = [
{
name: '時間軸',
data: Array.from({length: 15}, (_, i) => `${i + 1}天`),
showLine: true,
offset: 0,
axisTick: {
show: true,
alignWithLabel: false,
length:40,
lineStyle: {
color: '#333'
}
}
},
{
name: 'xxx分類',
intervals: [[1, 3], [4, 7], [8, 11], [12, 15]],
labels: ['xxx1', 'xxx2', 'xxx3', 'xxx4'],
offset: 40,
axisTick: {
show: true,
length: 40,
interval: (index) => [0, 3, 7, 11].includes(index),
lineStyle: {
color: '#666'
}
}
},
{
name: 'AAA分類',
intervals: [[1, 7], [8, 11], [12, 15]],
labels: ['AAA1', 'AAA2', 'AAA3'],
offset: 80,
axisTick: {
show: true,
length: 40,
interval: (index) => [0, 7, 11].includes(index),
lineStyle: {
color: '#666'
}
}
},
{
name: 'bbb分類',
intervals: [[1, 5], [6, 9], [10, 12], [13, 15]],
labels: ['bbb1', 'bbb2', 'bbb3', 'bbb4'],
offset: 120,
axisTick: {
show: true,
length: 15,
interval: (index) => [0, 5, 9, 12].includes(index),
lineStyle: {
color: '#666'
}
}
}
];
// 生成合并區間的X軸數據
const generateMergedAxis = (intervals, labels) => {
const axisData = Array(15).fill('');
intervals.forEach(([start, end], index) => {
const middlePos = start + Math.floor((end - start) / 2) - 1;
axisData[middlePos] = labels[index];
});
return axisData;
};
onMounted(() => {
initChart();
});
onBeforeUnmount(() => {
if (chartInstance) {
chartInstance.dispose();
}
});
const initChart = () => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
// 準備X軸配置
const xAxis = axisConfigs.map((config, index) => ({
type: 'category',
position: 'bottom',
offset: config.offset,
data: config.intervals
? generateMergedAxis(config.intervals, config.labels)
: config.data,
axisLabel: {
interval: 0,
rotate: 0,
formatter: (value) => value,
margin: 8,
verticalAlign: 'top', // 標簽在刻度上方
padding: [index === 0 ? 0 : 10, 0, 0, 0] // 調整標簽位置
},
axisTick: config.axisTick,
axisLine: {
show: true,
lineStyle: {
color: index === 0 ? '#333' : '#666',
width: index === 0 ? 1 : 0.8
},
onZero: false // 軸線不一定要在零值位置
},
splitLine: {
show: false
},
nameLocation: 'middle',
nameGap: index === 0 ? 25 : 40, // 調整軸名稱與軸線的距離
nameTextStyle: {
padding: [0, 0, index === 0 ? 10 : 30, 0] // 調整軸名稱位置
}
}));
const option = {
title: {
text: '生產數據統計',
left: 'center',
top: 10
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999'
}
}
},
legend: {
data: ['產量', '不良品A', '不良品B', '不良率A', '不良率B'],
bottom: 10,
itemGap: 20
},
grid: {
top: '18%',
right: '12%',
bottom: '35%', // 增加底部空間容納多X軸
left: '10%'
},
xAxis: xAxis,
yAxis: [
{
type: 'value',
name: '數量',
min: 0,
axisLabel: {
formatter: '{value}'
}
},
{
type: 'value',
name: '不良率',
min: 0,
max: 100,
axisLabel: {
formatter: '{value}%'
}
}
],
series: [
{
name: '產量',
type: 'bar',
barWidth: '60%',
data: [120, 132, 145, 160, 172, 190, 210, 232, 256, 280, 300, 320, 340, 360, 380],
itemStyle: {
color: '#5470C6'
}
},
{
name: '不良率A',
type: 'line',
yAxisIndex: 1,
data: [16.7, 16.7, 17.2, 16.3, 15.7, 15.3, 14.8, 14.2, 13.7, 13.6, 13.3, 13.1, 12.9, 12.8, 12.6],
itemStyle: {
color: '#EE6666'
},
symbol: 'circle',
symbolSize: 8,
lineStyle: {
width: 3
}
},
{
name: '不良率B',
type: 'line',
yAxisIndex: 1,
data: [8.3, 9.1, 10.3, 10.0, 9.9, 10.0, 10.0, 9.9, 9.8, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0],
itemStyle: {
color: '#73C0DE'
},
symbol: 'diamond',
symbolSize: 8,
lineStyle: {
width: 3
}
}
]
};
chartInstance.setOption(option);
// 添加區間分隔線
const addIntervalLines = () => {
const graphics = [];
axisConfigs.slice(1).forEach((config, axisIndex) => {
if (!config.intervals) return;
const axisModel = chartInstance.getModel().getComponent('xAxis', axisIndex + 1);
const axis = axisModel.axis;
const axisPos = axis.getExtent()[1] + config.offset;
config.intervals.forEach(([start, end]) => {
// 區間起始線
const x1 = chartInstance.convertToPixel({ xAxisIndex: 0 }, start - 1.5);
graphics.push({
type: 'line',
shape: {
x1,
y1: axisPos - 20, // 從軸線向上延伸
x2: x1,
y2: axisPos + 5 // 向下延伸一點
},
style: {
stroke: '#666',
lineWidth: 1,
opacity: 0.8
},
z: 100
});
// 區間結束線
const x2 = chartInstance.convertToPixel({ xAxisIndex: 0 }, end - 0.5);
graphics.push({
type: 'line',
shape: {
x1: x2,
y1: axisPos - 20,
x2: x2,
y2: axisPos + 5
},
style: {
stroke: '#666',
lineWidth: 1,
opacity: 0.8
},
z: 100
});
});
});
chartInstance.setOption({ graphic: graphics });
};
setTimeout(() => {
addIntervalLines();
}, 0);
// 響應式調整
const resizeHandler = () => {
if (chartInstance) {
chartInstance.resize();
setTimeout(() => {
chartInstance.setOption({ graphic: [] });
addIntervalLines();
}, 0);
}
};
window.addEventListener('resize', resizeHandler);
onBeforeUnmount(() => {
window.removeEventListener('resize', resizeHandler);
});
};
</script>
浙公網安備 33010602011771號