Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)

项目中展示图表的地方很多,不想每次都写一长串的 options配置,就整合了一下常用的配置项,简单封装了一下,也能保证整个系统的图表风格统一,需要调整样式的时候也不用改很多地方

2022-11-07:legendData 改为数组,以支持多 legend的场景;添加 dataZoom组件

一、效果图(数据不足,示例图可能比较秃)

折线图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第1张图片 柱状图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第2张图片
散点图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第3张图片 饼图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第4张图片
环形图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第5张图片 嵌套环形图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第6张图片
仪表盘 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第7张图片 雷达图 Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第8张图片

二、代码

文件目录结构
Vue + Echarts(v5.版本)的简单组件封装(折线图、柱状图、散点图、饼/环形图、仪表盘、雷达图)_第9张图片

* mixin文件:Chart.js

/*
  echarts组件封装
  id:元素id ,唯一
  chartStyle:容器样式
  colors:颜色
  legendData:图例
  seriesData:所需数据
  xData:X轴显示的数据
  yData:Y轴显示的数据
*/
export default {
  props: {
    // 此处id切记不可传重复
    id: {
      type: String,
      default: '',
    },
    // 样式
    chartStyle: {
      type: Object,
      default: () => {},
    },
    // 标题
    titleData: {
      type: Object,
      default: () => {},
    },
    // 副标题
    subtitleData: {
      type: Object,
      default: () => {},
    },
    // 网格
    gridData: {
      type: Object,
      default: () => {},
    },
    // 颜色
    colors: {
      type: Array,
      default: () => [],
    },
    // 提示框
    tooltipData: {
      type: Object,
      default: () => {},
    },
    // 图例
    legendData: {
      type: Array,
      default: () => [],
    },
    // series
    seriesData: {
      type: Array,
      default: () => [],
    },
    // x 轴
    xData: {
      type: Array,
      default: () => [],
    },
    // x 轴样式
    xStyle: {
      type: Object,
      default: () => {},
    },
    // y 轴
    yData: {
      type: Array,
      default: () => [],
    },
    // 缩放
    dataZoomData: {
      type: Array,
      default: () => [],
    },
    // 是否有点击事件
    clickable: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      chartsDom: null,
      option: null,
      resizeTimer: null,
      tooltipConfig: {
        backgroundColor: 'rgba(0, 0, 0, 0.7)',
        borderColor: '#333',
        // borderRadius: 10,
        borderWidth: 0,
        shadow: false,
        textStyle: {
          color: '#FFFFFF',
          fontSize: 12,
        },
      },
    };
  },
  watch: {
    // 监听 series变化
    seriesData: {
      handler(newVal) {
        if (newVal) {
          this.$nextTick(() => {
            if (this.chartsDom) {
              // 先销毁,释放内存
              this.chartsDom.dispose();
            }
            this.init();
          });
        }
      },
      deep: true, // 对象内部属性的监听,关键。
    },
  },
  mounted() {
    // 部分暂时隐藏的 chart图,在显示的时候监听不到 seriesData的变化,需由 mounted来调用
    this.$nextTick(() => {
      if (this.chartsDom) {
        // 先销毁,释放内存
        this.chartsDom.dispose();
      }
      this.init();
    });
    window.addEventListener('resize', this.resize);
  },
  beforeDestroy() {
    // 解除监听
    window.removeEventListener('resize', this.resize);
    // 销毁 echart实例
    if (this.chartsDom) {
      this.chartsDom.dispose();
    }
  },
  methods: {
    // 尺寸变化自适应
    resize() {
      if (this.resizeTimer) { clearTimeout(this.resizeTimer); }
      this.resizeTimer = setTimeout(() => { this.chartsDom.resize(); }, 200);
    },
  },
};

>> 折线图:LineChart.vue

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart';

