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

news2025/7/19 7:48:14

在这里插入图片描述

组件功能

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

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

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

<template>
  <JianbianZhu :warningData="warningData" :warningSevenItem="warningSevenItem" />
</template>

<script>
export default {
  data() {
    return {
      warningData: {
        high: [30, 40, 35, 50, 60, 40, 80],    // 高危数据
        middle: [70, 60, 65, 60, 60, 80, 70],  // 中危数据
        low: [50, 70, 80, 70, 60, 70, 60]      // 低危数据
      },
      warningSevenItem: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'] // X轴标签
    }
  }
}
</script>

核心代码实现

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}<br/>总数: ${total}<br/>`;
    
    // 添加各危险级别数据
    params.forEach((param) => {
      let value = param.value || 0;
      result += `${param.seriesName}: ${value}<br/>`;
    });
    
    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}<br/>总数: ${total}<br/>`;
            
            // 按顺序添加高中低危数据
            params.forEach((param: any) => {
              let value = param.value || 0;
              result += `${param.seriesName}: ${value}<br/>`;
            });
            
            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}<br/>总数: ${total}<br/>`;
            
            // 按顺序添加高中低危数据
            params.forEach((param: any) => {
              let value = param.value || 0;
              result += `${param.seriesName}: ${value}<br/>`;
            });
            
            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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2387485.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

山东大学软件学院创新项目实训开发日志——第十三周

目录 1.开展prompt工程&#xff0c;创建个性化AI助理&#xff0c;能够基于身份实现不同角度和语言风格的回答。 2.对输出进行格式化&#xff0c;生成特定格式的会议计划文档。 3.学习到的新知识 本阶段我所做的工作 1.开展prompt工程&#xff0c;创建个性化AI助理&#xff…

Cesium 透明渐变墙 解决方案

闭合路径修复 通过增加额外点确保路径首尾相接 透明渐变效果 使用RGBA颜色模式实现从完全不透明到完全透明的平滑渐变 参数可调性 提供多个可调参数&#xff0c;轻松自定义颜色、高度和圆环尺寸 完整代码实现 <!DOCTYPE html> <html> <head><meta …

day022-定时任务-故障案例与发送邮件

文章目录 1. cron定时任务无法识别命令1.1 故障原因1.2 解决方法1.2.1 对命令使用绝对路径1.2.2 在脚本开头定义PATH 2. 发送邮件2.1 安装软件2.2 配置邮件信息2.3 巡检脚本与邮件发送2.3.1 巡检脚本内容2.3.2 制作时任务发送邮件 3. 调取API发送邮件3.1 编写文案脚本3.2 制作定…

新增 git submodule 子模块

文章目录 1、基本语法2、添加子模块后的操作3、拉取带有submodule的仓库 git submodule add 是 Git 中用于将另一个 Git 仓库作为子模块添加到当前项目中的命令。 子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录&#xff0c;同时保持它们各自的提交历史独立。 1、基…

List优雅分组

一、前言 最近小永哥发现&#xff0c;在开发过程中&#xff0c;经常会遇到需要对list进行分组&#xff0c;就是假如有一个RecordTest对象集合&#xff0c;RecordTest对象都有一个type的属性&#xff0c;需要将这个集合按type属性进行分组&#xff0c;转换为一个以type为key&…

Linux 使用 Docker 安装 Milvus的两种方式

一、使用 Docker Compose 运行 Milvus (Linux) 安装并启动 Milvus Milvus 在 Milvus 资源库中提供了 Docker Compose 配置文件。要使用 Docker Compose 安装 Milvus&#xff0c;只需运行 wget https://github.com/milvus-io/milvus/releases/download/v2.5.10/milvus-standa…

AR眼镜+AI视频盒子+视频监控联网平台:消防救援的智能革命

在火灾现场&#xff0c;每一秒都关乎生死。传统消防救援方式面临信息滞后、指挥盲区、环境复杂等挑战。今天&#xff0c;一套融合AR智能眼镜AI视频分析盒子智能监控管理平台的"三位一体"解决方案&#xff0c;正在彻底改变消防救援的作业模式&#xff0c;为消防员装上…

编程技能:字符串函数10,strchr

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;编程技能&#xff1a;字符串函数09&#xff0c;strncmp 回到目录…

使用tunasync部署企业内部开源软件镜像站-Centos Stream 9

使用tunasync部署企业内部开源软件镜像站 tunasync 是清华大学 TUNA 镜像源目前使用的镜像方案&#xff0c;本文将介绍如何使用 tunasync 部署企业内部开源软件镜像站。 基于tunasync mirror-web nginx进行镜像站点搭建。 1. tunasync设计 tunasync架构如下&#xff1a; …

C++----Vector的模拟实现

上一节讲了string的模拟实现&#xff0c;string的出现时间比vector靠前&#xff0c;所以一些函数给的也比较冗余&#xff0c;而后来的vector、list等在此基础上做了优化。这节讲一讲vector的模拟实现&#xff0c;vector与模板具有联系&#xff0c;而string的底层就是vector的一…

Mac redis下载和安装

目录 1、官网&#xff1a;https://redis.io/ 2、滑到最底下 3、下载资源 4、安装&#xff1a; 5、输入 sudo make test 进行编译测试 会提示 ​编辑 6、sudo make install 继续 7、输入 src/redis-server 启动服务器 8、输入 src/redis-cli 启动测试端 1、官网&#xff…

[25-cv-05718]BSF律所代理潮流品牌KAWS公仔(商标+版权)

潮流品牌KAWS公仔 案件号&#xff1a;25-cv-05718 立案时间&#xff1a;2025年5月21日 原告&#xff1a;KAWS, INC. 代理律所&#xff1a;Boies Schiller Flexner LLP 原告介绍 原告是一家由美国街头艺术家Brian Donnelly创立的公司&#xff0c;成立于2002年2月25日&…

深度思考、弹性实施,业务流程自动化的实践指南

随着市场环境愈发复杂化&#xff0c;各类型企业的业务步伐为了跟得上市场节奏也逐步变得紧张&#xff0c;似乎只有保持极强的竞争力、削减成本、提升抗压能力才能在市场洪流中博得一席之位。此刻企业需要制定更明智的解决方案&#xff0c;以更快、更准确地优化决策流程。与简单…

UWB:litepoint获取txquality里面的NRMSE

在使用litepoint测试UWB,获取txquality里面的NRMSE时,网页端可以正常获取NRMSE。但是通过SCPI 命令来获取NRMSE一直出错。 NRMSE数据类型和pyvisa问题: 参考了user guide,发现NRMSE的数值是ARBITRARY_BLOCK FLOAT,非string。 pyvisa无法解析会返回错误。 查询了各种办法…

VUE npm ERR! code ERESOLVE, npm ERR! ERESOLVE could not resolve, 错误有效解决

VUE &#xff1a; npm ERR! code ERESOLVE npm ERR! ERESOLVE could not resolve 错误有效解决 npm install 安装组件的时候出现以上问题&#xff0c;npm版本问题报错解决方法&#xff1a;用上述方法安装完成之后又出现其他的问题 npm install 安装组件的时候出现以上问题&…

IoT/HCIP实验-1/物联网开发平台实验Part1(快速入门,MQTT.fx对接IoTDA)

文章目录 实验介绍设备接入IoTDA进入IoTDA平台什么是IoTDA 开通服务创建产品和设备定义产品模型&#xff08;Profile&#xff09;设备注册简思(实例-产品-设备) 模拟.与平台通信虚拟设备/MQTT.fx应用 Web 控制台QA用户或密码错误QA证书导致的连接失败设备与平台连接成功 上报数…

DMA STM32H7 Domains and space distrubution

DMA这个数据搬运工&#xff0c;对谁都好&#xff0c;任劳任怨&#xff0c;接受雇主设备的数据搬运业务。每天都忙碌着&#xff01;哈哈哈。 1. DMA 不可能单独工作&#xff0c;必须接收其他雇主的业务&#xff0c;所以数据搬运业务的参与者是DMA本身和业务需求发起者。 2. 一…

洪水危险性评价与风险防控全攻略:从HEC-RAS数值模拟到ArcGIS水文分析,一键式自动化工具实战,助力防洪减灾与应急管理

&#x1f50d; 洪水淹没危险性是洪水损失评估、风险评估及洪水应急和管理规划等工作的重要基础。当前&#xff0c;我国正在开展的自然灾害风险普查工作&#xff0c;对洪水灾害给予了重点关注&#xff0c;提出了对洪水灾害危险性及风险评估的明确要求。洪水危险性及风险评估通常…

Leetcode 3269. 构建两个递增数组

1.题目基本信息 1.1.题目描述 给定两个只包含 0 和 1 的整数数组 nums1 和 nums2&#xff0c;你的任务是执行下面操作后使数组 nums1 和 nums2 中 最大 可达数字 尽可能小。 将每个 0 替换为正偶数&#xff0c;将每个 1 替换为正奇数。在替换后&#xff0c;两个数组都应该 递…

三轴云台之积分分离PID控制算法篇

一、核心原理 积分分离PID控制的核心在于动态调整积分项的作用&#xff0c;以解决传统PID在三轴云台应用中的超调、振荡问题&#xff1a; 大误差阶段&#xff08;如云台启动或快速调整时&#xff09;&#xff1a; 关闭积分项&#xff0c;仅使用比例&#xff08;P&#xff09;…