封装渐变堆叠柱状图组件附完整代码

组件功能

这是一个渐变堆叠柱状图组件,主要功能包括:

  1. 在一根柱子上同时显示高、中、低三种危险级别数据
  2. 使用渐变色区分不同危险级别(高危红色、中危橙色、低危蓝色)
  3. 悬停显示详细数据信息(包括总量和各级别数据)
  4. 自适应容器大小变化

使用方法在父组件中引入组件并传入数据




核心代码实现

1. 堆叠柱状图配置

// 核心实现:创建堆叠柱状图,三个系列分别代表低、中、高危
series: [
  {
    name: '低危',
    type: 'bar',
    stack: '总量',  // 设置堆叠,关键属性
    data: lowData,
    barWidth: '20%',
    itemStyle: {
      color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
        { offset: 0, color: '#70B2F7' },  // 顶部颜色
        { offset: 0.5, color: '#52A2FF' }, // 中间颜色
        { offset: 1, color: '#1970C2' }   // 底部颜色
      ])
    }
  },
  {
    name: '中危',
    type: 'bar',
    stack: '总量',
    data: middleData,
    // 中危渐变色配置...
  },
  {
    name: '高危',
    type: 'bar',
    stack: '总量',
    data: highData,
    // 高危渐变色配置...
  }
]

2. 渐变色实现

// 通过LinearGradient创建渐变效果
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
  { offset: 0, color: '#FF2E2E' },  // 顶部颜色(更鲜艳)
  { offset: 0.5, color: '#FF5252' }, // 中间颜色
  { offset: 1, color: '#FF8A8A' }   // 底部颜色(过渡到中危)
]),

3. 数据提示框配置