export default {
  mixins: [Chart],
  props: {},
  data() {
    return {
    };
  },
  methods: {
    init() {
      this.chartsDom = this.$echarts.init(document.getElementById(this.id));
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_');
      this.option = {
        grid: {
          left: 0,
          right: 20, // 设置 0的话最后一个x轴标签会被截断
          top: 35',
          bottom: (this.dataZoomData && this.dataZoomData.length) ? 50 : 0,
          containLabel: true,
          ...this.gridData,
        },
        tooltip: {
          trigger: 'axis',
          ...this.tooltipConfig,
          ...this.tooltipData,
        },
        legend: this.legendData.map((v) => ({
          top: 0,
          right: 0,
          icon: 'rect',
          itemWidth: 8,
          itemHeight: 8,
          itemGap: 12,
          textStyle: {
            fontSize: 12,
            color: '#455A74',
          },
          ...v,
        })),
        dataZoom: [...this.dataZoomData],
        color: this.colors,
        xAxis: [
          {
            type: 'category',
            boundaryGap: false,
            axisTick: { // 刻度点
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.45)',
              },
            },
            data: this.xData,
            axisLabel: {
              interval: 'auto',
              rotate: 0, // 旋转角度
              margin: 12, // 标签文字到轴的距离
              align: 'center',
              lineHeight: 17,
              color: '#455A74',
            },
            axisLine: {
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.45)',
              },
            },
            ...this.xStyle,
          },
        ],
        yAxis: this.yData.length === 0 ? [] : this.yData.map((item) => (
          {
            type: 'value',
            nameTextStyle: {
              color: '#455A74',
              align: 'left',
            },
            // min: 'dataMin',
            splitLine: {
              show: true,
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.15)',
                type: 'dashed',
              },
            },
            axisLine: {
              show: false,
            },
            axisTick: false,
            axisLabel: {
              color: '#455A74',
              // margin: 12,
            },
            ...item,
          }
        )),
        series: this.seriesData.map((item, index) => (
          {
            type: 'line',
            showSymbol: false,
            areaStyle: {
              color: {
                type: 'linear',
                x: 0,
                y: 0,
                x2: 0,
                y2: 1,
                colorStops: this.getColorStopsByColor(this.colors[index]),
                global: false, // 缺省为 false
              },
            },
            ...item,
          }
        )),
      };

      this.chartsDom.setOption(this.option);
      
      // 绑定点击事件
      this.chartsDom.on('click', (param) => {
        this.$emit('chartClick', param);
      });
    },
    getColorStopsByColor(color) {
      let color0;
      let color1;
      switch (color) {
        case '#169BFA':
          color0 = 'rgba(22, 155, 250, 0.3)';
          color1 = 'rgba(22, 155, 250, 0)';
          break;
        case '#31CF9A':
          color0 = 'rgba(49, 207, 154, 0.3)';
          color1 = 'rgba(49, 207, 154, 0)';
          break;
        case '#FBAD3B':
          color0 = 'rgba(251, 173, 59, 0.3)';
          color1 = 'rgba(251, 173, 59, 0)';
          break;
        default:
          color0 = 'rgba(255, 255, 255, 0)';
          color1 = 'rgba(255, 255, 255, 0)';
          break;
      }
      const colorStops = [{
        offset: 0, color: color0, // 0% 处的颜色
      }, {
        offset: 1, color: color1, // 100% 处的颜色
      }];
      return colorStops;
    },
  },
};
</script>

【使用示例】

<template>
  <LineChart id="myLine" v-bind="myLine" />
</template>

<script>
import LineChart from '@/components/Charts/LineChart';

