<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>
posted on 2025-05-18 22:12  铭の  阅读(10)  评论(0)    收藏  举报

友情链接:箫竹影(Java工程师)