首页 前端知识 使用 Vue_ElementUI_Echarts_Typescript 实现饼图统计【最新教程】

使用 Vue_ElementUI_Echarts_Typescript 实现饼图统计【最新教程】

2024-05-14 23:05:10 前端知识 前端哥 40 787 我要收藏

效果预览

在这里插入图片描述

组件调用示例:

<el-row class="block-margin--top" :gutter="20">
  <el-col :span="9">
    <div class="col-block col-block--card" style="height: 300px">
      <div class="col-block__title">告警类型分布</div>
      <div class="col-block__content col-block__content--full">
        <PieEcharts height="100%" :center-pie="['25%', '50%']" label-type="center" label-method="grossValue" :chartData="chartData"></PieEcharts>
      </div>
    </div>
  </el-col>
</el-row>

组件示例数据:

/** 饼图数据示例 */
private chartData = [
  {value: 22600, name: '侦察', suffix: '2.26万次 (7.05%)'},
  {value: 10400, name: '可疑', suffix: '1.04万次 (7.05%)'},
  {value: 43200, name: '拒绝服务', suffix: '2.26万次 (7.05%)'},
  {value: 5567, name: '恶意文件', suffix: '5567次 (7.05%)'},
  {value: 6918, name: 'WEB攻击', suffix: '6918次 (7.05%)'},
  {value: 6918, name: '远程控制', suffix: '2.26万次 (7.05%)'},
  {value: 6918, name: '恶意利用', suffix: '2.26万次 (7.05%)'},
  {value: 6918, name: 'APT攻击', suffix: '2.26万次 (7.05%)'},
  {value: 6918, name: '自定义威胁', suffix: '5401次 (7.05%)'},
]

具体代码实现:

Template 代码

<template>
  <div :style="{ height: height, width: width }">
    <div v-show="!showEmpty" v-loading="loading" :class="className" ref="myChart" :style="{ height: height, width: width }"></div>
    <el-empty v-show="showEmpty" style="padding: 0;height: calc(100% - 40px);" :description="emptyDesc" :image-size="100"></el-empty>
  </div>
</template>

Script 代码

<script lang="ts">
import Vue from 'vue';
import * as echarts from 'echarts/core';
import {Component, Emit, Prop, Watch} from 'vue-property-decorator';
import {common} from '@/utils/common';
import {nextTick} from 'vue/types/umd';
import {NdrModule} from '@/store/modules/Ndr';

@Component({})
export default class PieEcharts extends Vue {
  // 图表数据
  @Prop({
    default: () => {
    }
  }) readonly chartData?: any;
  // 图表样式名称
  @Prop({default: 'class-name'}) readonly className?: String;
  // 图表颜色
  @Prop({default: () => ['#3761ee', '#ff905d', '#f94565', '#e7bcf3', '#aa6dfe', '#8378ea', '#1c9dff', '#31c5e9', '#90c577']})
  readonly color?: any;
  // 图表内外半径
  @Prop({default: () => ['50%', '70%']}) readonly radius?: any;
  // 图表位置
  @Prop({default: () => ['36%', '50%']}) readonly centerPie?: any;
  // 图表宽
  @Prop({default: '100%'}) readonly width?: any;
  // 图表高
  @Prop({default: '80%'}) readonly height?: any;
  // 图表标题
  @Prop({default: '数据图表'}) readonly name?: any;
  // 右边子栏是否显示
  @Prop({default: true}) readonly legendShow?: any;
  // 右边子栏是否能点击
  @Prop({default: true}) readonly electedModeShow?: any;
  // 右边子栏位置
  @Prop({default: 'center'}) readonly legendTop?: any;
  // label描述显示位置 显示类型1.outside 2.
  @Prop({default: 'outside'}) readonly labelType?: any;
  // label描述是否显示
  @Prop({default: true}) readonly labelShow?: any;
  // label鼠标移入是否显示信息
  @Prop({default: true}) readonly emphasisEabelShow?: any;
  // label描述的几种显示形式
  @Prop({default: 'default'}) readonly labelMethod?: any;
  // 整体图表类型 类型1 default  //类型2progress
  @Prop({default: 'default'}) readonly typePie?: any;
  // 最小的扇区角度(0 ~ 360),用于防止某个值过小导致扇区太小影响交互
  @Prop({default: 0}) readonly minAngle?: any;
  // 是否显示组件自动计算的占比
  @Prop({default: false}) readonly tipsAppendAutoPercent?: any;
  // 是否显示数量单位
  @Prop({default: ''}) readonly tipsAppendAutoUnit?: string
  // 是否显示悬浮信息
  @Prop({default: true}) readonly showTooltip?: boolean
  /** Loading */
  protected loading: any = false;
  /** 图表是否显示暂无数据 */
  protected showEmpty = false
  /** 中间状态的描述文字 */
  protected emptyDesc: string = '';
  protected chartInstance: any = null;
  protected options: any = {};