export default {
  components: { LineChart },
  data() {
    return {
    	myLine: {
	        chartStyle: {
	          height: '220px',
	        },
	        colors: ['#169BFA', '#FBAD3B', '#31CF9A'],
	        legendData: [{ data: ['line1', 'line2', 'line3'] }],
	        xData: ['2022-07-01 10:00', '2022-07-01 10:30', '2022-07-01 11:00', '2022-07-01 11:30'],
	        xStyle: {
	        	axisLabel: {
			        formatter: (params) => params.substr(5),
		      	},
	        },
	        yData: [{ name: '单位:xx' }],
	        seriesData: [
	          { name: 'line1', data: [20, 50, 70, 100] },
	          { name: 'line2', data: [30, 60, 90, 40] },
	          { name: 'line3', data: [80, 40, 70, 20] },
	        ],
	      },
    };
  },
</script>

>> 柱状图:BarChart.vue(其实这里的柱状图和折线图都是二维坐标图,很多配置都是相似的,可以合并)

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart';

export default {
  mixins: [Chart],
  props: {},
  data() {
    return {
    };
  },
  methods: {
    init() {
      this.chartsDom = this.$echarts.init(document.getElementById(this.id));
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_');
      this.option = {
        grid: {
          left: 0,
          right: 20,
          top: 35,
          bottom: (this.dataZoomData && this.dataZoomData.length) ? 50 : 0,
          containLabel: true,
          ...this.gridData,
        },
        tooltip: {
          trigger: 'axis',
          ...this.tooltipConfig,
          ...this.tooltipData,
        },
        legend: this.legendData.map((v) => ({
          top: 0,
          right: 0,
          icon: 'rect',
          itemWidth: 8,
          itemHeight: 8,
          itemGap: 12,
          textStyle: {
            fontSize: 12,
            color: '#455A74',
          },
          ...v,
        })),
        dataZoom: [...this.dataZoomData],
        color: this.colors,
        xAxis: [
          {
            type: 'category',
            boundaryGap: true,
            axisTick: { // 刻度点
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.45)',
              },
            },
            data: this.xData,
            axisLabel: {
              interval: 'auto',
              rotate: 0, // 旋转角度
              margin: 12, // 标签文字到轴的距离
              align: 'center',
              lineHeight: 17,
              color: '#455A74',
            },
            axisLine: {
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.45)',
              },
            },
            ...this.xStyle,
          },
        ],
        yAxis: this.yData.length === 0 ? [] : this.yData.map((item) => (
          {
            type: 'value',
            nameTextStyle: {
              color: '#455A74',
            },
            splitLine: {
              show: true,
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.15)',
                type: 'dashed',
              },
            },
            axisLine: {
              show: false,
            },
            axisTick: false,
            axisLabel: {
              color: '#455A74',
              // margin: 12,
            },
            ...item,
          }
        )),
        series: this.seriesData.map((item) => (
          {
            barWidth: 16,
            type: 'bar',
            ...item,
          }
        )),
      };

      this.chartsDom.setOption(this.option);
      
      // 绑定点击事件
      this.chartsDom.on('click', (param) => {
        this.$emit('chartClick', param);
      });
    },
  },
};
</script>

【使用示例】
(与折线图类似)

>> 散点图:ScatterChart.vue(其实与折线图、柱状图的配置差不多,只是数据格式略有差异)

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart';