tooltip: {
  trigger: 'axis',
  formatter: function(params) {
    const index = params[0].dataIndex;
    const date = xAxisData[index];
    const total = totalData[index] || 0;
    
    let result = `${date}
总数:
${total}
`
; // 添加各危险级别数据 params.forEach((param) => { let value = param.value || 0; result += `${param.seriesName}: ${value}
`
; }); return result; } }

4. 数据变化更新

// 监听数据变化,只在真正变化时更新图表
watch(() => [props.warningData, props.warningSevenItem], ([newWarningData, newWarningSevenItem]) => {
  const newWarningDataStr = JSON.stringify(newWarningData);
  const newWarningSevenItemStr = JSON.stringify(newWarningSevenItem);
  
  // 检查数据是否有变化
  const dataChanged = newWarningDataStr !== prevWarningData.value || 
                      newWarningSevenItemStr !== prevWarningSevenItem.value;
  
  if (dataChanged) {
    if (chartInstance) {
      updateChart();
    } else {
      initChart();
    }
  }
}, { deep: true });

自定义调整

  1. 修改显示顺序:调整series数组中三个对象的顺序即可改变柱状图中高中低危的堆叠顺序

  2. 调整颜色:修改各系列的LinearGradient配置可以改变渐变色效果

  3. 调整圆角:目前顶部系列设置了borderRadius: [2, 2, 0, 0]实现顶部圆角效果

完整组件代码:

<template>
  <div ref="chartContainer" class="chart-container">
    <div ref="chart" class="chart"></div>
  </div>
</template>

<script lang="ts">
import { defineComponent, onMounted, ref, watch, onUnmounted } from 'vue';
import * as echarts from 'echarts';

export default defineComponent({
  name: 'JianbianZhu',
  props: {
    warningData: {
      type: Object,
      required: true
    },
    warningSevenItem: {
      type: Array,
      required: true
    }
  },
  setup(props) {
    const chartRef = ref<HTMLElement | null>(null);
    let chartInstance: echarts.ECharts | null = null;
    
    // 保存上一次的数据快照,用于比较数据是否变化
    const prevWarningData = ref<string>('');
    const prevWarningSevenItem = ref<string>('');

    // 处理窗口大小变化
    const handleResize = () => {
      if (chartInstance) {
        chartInstance.resize();
      }
    };

    const initChart = () => {
      if (!chartRef.value) return;
      
      // 创建图表实例
      chartInstance = echarts.init(chartRef.value);
      
      // 准备数据
      const xAxisData = props.warningSevenItem;
      const highData = props.warningData.high || [];
      const middleData = props.warningData.middle || [];
      const lowData = props.warningData.low || [];
      
      // 计算总数据用于展示
      const totalData = highData.map((val: number, index: number) => {
        return val + (middleData[index] || 0) + (lowData[index] || 0);
      });
      
      // 更新数据快照
      prevWarningData.value = JSON.stringify(props.warningData);
      prevWarningSevenItem.value = JSON.stringify(props.warningSevenItem);
      
      // 配置图表选项
      const option = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow'
          },
          formatter: function(params: any) {
            const index = params[0].dataIndex;
            const date = xAxisData[index];
            const total = totalData[index] || 0;
            
            let result = `${date}
总数:
${total}
`
; // 按顺序添加高中低危数据 params.forEach((param: any) => { let value = param.value || 0; result += `${param.seriesName}: ${value}
`
; }); return result; } }, legend: { data: ['低危', '中危', '高危'], textStyle: { color: 'rgba(255, 255, 255, 0.65)' }, right: '5%', top: '0%' }, grid: { left: '5%', right: '5%', bottom: '10%', top: '15%', containLabel: true }, xAxis: { type: 'category', data: xAxisData, axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } }, axisLabel: { color: 'rgba(255, 255, 255, 0.65)', fontSize: 12, interval: 0, rotate: 0 }, axisTick: { show: false } }, yAxis: { type: 'value', name: '', nameTextStyle: { color: 'rgba(255, 255, 255, 0.65)' }, min: 0, axisLine: { show: false }, axisTick: { show: false }, splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)', type: 'dashed', width: 0.5 } }, axisLabel: { color: 'rgba(255, 255, 255, 0.65)', fontSize: 12, formatter: function(value: number) { if (value >= 1000) { return Math.floor(value / 1000) + 'k'; } return value; } } }, series: [ { name: '低危', type: 'bar', stack: '总量', data: lowData, barWidth: '20%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#70B2F7' }, // 顶部颜色(与中危底部接近) { offset: 0.5, color: '#52A2FF' }, // 中间颜色 { offset: 1, color: '#1970C2' } // 底部颜色(更深) ]) }, emphasis: { itemStyle: { opacity: 0.9 } }, z: 10 }, { name: '中危', type: 'bar', stack: '总量', data: middleData, barWidth: '20%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FFA066' }, // 顶部颜色(与高危底部接近) { offset: 0.5, color: '#FFA647' }, // 中间颜色 { offset: 1, color: '#FFD0A1' } // 底部颜色(过渡到低危) ]) }, emphasis: { itemStyle: { opacity: 0.9 } }, z: 10 }, { name: '高危', type: 'bar', stack: '总量', data: highData, barWidth: '20%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FF2E2E' }, // 顶部颜色(更鲜艳) { offset: 0.5, color: '#FF5252' }, // 中间颜色 { offset: 1, color: '#FF8A8A' } // 底部颜色(过渡到中危) ]), borderRadius: [2, 2, 0, 0] // 只有最上面的柱子需要圆角 }, emphasis: { itemStyle: { opacity: 0.9 } }, z: 10 }, ], backgroundColor: 'transparent' }; // 设置图表选项 chartInstance.setOption(option); // 监听窗口大小变化自动调整图表大小 window.addEventListener('resize', handleResize); }; // 更新图表,不销毁实例 const updateChart = () => { if (!chartInstance || !chartRef.value) return; // 准备数据 const xAxisData = props.warningSevenItem; const highData = props.warningData.high || []; const middleData = props.warningData.middle || []; const lowData = props.warningData.low || []; // 计算总数据用于展示 const totalData = highData.map((val: number, index: number) => { return val + (middleData[index] || 0) + (lowData[index] || 0); }); // 更新数据快照 prevWarningData.value = JSON.stringify(props.warningData); prevWarningSevenItem.value = JSON.stringify(props.warningSevenItem); // 解决方案:创建完整的配置,使用true参数强制重置所有配置 // 这将确保tooltip格式化器使用最新数据 chartInstance.clear(); // 清除当前图表 // 完整重新配置图表 const option = { tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' }, formatter: function(params: any) { const index = params[0].dataIndex; const date = xAxisData[index]; const total = totalData[index] || 0; let result = `${date}
总数:
${total}
`
; // 按顺序添加高中低危数据 params.forEach((param: any) => { let value = param.value || 0; result += `${param.seriesName}: ${value}
`
; }); return result; } }, legend: { data: ['高危', '中危', '低危'], textStyle: { color: 'rgba(255, 255, 255, 0.65)' }, right: '5%', top: '0%' }, grid: { left: '5%', right: '5%', bottom: '10%', top: '15%', containLabel: true }, xAxis: { type: 'category', data: xAxisData, axisLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.2)' } }, axisLabel: { color: 'rgba(255, 255, 255, 0.65)', fontSize: 12, interval: 0, rotate: 0 }, axisTick: { show: false } }, yAxis: { type: 'value', name: '', nameTextStyle: { color: 'rgba(255, 255, 255, 0.65)' }, min: 0, axisLine: { show: false }, axisTick: { show: false }, splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.1)', type: 'dashed', width: 0.5 } }, axisLabel: { color: 'rgba(255, 255, 255, 0.65)', fontSize: 12, formatter: function(value: number) { if (value >= 1000) { return Math.floor(value / 1000) + 'k'; } return value; } } }, series: [ { name: '高危', type: 'bar', stack: '总量', data: highData, barWidth: '20%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FF2E2E' }, // 顶部颜色(更鲜艳) { offset: 0.5, color: '#FF5252' }, // 中间颜色 { offset: 1, color: '#FF8A8A' } // 底部颜色(过渡到中危) ]), borderRadius: [2, 2, 0, 0] // 只有最上面的柱子需要圆角 }, emphasis: { itemStyle: { opacity: 0.9 } }, z: 10 }, { name: '中危', type: 'bar', stack: '总量', data: middleData, barWidth: '20%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#FFA066' }, // 顶部颜色(与高危底部接近) { offset: 0.5, color: '#FFA647' }, // 中间颜色 { offset: 1, color: '#FFD0A1' } // 底部颜色(过渡到低危) ]) }, emphasis: { itemStyle: { opacity: 0.9 } }, z: 10 }, { name: '低危', type: 'bar', stack: '总量', data: lowData, barWidth: '20%', itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#70B2F7' }, // 顶部颜色(与中危底部接近) { offset: 0.5, color: '#52A2FF' }, // 中间颜色 { offset: 1, color: '#1970C2' } // 底部颜色(更深) ]) }, emphasis: { itemStyle: { opacity: 0.9 } }, z: 10 } ], backgroundColor: 'transparent' }; // 使用完整配置重新初始化图表 chartInstance.setOption(option); }; // 在组件挂载后初始化图表 onMounted(() => { initChart(); }); // 监听数据变化,仅在数据真正变化时更新图表 watch(() => [props.warningData, props.warningSevenItem], ([newWarningData, newWarningSevenItem]) => { const newWarningDataStr = JSON.stringify(newWarningData); const newWarningSevenItemStr = JSON.stringify(newWarningSevenItem); // 检查数据是否有变化 const dataChanged = newWarningDataStr !== prevWarningData.value || newWarningSevenItemStr !== prevWarningSevenItem.value; if (dataChanged) { // 如果数据有变化,更新图表 if (chartInstance) { updateChart(); } else { initChart(); } } }, { deep: true }); // 组件卸载时清理资源 onUnmounted(() => { if (chartInstance) { // 移除resize事件监听 window.removeEventListener('resize', handleResize); chartInstance.dispose(); chartInstance = null; } }); return { chart: chartRef }; } }); </script> <style scoped> .chart-container { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } .chart { width: 100%; height: 100%; min-height: 280px; } </style>

你可能感兴趣的:(封装渐变堆叠柱状图组件附完整代码)