  @Emit('typeClick')
  protected typeClick(data: any) {
    return data;
  }

  /***********************************************************************************************
   * 方法
   * *******************************************************************************************/

  @Watch('chartData')
  onChartDataChanged(newVal: any, oldVal: any) {
    this.showEmpty = Object.keys(this.chartData).length == 0
    this.initChart()
  }

  @Watch('loading')
  protected loadingChange(val: any) {
    this.loading = val;
    this.emptyDesc = val ? '数据加载中' : '暂无数据';
  }

  //初始化图标
  private initChart = () => {
    this.options = {
      color: this.color,
      tooltip: {
        show: this.showTooltip,
        formatter: (params: any) => {
          let ext = '';
          let unit = '';
          if (params.data.unit) {
            unit += params.data.unit;
          }
          if (this.tipsAppendAutoPercent) {
            ext += ` (${params.percent}%)`;
          }
          if (this.tipsAppendAutoUnit) {
            unit += this.tipsAppendAutoUnit;
          }
          let returnData = `
          <div style="padding: 5px 10px;">
            <span style="display:inline-block; width:10px; height:10px; margin-right:5px; border-radius:10px; background-color:${
              params.color
          }"></span>
            <span style="color:${params.color}">${params.name}</span>
            <span style="color:${params.color}">${common.FormatNumberUnit(params.data.value)}${unit}${ext}</span>
          </div>`;
          this.$emit('changeData', params);
          return returnData;
        }
      },
      legend: {
        right: '0%',
        top: this.legendTop,
        orient: '',
        show: this.legendShow,
        selectedMode: this.electedModeShow,
        formatter: (params: any) => {
          let newChartData = this.chartData.map((item: any) => ({name: item.name, suffix: item.suffix}));
          let name = '';
          for (let i = 0; i < newChartData.length; i++) {
            if (newChartData[i].suffix && newChartData[i].name == params) {
              return params + '——' + newChartData[i].suffix;
            } else {
              name = params;
            }
          }
          return params;
        }
      },
      series: [
        {
          name: this.name,
          type: 'pie',
          minAngle: this.minAngle,//最小角度
          radius: this.radius,
          center: this.centerPie,
          avoidLabelOverlap: true,
          labelLine: {
            show: this.labelShow,
            length: 10,
            length2: 15
          },
          label: {
            show: this.labelShow,
            position: this.labelType,
            formatter: (params: any) => {
              let newChartData = this.chartData.map((item: any) => ({
                value: item.value,
                name: item.name,
                suffix: item.suffix
              }));
              let num = 0;
              let ext = '';
              if (params.data.ext) {
                ext += params.data.ext;
              }
              for (let i in newChartData) {
                num += parseInt(newChartData[i].value);
              }
              let title = this.labelType === 'center' ? 'b' : 'c';
              let context = this.labelType === 'center' ? 'a' : 'd';
              // 固定总值显示,正常显示,自我后缀显示
              if (this.labelMethod == 'grossValue') {
                return `{${title}|${common.FormatNumberUnit(num)}}\n{${context}|总数}`;
              } else if (this.labelMethod == 'percent') {
                return `{${title}|${params.data.percent} %}`;
              } else if (this.labelMethod == 'default') {
                return `{${title}|${params.data.name}}\n{${context}|${common.FormatNumberUnit(params.data.value)}${ext}(${params.data.percent})}`;
              }
            },
            rich: {
              a: {
                color: '#666',
                lineHeight: 10
              },
              b: {
                fontSize: 20,
                fontWeight: 700,
                color: '#333',
                display: 'block',
                lineHeight: 20
              },
              c: {
                fontSize: 14,
                fontWeight: 700,
                color: '#333',
                display: 'block',
                lineHeight: 20
              },
              d: {
                fontSize: 12,
                fontWeight: 500,
                color: '#333',
                display: 'block',
                lineHeight: 12
              }
            }
          },
          emphasis: {
            label: {
              show: this.emphasisEabelShow,
              fontSize: '10',
              fontWeight: 'bold'
            }
          },
          z: 2,
          data: this.chartData
        }
      ]
    }
    let that = this;
    if (this.$refs.myChart) {
      this.chartInstance = echarts.init(<any>this.$refs.myChart);
    }
    this.chartInstance.setOption(this.options);
    that.chartInstance.on('click', function (params: any) {
      let typeData: any = params.data;
      if (typeData) {
        that.$emit('typeClick', typeData);
      }
    });
  };