export default {
  mixins: [Chart],
  props: {},
  data() {
    return {
    };
  },
  methods: {
    init() {
      this.chartsDom = this.$echarts.init(document.getElementById(this.id));
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_');
      this.option = {
        grid: {
          left: 0,
          right: 80, // 设置 0的话最后一个x轴标签会被截断
          top: 35,
          bottom: 0,
          containLabel: true,
          ...this.gridData,
        },
        tooltip: {
          trigger: 'axis',
          ...this.tooltipConfig,
          ...this.tooltipData,
        },
        legend: this.legendData.map((v) => ({
          top: 0,
          right: 0,
          icon: 'rect',
          itemWidth: 8,
          itemHeight: 8,
          itemGap: 12,
          textStyle: {
            fontSize: 12,
            color: '#455A74',
          },
          ...v,
        })),
        color: this.colors,
        xAxis: [
          {
            type: 'value',
            min: 'dataMin',
            boundaryGap: false,
            axisTick: { // 刻度点
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.45)',
              },
            },
            axisLabel: {
              interval: 'auto',
              rotate: 0, // 旋转角度
              margin: 12, // 标签文字到轴的距离
              align: 'center',
              lineHeight: 17,
              color: '#455A74',
            },
            axisLine: {
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.45)',
              },
            },
            ...this.xStyle,
          },
        ],
        yAxis: this.yData.length === 0 ? [] : this.yData.map((item) => (
          {
            type: 'value',
            min: 'dataMin',
            nameTextStyle: {
              color: '#455A74',
              align: 'left',
            },
            splitLine: {
              show: true,
              lineStyle: {
                color: 'rgba(45, 62, 83, 0.15)',
                type: 'dashed',
              },
            },
            axisLine: {
              show: false,
            },
            axisTick: false,
            axisLabel: {
              color: '#455A74',
            },
            ...item,
          }
        )),
        series: this.seriesData.map((item, index) => (
          {
            type: 'scatter',
            ...item,
          }
        )),
      };

      this.chartsDom.setOption(this.option);
      
      // 绑定点击事件
      this.chartsDom.on('click', (param) => {
        this.$emit('chartClick', param);
      });
    },
  },
};
</script>

【使用示例】

<template>
  <ScatterChart id="myScatter" v-bind="myScatter" />
</template>

<script>
import ScatterChart from '@/components/Charts/ScatterChart';

export default {
  components: { ScatterChart },
  data() {
    return {
    	myScatter: {
	        chartStyle: {
	          height: '220px',
	        },
	        colors: ['#169BFA', '#31CF9A'],
	        legendData: [{ data: ['scatter1', 'scatte2'] }],
	        xStyle: { name: '单位:xx' },
	        yData: [{ name: '单位:yy' }],
	        seriesData: [
	          {
	          	name: 'scatter1',
	          	data: [
	              [174, 65.6],
	              [175.3, 71.8],
	              [193.5, 80.7],
	              [186.5, 72.6],
	              [187.2, 78.8],
	              [181.5, 74.8],
	              [184, 86.4],
	              [184.5, 78.4],
	              [175, 62],
	              [184, 81.6],
	              [180, 76.6],
	              [177.8, 83.6],
	          	],
	          },
	          {
	          	name: 'scatter2',
	          	data: [
	              [175, 62],
	              [184, 81.6],
	              [180, 76.6],
	              [177.8, 83.6],
	              [192, 90],
	              [176, 74.6],
	              [174, 71],
	              [184, 79.6],
	              [192.7, 93.8],
	              [171.5, 70],
	              [173, 72.4],
	              [176, 85.9],
	              [176, 78.8],
	              [180.5, 77.8],
	              [172.7, 66.2],
	              [176, 86.4],
	          	],
	          },
	        ],
	      },
    };
  },
</script>

>> 饼图/环形图/嵌套环形图:PieChart.vue(环形图都是饼图演变来的)

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart';

export default {
  mixins: [Chart],
  props: {
    // 提示框
    tooltipData: {
      type: Object,
      default() {
        return {
          formatter: '{b}: {c} ({d}%)',
        };
      },
    },
    // 是否是环形
    isRing: {
      type: Boolean,
      default: false,
    },
    // 是否显示延长线标签
    hasPieLabel: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
    };
  },
  methods: {
    init() {
      this.chartsDom = this.$echarts.init(document.getElementById(this.id));
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_');
      this.option = {
        title: {
          subtextStyle: {
            color: '#2D3E53',
            fontSize: 24,
            lineHeight: 32,
            fontWeight: 500,
          },
          textStyle: {
            color: '#7B93A7',
            fontSize: 12,
            lineHeight: 16,
          },
          left: 'center',
          top: 60,
          itemGap: 2,
          ...this.titleData,
        },
        grid: {
          ...this.gridData,
        },
        tooltip: {
          ...this.tooltipConfig,
          ...this.tooltipData,
        },
        legend: this.legendData.map((v) => ({
          top: 0,
          right: 0,
          icon: 'rect',
          itemWidth: 8,
          itemHeight: 8,
          itemGap: 12,
          textStyle: {
            fontSize: 12,
            color: '#455A74',
          },
          ...v,
        })),
        color: this.colors,
        series: [],
      };
      // 处理 series
      this.seriesData.forEach((item) => {
        const { data = [] } = item;
        const dataTemp = data.map((v) => ({ ...v, value: v.value || '' }));
        const serie = {
          type: 'pie',
          minAngle: 5,
          radius: this.isRing ? ['50', '70'] : ['0', '70'],
          center: ['50%', '92'],
          label: {
            show: this.hasPieLabel,
            formatter: '{b} {c} ({d}%)',
            fontSize: 12,
            color: '#455A74',
          },
          itemStyle: {
            borderColor: '#FFFFFF',
            borderWidth: 1,
          },
          ...item,
          data: dataTemp,
        };
        this.option.series.push(serie);
      });
      this.chartsDom.setOption(this.option);
    },
  },
};
</script>

