echarts重叠柱状图相关

前言

效果图
在这里插入图片描述
在这里插入图片描述

主要问题点
1、柱状图重叠stack
2、数据过大显示处理log(对数坐标)
3、对数(极)坐标0数据处理
4、0数据项显示为灰色柱状条
5、窗口变化自适应
6、侧边导航展开收起对echarts图的影响
  1. 柱状图重叠stack

    // 1.切换x、y轴
     yAxis: {
      type: 'category',
      boundaryGap: true,
      show: true,
      axisLine: {
        lineStyle: {
          opacity: 0,
        },
      },
      axisTick: { show: false }, // 刻度线提示
      data: ['网络风险', '应用风险', '攻击威胁', '数据泄漏', 'APP风险'],
    },
    xAxis: {
      type: 'value',
      show: true,
      axisLine: {
        show: false,
      },
    },
    // 2.数据准备
     series: [
      {
        name: '已认证企业',
        data: ['1', '2', '700', '10000', '80'],
        type: 'bar',
        smooth: true,
        stack: 'total',
        emphasis: {
          focus: 'series',
        },
        itemStyle: {
          color: 'rgba(15, 175, 255, 1)', // 柱状图颜色
          borderRadius: [7, 0, 0, 7], // 柱状图圆角
          borderWidth: 0,
        },
        barWidth: 15, // 宽
        barMinHeight: 10, // 高
      },
      {
        name: '三方供应商',
        data: ['100', '200000', '700', '200', '800000'],
        type: 'bar',
        stack: 'total',
        smooth: true,
        emphasis: {
          focus: 'series',
        },
        itemStyle: {
          color:  'rgba(36, 207, 241, 1)',
          borderRadius: [0, 7, 7, 0],
          borderWidth: 0,
        },
        barWidth: 15, // 柱状图的宽度
        barMinHeight: 10, // 柱状图的最小高度
      },
    ],
    

    在这里插入图片描述 在这里插入图片描述
    以上数据展示效果图:发现超大数据影响柱状图大小
    在这里插入图片描述

  2. 数据过大显示处理log(对数坐标轴)

    	xAxis: {
    ++    type: 'log',
    --    //type: 'value',
          show: true,
          axisLine: {
            show: false,
          },
        },
    
    数值坐标系改为:log对数坐标系
    啥是对数坐标轴:n=log(a)(b);看下图,这样我们就能理解为什么图可以展示全了
    

    在这里插入图片描述
    现在效果图
    在这里插入图片描述
    发现:鼠标移上去其他数据被淡出
    在这里插入图片描述
    处理:series->emphasis:disabled: true,

    series: [
      {
        name: '已认证企业',
        data: ['1', '2', '700', '10000', '80'],
        // data: data && [data[0]?.risk_network, data[0]?.risk_app, data[0]?.risk_attack, data[0]?.risk_data_leak, data[0]?.risk_mobile_app],
        type: 'bar',
        smooth: true,
        stack: 'total',
        emphasis: {
          focus: 'series',
    ++    disabled: true,
        },
        startValue: 100,
        lineStyle: {
          color: '#2878FF',
          width: 1,
        },
        itemStyle: {
          color: (params:any) => {
            if (params.value === 105.05) { // undefined的空数据处理 颜色灰色
              return '#ccc'
            }
            return 'rgba(15, 175, 255, 1)'
          },
          borderRadius: [7, 0, 0, 7],
          borderWidth: 0,
        },
        barWidth: 15,
        barMinHeight: 10,
      },
    
  3. 极坐标0数据处理
    根据对数坐标系n=log(a)(b) a>0;那么后台返回数据项如果有0的话,会显示如下
    在这里插入图片描述
    0项数据置为undefined

    这里的data是获取后台返回的接口数据,然后进行遍历操作
    const arr = data.map((item:any) => ({
        risk_network: item.risk_network || undefined,
        risk_app: item.risk_app || undefined,
        risk_attack: item.risk_attack || undefined,
        risk_data_leak: item.risk_data_leak || undefined,
        risk_mobile_app: item.risk_mobile_app || undefined,
      }))
      initContainer(arr)
    

    提示判断是否有undefined

     // 提示判断是否有undefined
     	 tooltip: {
          padding: 0,
          backgroundColor: 'rgba(255,255,255,0)',
          trigger: 'axis',
          axisPointer: {
            type: 'shadow',
            axis: 'y',
          },
          formatter(params:any) {
    	        let tipHtml = '';
    	        tipHtml = `${'<div style="box-sizing: border-box;box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.08), 0px 0px 12px rgba(0, 0, 0, 0.08);border-radius: 4px;width:160px;linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0) 100%);font-size: 9px;padding:1px 8px;">'
    	            + '<div style="width:86%;height:30px;line-height:30px;">'
    	            + '<span style="color:#181818;font-size:12px;">'}${params[0]?.axisValue}</span>`
    	            + '</div>'
    	            + '<div style="width: 144px;padding-bottom:4px">'
    	            + '<p style="color:#666666;border-radius: 2px;font-size:12px;line-height:30px;background: #FFFFFF;margin-bottom:6px;padding:0 6px">'
    	            + `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[0]?.color};margin:0 3px;"></span>`
    	            + `${params[0]?.seriesName}`
    	            // eslint-disable-next-line no-unsafe-optional-chaining
    	            + `<span style="color:#000;margin:0 6px;float: right;">${params[0]?.value === undefined ? 0 : params[0]?.value}</span>`
    	            + '</p>'
    	            + `<p style="color:#666666;border-radius: 2px;line-height:30px;background: #FFFFFF;font-size:12px; display:${params[1] ? 'block' : 'none'}">`
    	            + `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[1]?.color};margin:0 3px;"></span>`
    	            + `${params[1]?.seriesName}`
    	            // eslint-disable-next-line no-unsafe-optional-chaining
    	            + `<span style="color:#000;margin:0 6px;float: right;">${params[1]?.value === undefined ? 0 : params[1]?.value}</span>`
    	            + '</p>'
    	            + '</div>'
    	        return tipHtml;
    	      },
    
  4. 0数据项显示为灰色柱状条

    如果好几项为空,页面显示就会很空荡
    

    在这里插入图片描述

    处理:因为数据都是整数,所以当数据返回为0时,我们给他一个小数105.5;在设置颜色的时候,
    为其加一个灰色
    
    1、undefined改成105.5
    
    	const arr = data.map((item:any) => ({
    		    risk_network: item.risk_network || 105.05,
    		    risk_app: item.risk_app || 105.05,
    		    risk_attack: item.risk_attack || 105.05,
    		    risk_data_leak: item.risk_data_leak || 105.05,
    		    risk_mobile_app: item.risk_mobile_app || 105.05,
    		  }))
    		  initContainer(arr)
    
    2、tooltip->formatter中的undefined改成105.5
    	
    		${params[0]?.value === undefined ? 0 : params[0]?.value}
    
    3、series->itemStyle->color使用回调函数形式
    
     series: [
      {
        name: '已认证企业',
        data: ['0', '0', '0', '10000', '80'],
        // data: data && [data[0]?.risk_network, data[0]?.risk_app, data[0]?.risk_attack, data[0]?.risk_data_leak, data[0]?.risk_mobile_app],
        type: 'bar',
        smooth: true,
        stack: 'total',
        emphasis: {
          focus: 'series',
          disabled: true,
        },
        startValue: 100,
        lineStyle: {
          color: '#2878FF',
          width: 1,
        },
        itemStyle: {
    ++          color: (params:any) => {
    ++            if (params.value === 105.05) { // undefined的空数据处理 颜色灰色
     ++             return '#ccc'
     ++           }
      ++          return 'rgba(15, 175, 255, 1)'
     ++         },
          borderRadius: [7, 0, 0, 7],
          borderWidth: 0,
        },
        barWidth: 15,
        barMinHeight: 10,
      },
    

    在这里插入图片描述

  5. 窗口变化自适应

    只需要在执行函数时进行窗口监听视图大小变化,进而重绘就可以了
    resize()方法
    

    在这里插入图片描述

    import { ECharts } from 'echarts';
    const scatterChart = ref<ECharts | null>(null);
    onMounted(() => {
      window.onresize = () => {
        // console.log('视图大小变化')
        scatterChart.value?.resize();
      };
    })
    
  6. 侧边导航展开收起对echarts图的影响
    在这里插入图片描述

    当侧边栏有展开收起时,echarts图不会自适应,这个时候需要重绘我们的echarts图,
    我想的是既然窗口能监听,那么dom元素是否也可以监听
    

    https://developer.mozilla.org/zh-CN/docs/Web/API/ResizeObserver/observe在这里插入图片描述

    	<div
        id="riskTypeColumnar"
        ref="refriskTypeColumnar"
      />
      const refriskTypeColumnar = ref<HTMLElement |null>(null)
     	const resizeObserver = new ResizeObserver((entries:any) => {
    	  scatterChart.value?.resize();
    	});
    	onMounted(() => {
    ++   resizeObserver.observe(refriskTypeColumnar.value);
    	  window.onresize = () => {
    	    // console.log('视图大小变化')
    	    scatterChart.value?.resize();
    	  };
    	})
    	注意:需要给它父级dom设置宽度
    
  7. 代码

    	<template>
    	 <div
    	    style="position: absolute;
    	    left: 0px;
    	    top: 19.5px;
    	    font-weight: 500;
    	    font-size: 16px;
    	    line-height: 25px;
    	    color: #181818;"
    	  >
    	    风险分布
    	  </div>
    	  <div
    	    id="riskTypeColumnar"
    	    ref="refriskTypeColumnar"
    	  />
    	</template>
    
    <script setup lang="ts">
    import {
      ref, onUnmounted, markRaw, watch, onMounted,
    } from 'vue';
    import { ECharts } from 'echarts';
    import { overviewRiskDistribution } from '@/api/user';
    const scatterChart = ref<ECharts | null>(null);
    const refriskTypeColumnar = ref<HTMLElement |null>(null)
    const riskTotal = ref<any>(0);
    const assetsTotal = ref<any>(0);
    const getTableData = async () => {
      const params:any = {
        scope: 0,
      };
      const { code, msg, data } = await overviewRiskDistribution(params);
    
      if (scatterChart.value) {
        scatterChart.value?.dispose();
      }
      const arr = data.map((item:any) => ({
        risk_network: item.risk_network || 105.05,
        risk_app: item.risk_app || 105.05,
        risk_attack: item.risk_attack || 105.05,
        risk_data_leak: item.risk_data_leak || 105.05,
        risk_mobile_app: item.risk_mobile_app || 105.05,
        // risk_network: item.risk_network || undefined,
        // risk_app: item.risk_app || undefined,
        // risk_attack: item.risk_attack || undefined,
        // risk_data_leak: item.risk_data_leak || undefined,
        // risk_mobile_app: item.risk_mobile_app || undefined,
      }))
      initContainer(arr)
    };
    onUnmounted(() => {
      scatterChart.value?.dispose();
    });
    const initContainer = async (data:any) => {
      const echarts = await import('echarts');
      scatterChart.value = markRaw(echarts.init((document as any).getElementById('riskTypeColumnar')));
      renderMap(data)
    };
    const renderMap = (data:any) => {
      const option = {
        legend: {
          type: 'scroll',
          pageButtonItemGap: 2,
          itemWidth: 10,
          itemHeight: 6,
          itemGap: 30,
          icon: 'rect',
          data: [{
            name: '已认证企业',
            itemStyle: { color: '#0FAFFF' },
            icon: 'roundRect',
          }, {
            name: '三方供应商',
            itemStyle: { color: '#24CFF1' },
            icon: 'roundRect',
          }],
          pageTextStyle: {
            fontSize: 6,
          },
          bottom: 0,
        },
        itemStyle: {
          borderWidth: 2,
          borderColor: '#fff',
        },
        tooltip: {
          padding: 0,
          backgroundColor: 'rgba(255,255,255,0)',
          trigger: 'axis',
          axisPointer: {
            type: 'shadow',
            axis: 'y',
          },
          formatter(params:any) {
            let tipHtml = '';
            tipHtml = `${'<div style="box-sizing: border-box;box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.08), 0px 0px 12px rgba(0, 0, 0, 0.08);border-radius: 4px;width:160px;linear-gradient(180deg, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0) 100%);font-size: 9px;padding:1px 8px;">'
                + '<div style="width:86%;height:30px;line-height:30px;">'
                + '<span style="color:#181818;font-size:12px;">'}${params[0]?.axisValue}</span>`
                + '</div>'
                + '<div style="width: 144px;padding-bottom:4px">'
                + '<p style="color:#666666;border-radius: 2px;font-size:12px;line-height:30px;background: #FFFFFF;margin-bottom:6px;padding:0 6px">'
                + `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[0]?.color};margin:0 3px;"></span>`
                + `${params[0]?.seriesName}`
                // eslint-disable-next-line no-unsafe-optional-chaining
                + `<span style="color:#000;margin:0 6px;float: right;">${params[0]?.value === 105.05 ? 0 : params[0]?.value}</span>`
                + '</p>'
                + `<p style="color:#666666;border-radius: 2px;line-height:30px;background: #FFFFFF;font-size:12px; display:${params[1] ? 'block' : 'none'}">`
               + `<span style="width: 6px;height: 6px;display:inline-block;vertical-align: middle;border-radius: 6px; background:${params[1]?.color};margin:0 3px;"></span>`
                + `${params[1]?.seriesName}`
                // eslint-disable-next-line no-unsafe-optional-chaining
    	            + `<span style="color:#000;margin:0 6px;float: right;">${params[1]?.value === 105.05 ? 0 : params[1]?.value}</span>`
                + '</p>'
                + '</div>'
            return tipHtml;
          },
        },
        grid: {
          left: '3%',
          right: '4%',
          top: '0%',
          bottom: '10%',
          containLabel: true,
        },
        yAxis: {
          type: 'category',
          boundaryGap: true,
          show: true,
          axisLine: {
            lineStyle: {
              opacity: 0,
            },
          },
          axisTick: { show: false },
          data: ['网络风险', '应用风险', '攻击威胁', '数据泄漏', 'APP风险'],
        },
        xAxis: {
          type: 'log',
          // type: 'value',
          show: true,
          axisLine: {
            show: false,
          },
        },
        series: [
          {
            name: '已认证企业',
            data: data && [data[0]?.risk_network, data[0]?.risk_app, data[0]?.risk_attack, data[0]?.risk_data_leak, data[0]?.risk_mobile_app],
            type: 'bar',
            smooth: true,
            stack: 'total',
            emphasis: {
              focus: 'series',
              disabled: true,
            },
            startValue: 100,
            lineStyle: {
              color: '#2878FF',
              width: 1,
            },
            itemStyle: {
              color: (params:any) => {
                if (params.value === 105.05) { // undefined的空数据处理 颜色灰色
                  return '#ccc'
                }
                return 'rgba(15, 175, 255, 1)'
              },
              borderRadius: [7, 0, 0, 7],
              borderWidth: 0,
            },
            barWidth: 15,
            barMinHeight: 10,
          },
          {
            name: '三方供应商',
            // data: ['0', '0', '0', '200', '800000'],
            data: data && [data[1]?.risk_network, data[1]?.risk_app, data[1]?.risk_attack, data[1]?.risk_data_leak, data[1]?.risk_mobile_app],
            type: 'bar',
            stack: 'total',
            smooth: true,
            emphasis: {
              focus: 'series',
              disabled: true,
            },
            startValue: 10,
            // endValue: 10,
            lineStyle: {
              color: '#FF5A5A',
              width: 1,
            },
            itemStyle: {
              color: (params:any) => {
                if (params.value === 105.05) {
                  return '#ccc'
                }
                return 'rgba(36, 207, 241, 1)'
              },
              borderRadius: [0, 7, 7, 0],
              borderWidth: 0,
            },
            barWidth: 15, // 柱状图的宽度
            barMinHeight: 10, // 柱状图的最小高度
          },
        ],
      };
    
      scatterChart.value?.setOption(option as any, true);
    };
    const resizeObserver = new ResizeObserver((entries:any) => {
      scatterChart.value?.resize();
    });
    // initContainer()
    getTableData()
    defineExpose({
      initContainer,
    });
    onMounted(() => {
      console.log('refriskTypeColumnar.value', refriskTypeColumnar.value)
      resizeObserver.observe(refriskTypeColumnar.value);
      window.onresize = () => {
        // console.log('视图大小变化')
        scatterChart.value?.resize();
      };
    })
    </script>
    
    <style scoped>
    #riskTypeColumnar {
      width: 100%;
      margin-top: 55px;
      height: calc(100% - 55px);
      justify-content: center;
      position: relative;
    }
    </style>
    
  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值