封装echarts5组件

73 阅读1分钟
<template>
  <div class="chart-wrapper">
    <div class="chart-dom" ref="chartRef" />
    <div class="chart-overlay" v-if="isEmpty">
      <span>暂无数据</span>
    </div>
    <div class="chart-overlay" v-if="loading">
      <a-icon slot="indicator" type="loading" style="font-size: 24px" />
    </div>
  </div>
</template>

<script>
import * as echarts from 'echarts5/core'
// 引入柱状图图表,图表后缀都为 Chart
import { BarChart, LineChart, PieChart } from 'echarts5/charts'
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
  GridComponent,
  LegendComponent,
  TitleComponent,
  TooltipComponent,
  TransformComponent,
} from 'echarts5/components'
import { CanvasRenderer } from 'echarts5/renderers'
import debounce from 'lodash/debounce'
import { cloneDeep, merge } from 'lodash'
import graphic from 'echarts/lib/util/graphic'
// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  TransformComponent,
  CanvasRenderer,
  LegendComponent,
  PieChart,
  BarChart,
  LineChart
])
//十六进制颜色转换为rgba
const hexToRgba = (hex, opacity) => {
  let rgbaColor = "";
  let reg = /^#[\da-f]{6}$/i;
  if (reg.test(hex)) {
    rgbaColor = `rgba(${parseInt("0x" + hex.slice(1, 3))},${parseInt(
      "0x" + hex.slice(3, 5)
    )},${parseInt("0x" + hex.slice(5, 7))},${opacity})`;
  }
  return rgbaColor;
}
const grid = {
  left: 10,
  right: 20,
  top: 20,
  bottom: 2,
  containLabel: true
}
const legend = {
  show: false,
  left: 'right',
  itemWidth: 16,
  itemHeight: 2,
  selectedMode: false,
  icon: 'rect',
}
const xAxis = {
  type: 'category',
  boundaryGap: true,
  axisTick: {
    show: false,
  },
  axisLabel: {
    fontSize: 12,
    color:'#adadad',
  },
  axisLine: {
    show: false,
  },
  data: []
}

const tooltip = {
  trigger: 'axis',
  appendToBody: true,
  confine: true,
}

/**
 * 图表配色:option 传入以下配置
 * { palette: 'yh' }
 * { color: [color1, color2] }
 * 默认为 palette: 'year2'
 */
const palette = {
  yh: ['#5C8FF9','#F7A35D','#FFDB5C','#FF9D9A','#B9E1FF','#8CA5CD','#6DC8EC','#EDF0F4'],
  year2: ['#5C8FF9', '#e7e7e7'],
  year3: ['#5C8FF9', '#9AC5FF', '#e7e7e7']
}

const yAxis = {
  type: 'value',
  splitLine: {
    lineStyle: {
      color: '#f7f7f7',
    }
  },
}
export default {
  name: 'a-chart',
  props: {
    option: Object, // echartsOptions & { palette: idx }
    loading: Boolean,
  },
  data() {
    return {}
  },
  computed: {
    isEmpty() {
      return !this.option?.series || this.option?.series?.every(s => {return !s.data?.length})
    }
  },
  watch: {},
  mounted() {
    const resizeFunc = debounce(() => {
      if(!this.chart || this.chart._disposed) {
        return
      }
      this.chart.resize({
        animation: {
          duration: 300,
          easing: 'cubicInOut'
        }
      })
    }, 100)
    resizeFunc()
    const ob = new ResizeObserver((entries) => {
      resizeFunc()
    })
    ob.observe(this.$refs.chartRef)
    window.addEventListener('resize', resizeFunc)
    this.$on('beforeDestroy', () => {
      window.removeEventListener('resize', resizeFunc)
      ob.disconnect()
    })
    setTimeout(() => {
      this.renderChart()
    }, 100)
    this.$watch('option', () => {
      this.renderChart()
    })
  },
  beforeDestroy() {
    this.chart?.dispose()
  },
  methods: {
    renderChart() {
      if (!this.chart) {
        this.chart = echarts.init(this.$refs.chartRef)
      }
      this.chart.clear()
      this.chart.setOption(merge({}, {
        grid,
        xAxis,
        yAxis,
        tooltip,
        legend
      },this.parseOption(this.option)))
    },
    parseOption(option) {
      const color = option?.color ? option.color : option?.palette ? palette[option?.palette] : palette.year2
      const op = cloneDeep(option) || {}
      const {series} = op
      merge(op, {
        series: series?.map((item, idx) => {
          let { type, barWidth } = item || {}
          if (!type) {
            type = 'line'
          }
          const gradient = item.gradient //false: 折线图 ;  true: 面积图
          const thisColor = color[idx]
          return {
            ...item,
            type,
            z: 10 - idx,
            color: color[idx],
            ...(type === 'line' ? {
              symbol: 'none',
              areaStyle: {
                color: gradient === false ? 'transparent' : new graphic.LinearGradient(0, 0, 0, 1, [
                {offset: 0, color: hexToRgba(thisColor, 0.6)},
                {offset: 1, color: hexToRgba(thisColor, 0.1)},
                ]),
              },
              emphasis: {
                disabled: true,
              },
            } : {}),
            ...(type === 'bar' ? {
              barWidth: barWidth || 20,
              emphasis: {
                disabled: true,
              },
            } : {})
          }
        })
      })
      return op
    },
  }
}
</script>


<style scoped lang="scss">
.chart-wrapper {
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  position: relative;

  .chart-dom {
    height: 100%;
    width: 100%;
  }

  .chart-overlay {
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    border-radius: 4px;
    background: #fafafa;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #a9a9a9;
  }
}
</style>