【使用示例】

  • 饼图
<template>
  <PieChart id="myPie" v-bind="myPie" />
</template>

<script>
import PieChart from '@/components/Charts/PieChart';

export default {
  components: { PieChart },
  data() {
    return {
    	myPie: {
	        chartStyle: {
	          height: '220px',
	        },
	        colors: ['#169BFA', '#FBAD3B', '#31CF9A'],
	        legendData: [{ data: ['pie1', 'pie2', 'pie3'] }],
	        seriesData: [{
	          data: [
	            { name: 'pie1', value: 5 },
	            { name: 'pie2', value: 10 },
	            { name: 'pie3', value: 15 },
	          ],
	        }],
	     },
    };
  },
</script>
  • 环形图
<template>
  <PieChart id="myPie" v-bind="myPie" />
</template>

<script>
import PieChart from '@/components/Charts/PieChart';

export default {
  components: { PieChart },
  data() {
    return {
    	myPie: {
    		isRing: true,
	        chartStyle: {
	          height: '220px',
	        },
	        titleData: {
	          subtext: '100',
	          text: '标题',
	        },
	        colors: ['#169BFA', '#FBAD3B', '#31CF9A'],
	        legendData: [{ data: ['pie1', 'pie2', 'pie3'] }],
	        seriesData: [{
	          data: [
	            { name: 'pie1', value: 5 },
	            { name: 'pie2', value: 10 },
	            { name: 'pie3', value: 15 },
	          ],
	        }],
        	hasPieLabel: false,
	      },
    };
  },
</script>
  • 嵌套环形图
<template>
  <PieChart id="myPie" v-bind="myPie" />
</template>

<script>
import PieChart from '@/components/Charts/PieChart';

export default {
  components: { PieChart },
  data() {
    return {
    	myPie: {
    		isRing: true,
	        chartStyle: {
	          height: '220px',
	        },
	        colors: ['#169BFA', '#FBAD3B', '#31CF9A'],
	        legendData: [{ data: ['pie1', 'pie2', 'pie3'] }],
	        seriesData: [
	          { name: 'pie1', radius: [45, 50], center: ['50%', 60], 
	          	data: [
	          		{ name: 'pie1', value: 60 },
	          		{ value: 100 - 60, itemStyle: { color: '#F6F6F6' }, emphasis: { disabled: true } }
	          	],
	          },
	          { name: 'pie2', radius: [35, 40], center: ['50%', 60],
	          	data: [
	          		{ name: 'pie1', value: 40 },
	          		{ value: 100 - 40, itemStyle: { color: '#F6F6F6' }, emphasis: { disabled: true } }
	          	],
	          },
	          { name: 'pie3', radius: [25, 30], center: ['50%', 60],
	          	data: [
	          		{ name: 'pie1', value: 80 },
	          		{ value: 100 - 80, itemStyle: { color: '#F6F6F6' }, emphasis: { disabled: true } }
	          	],
	          },
	        ],
	        hasPieLabel: false,
	      },
    };
  },
</script>

>> 自定义仪表盘:GaugeChart.vue(如果是v4.版本的 Echarts,这种样式用原始的 gauge做不出来,需要加极坐标之类的做改造,见下)

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart';

export default {
  mixins: [Chart],
  props: {
    // 进度条
    progressData: {
      type: Object,
      dedault: () => {},
    },
  },
  data() {
    return {
    };
  },
  methods: {
    init() {
      this.chartsDom = this.$echarts.init(document.getElementById(this.id));
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_');
      this.option = {
        series: [
          {
            type: 'gauge',
            center: ['50%', '60%'],
            progress: {
              show: true,
              width: 10,
              roundCap: true,
              itemStyle: {
                // color: 'red' // 进度条颜色
              },
              ...this.progressData,
            },
            axisLine: {
              roundCap: true,
              lineStyle: {
                width: 10,
                color: [[1, '#EBEBEB']],
              },
            },
            splitNumber: 4, // 分割段数
            splitLine: { // 分割线样式
              distance: -24,
              length: 5,
              lineStyle: {
                width: 2,
                color: '#A0A0A0',
              },
            },
            axisTick: { // 刻度样式
              distance: -21,
              length: 3,
              lineStyle: {
                width: 1,
                color: '#A0A0A0',
              },
            },
            axisLabel: { // 刻度文字样式
              distance: -10,
              color: '#455A74',
              fontSize: 10,
            },
            anchor: { // 指针固定点
              show: false,
            },
            pointer: { // 指针
              show: false,
            },
            title: { // 文字样式
              fontSize: 18,
              offsetCenter: [0, '80%'],
              // color: 'red'
              ...this.titleData,
            },
            detail: { // 数值样式
              valueAnimation: true,
              fontSize: 24,
              offsetCenter: [0, 0],
              formatter: (value) => `${value}%`,
            },
            data: this.seriesData.map((item) => (
              {
                ...item,
              }
            )),
          },
        ],
      };
      this.chartsDom.setOption(this.option);
    },
  },
};
</script>

【使用示例】

<template>
  <GaugeChart id="myGauge" v-bind="myGauge" />
</template>

<script>
import GaugeChart from '@/components/Charts/GaugeChart';

export default {
  components: { GaugeChart },
  data() {
    return {
    	myGauge: {
	        chartStyle: {
	          width: '180px',
	          height: '180px',
	        },
	        progressData: {
	          itemStyle: {
	            color: '#59D38C',
	          }
	        },
			seriesData: [{
	          name: '优',
	          value: 80,
	        }],
	      },
    };
  },
</script>

v4.版本实现

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart'

export default {
  mixins: [Chart],
  props: {
    // 区间颜色
    grade: {
      type: Object,
      default () {
        return {}
      }
    }
  },
  methods: {
    init () {
      this.chartsDom = this.$echarts.init(document.getElementById(this.id))
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_')
      const seriesValue = (this.seriesData && this.seriesData.length) ? this.seriesData[0]?.data : 0
      const option = {
        tooltip: {
          ...this.tooltipConfig,
          ...this.tooltipData
        },
        angleAxis: {
          show: false,
          max: (100 * 360) / 270, // -45度到225度,二者偏移值是270度除360度
          type: 'value',
          startAngle: 225 // 极坐标初始角度
        },
        barMaxWidth: 8, // 圆环宽度
        radiusAxis: {
          show: false,
          type: 'category'
        },
        // 极坐标系 圆环位置和大小
        polar: {
          center: ['50%', 105],
          radius: 130
        },
        series: [
          // 下层圆环,显示最大值 // 写在上层配置前,如果写在后面,需要设置 z属性(相当于z-index)来区分层级
          {
            type: 'bar',
            name: '当日负荷均值',
            data: [
              {
                value: 100, // 最大值
                itemStyle: {
                  color: '#eee'
                }
              }
            ],
            barGap: '-100%', // 柱间距离,上下两层圆环重合
            coordinateSystem: 'polar', // 对应极坐标系
            roundCap: true // 两端圆角
          },
          // 上层圆环,显示数据
          {
            type: 'bar',
            data: [
              {
                value: seriesValue,
                itemStyle: {
                  color: this.grade.color
                }
              }
            ],
            barGap: '-100%',
            coordinateSystem: 'polar',
            roundCap: true
          },
          // 仪表盘
          {
            type: 'gauge',
            center: ['50%', 105],
            radius: 80,
            detail: { // 补充文字
              formatter: this.grade.text,
              offsetCenter: [0, 18],
              fontSize: 18,
              color: this.grade.color
            },
            data: [
              {
                name: `${seriesValue}%` // 对应 title的设置
              }
            ],
            title: { // 居中文字
              color: '#455A74',
              fontSize: 28,
              offsetCenter: [0, -12]
            },
            min: 0,
            max: 100,
            splitNumber: 4, // 分割段数
            pointer: {
              show: false
            },
            splitLine: { // 分割线样式
              length: 5,
              lineStyle: {
                width: 1,
                color: '#ccc'
              }
            },
            axisTick: { // 刻度样式
              length: 2,
              lineStyle: {
                color: '#ccc'
              }
            },
            axisLine: { // 刻度盘样式
              show: false
            },
            axisLabel: { // 刻度标签
              distance: -25, // 设置为负数,在刻度线外侧
              fontSize: 12,
              color: '#455A74',
              formatter: (value) => {
                // 显示 优良中差
                switch (String(value)) {
                  case '0':
                    return '0'
                  case '25':
                    return '25'
                  case '50':
                    return '50'
                  case '75':
                    return '75'
                  case '100':
                    return '100'
                  default:
                    return ''
                }
              }
            }
          }
        ]
      }
      this.chartsDom.setOption(option)
    }
  }
}
</script>

【使用示例】

<template>
  <GaugeChart id="myGauge" v-bind="myGauge" />
</template>

<script>
import GaugeChart from '@/components/Charts/GaugeChart';

export default {
  components: { GaugeChart },
  data() {
    return {
    	myGauge: {
	        chartStyle: {
	          height: '180px',
	        },
	        seriesData: [{
	          name: 'xxx',
	          value: 80,
	        }],
	        grade: {
	          color: '#31CF9A',
	          text: '优',
	        }
	      },
    };
  },
</script>

>> 雷达图

<template>
  <div :id="id" :style="chartStyle"></div>
</template>

<script>
import Chart from './mixins/Chart';

export default {
  mixins: [Chart],
  props: {},
  data() {
    return {
    };
  },
  methods: {
    init() {
      // 初始化
      this.chartsDom = this.$echarts.init(document.getElementById(this.id));
      // 每次数据更新都清空一次实例,以便页面重新渲染,否则更新数据没有变化,页面看起来像没动
      document.getElementById(this.id).removeAttribute('_echarts_instance_');
      // 处理 indicator
      let indicator = [];
      if (this.seriesData && this.seriesData.length > 0) {
        // 防止数据缺失
        const validData = this.seriesData.find(
          (item) => (item.data && item.data.length > 0),
        );
        indicator = validData ? validData.data.map(
          (item) => ({ name: item.name, min: 0, max: 1 }),
        ) : [{}, {}, {}, {}, {}, {}];
      } else {
        for (let i = 0; i < 6; i += 1) {
          indicator.push(
            { name: '', min: 0, max: 1 },
          );
        }
      }
      this.option = {
        tooltip: {
          trigger: 'item',
          ...this.tooltipConfig,
          ...this.tooltipData,
        },
        legend: this.legendData.map((v) => ({
          bottom: 0,
          right: 20,
          icon: 'rect',
          itemStyle: {
            borderColor: '#FFFFFF',
            borderWidth: 12,
          },
          inactiveBorderColor: '#FFFFFF',
          ...v,
        })),
        radar: {
          indicator,
          center: ['50%', '115'],
          radius: 85,
          splitNumber: 10,
          splitArea: {
            areaStyle: {
              color: [
                'rgba(255, 95, 95, 0.15)',
                'rgba(255, 95, 95, 0.15)',
                'rgba(255, 95, 95, 0.15)',
                'rgba(255, 95, 95, 0.15)',
                'rgba(255, 95, 95, 0.15)',
                'rgba(255, 95, 95, 0.15)',
                'rgba(255, 209, 25, 0.15)',
                'rgba(255, 209, 25, 0.15)',
                'rgba(255, 172, 76, 0.15)',
                'rgba(49, 207, 154, 0.15)',
              ],
            },
          },
          splitLine: {
            lineStyle: {
              color: [
                'rgba(0, 0, 0, 0)',
                'rgba(0, 0, 0, 0)',
                'rgba(0, 0, 0, 0)',
                'rgba(0, 0, 0, 0)',
                'rgba(0, 0, 0, 0)',
                'rgba(0, 0, 0, 0)',
                'rgba(45, 62, 83, 0.15)',
                'rgba(0, 0, 0, 0)',
                'rgba(45, 62, 83, 0.15)',
                'rgba(45, 62, 83, 0.15)',
                'rgba(45, 62, 83, 0.15)',
              ],
            },
          },
          axisLine: {
            lineStyle: {
              color: 'rgba(45, 62, 83, 0.15)',
            },
          },
          name: {
            fontSize: 12,
            color: '#455A74',
            /* width: 50,
            overflow: 'breakAll', */
            formatter: (params) => {
              let text = params;
              if (text !== indicator[0].name && text !== indicator[3].name && text.length > 8) {
                const splitIndex = Math.ceil(text.length / 2);
                text = `${params.substr(0, splitIndex)}\n${params.substr(splitIndex)}`;
              }
              return text;
            },
          },
        },
        series: [],
      };
      // 处理 series
      this.seriesData.forEach((item) => {
        this.option.series = [
          ...this.option.series,
          ...this.handleSeries(item),
        ];
        // if (item.data && item.data.length > 0) {
        // }
      });
      this.chartsDom.setOption(this.option);
    },
    handleSeries(serie) {
      const { name, color, data } = serie;
      const dataList = data.map((item, index) => {
        const arr = new Array(data.length);
        arr.splice(index, 1, item);
        return arr;
      });
      return [data, ...dataList].map((item, index) => {
        return {
          type: 'radar',
          name,
          symbol: index === 0 ? 'circle' : 'none',
          symbolSize: 6,
          itemStyle: {
            color,
            borderColor: '#FFFFFF',
            borderWidth: 0.5,
          },
          lineStyle: {
            color: index === 0 ? color : 'transparent',
          },
          z: index === 0 ? 1 : 2,
          data: [item],
        };
      });
    },
  },
};
</script>

【使用示例】

<template>
  <RadarChart id="myRadar" v-bind="myRadar" />
</template>

<script>
import RadarChart from '@/components/Charts/RadarChart';

export default {
  components: { RadarChart },
  data() {
    return {
    	myRadar: {
	        chartStyle: {
	          height: '220px',
	        },
	        colors: ['#31CF9A', '#FFAC4C'],
	        legendData: [{ data: ['radar1', 'radar2'] }],
	        seriesData: [
	          { name: 'radar1', color: '#31CF9A', data: [0.80.910.90.9] },
	          { name: 'radar2', color: '#FFAC4C', data: [0.4, 0.5, 0.6, 0.5, 0.6] },
	        ],
	      },
    };
  },
</script>

你可能感兴趣的:(Echarts,echarts,vue.js,javascript)