  //更改图标样式
  private updateCharts = () => {
    if (this.typePie == 'progress') {
      let round = <any>{
        type: 'pie',
        radius: this.radius,
        center: this.centerPie,
        hoverAnimation: false,
        animation: false,
        itemStyle: {
          color: '#ff905d'
        },
        z: 0,
        label: {
          show: false
        },
        stillShowZeroSum: true,
        data: [0]
      };
      this.options.series.unshift(round);
    }
    this.initChart();
  };
  private screenAdapter = () => {
    this.initChart();
    this.chartInstance.resize();
  };

  protected async initData() {
    await this.initChart()
    await this.updateCharts();
  }

  async mounted() {
    await this.initData()
    this.showEmpty = Object.keys(this.chartData).length == 0
    window.addEventListener('resize', this.screenAdapter);
  }
}
</script>

SCSS 代码

.col-block {
  padding: 15px 25px;
  border-radius: 15px;
  overflow: hidden;
  &--inner-padding {
    padding: 15px 20px;
  }
  &.normal-gray-border {
    border: 1px solid $gray-dash-border-color;
    //box-shadow: none;
    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.03);
  }

  &--card {
    background-color: #fff;
    box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.1);
    .text-hover {
      cursor: pointer;
      &:hover {
        color: $blue-color;
      }
    }
  }

  &--no-padding {
    padding: 0;
    border-radius: 0;
  }

  &__title {
    margin-bottom: 5px;
    align-items: center;
    font-size: 16px;
    font-weight: bolder;
    display: flex;
    justify-content: space-between;
    &--normal-font {
      font-weight: normal;
    }
  }

  &__content {
    height: fit-content;
    margin-bottom: 5px;

    &--full {
      height: calc(100% - 10px);
    }

    &--full-b20 {
      height: calc(100% - 20px);
    }

    &--full-b30 {
      height: calc(100% - 30px);
    }

    &--left {
      float: left;

    }

    &--right {
      float: right;
    }

    &--between {
      display: flex;
      justify-content: space-between;
    }

    &__text {
      line-height: 55px;
      width: calc(100% - 55px);
      overflow: hidden;
      height: 55px;

      &__append-text {
        margin-left: 5px;
      }
    }

    &__icon {
      height: 55px;
      width: 55px;
      &--align-center {
        align-self: center;
      }
    }
  }

  &__content--pie-center {
    height: 100%; position: relative;
    .pie-center-title {
      position: absolute; text-align: center; top: 20px; width: 100%; z-index: 99;
      .big-font-count {
        font-size: 18px;
        font-weight: bold;
      }
    }
  }

  &__footer {
    margin-top: 10px;
    display: flex;
    justify-content: space-between;

    span {
      &:first-child {
        //margin-right: 15%;
      }

      display: inline-block;
    }
  }
}

总结:

至此,我们的组件编码就已经完成了,小伙伴们可以尝试着跑起来了。

本专栏会定期更新 Vue_ElementUI_Echarts_Typescript 有关的实例教程,希望感兴趣的小伙伴们多多关注~ 有问题在评论区留言吧~ 谢谢

转载请注明出处或者链接地址:https://www.qianduange.cn//article/8677.html
标签
评论
发布的文章

video 自定义视频播放控件

2024-05-26 01:05:25

HTML5 画布绘制海报

2024-05-26 01:05:13

HTML5学习(三)

2024-05-26 01:05:43

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!