vue3+ts+echarts组件封装

2,239 阅读2分钟

Snipaste_2022-03-22_22-15-50.png

//Components/VEcharts/echarts.ts 复制来自echarts官网
import * as echarts from 'echarts/core';
import {
  BarChart,
  // 系列类型的定义后缀都为 SeriesOption
  BarSeriesOption,
  LineChart,
  LineSeriesOption,
} from 'echarts/charts';
import { LegendComponent } from 'echarts/components';
import {
  TitleComponent,
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponent,
  TooltipComponentOption,
  GridComponent,
  GridComponentOption,
  // 数据集组件
  DatasetComponent,
  DatasetComponentOption,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent
} from 'echarts/components';
import { LabelLayout, UniversalTransition } from 'echarts/features';
import { CanvasRenderer } from 'echarts/renderers';

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
export type ECOption = echarts.ComposeOption<
  | BarSeriesOption
  | LineSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | DatasetComponentOption
>;

// 注册必须的组件
echarts.use([
  LegendComponent,
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  BarChart,
  LineChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer
]);
export default echarts
//Components/VEcharts/index.vue
<template>
  <div ref="echartsRef" class="echarts-container"></div>
</template>

<script setup lang="ts">
import echarts, { ECOption } from './echarts' //引入配置文件
let myChart: echarts.ECharts
const echartsRef = ref<HTMLElement>()

const props = defineProps({
  width: {
    type: String,
    default: '100%',
    required: true
  },
  height: {
    type: String,
    default: '100%',
    required: true
  },
  option: {
    type: Object,
    required: true,
    default: () => ({})
  }


})


const  debounce=(fn: Function, delay?: number)=> {
  let delays = delay || 500;
  let timer: NodeJS.Timeout|null
  return  (...rest:Array<any>)=> {
    if (timer) clearTimeout(timer);
    timer = setTimeout(function () {
      timer = null;
     fn.apply(null, rest);
    }, delays);
  };
}
let debounceResize:any


const init = (dom: HTMLElement) => {
  myChart = echarts.init(<HTMLElement>echartsRef.value)
  myChart.setOption(props.option)


}

onMounted(() => {
  if (echartsRef.value) {
    init(echartsRef.value)
    debounceResize=debounce(myChart.resize)
    window.addEventListener('resize',debounceResize)

  }

})
onBeforeUnmount(() => {
  if (myChart) myChart.dispose() //销毁
  window.removeEventListener('resize',debounceResize)

})

const getDom = () => {
  if (myChart) return myChart.getDom()
}
const setOption = (option: ECOption) => {
  if (myChart) myChart.setOption(option)
}
defineExpose({
  getDom,
  setOption

})
</script>

<style scoped>
.echarts-container {
  width: v-bind("props.width");
  height: v-bind("props.height");
}
</style>
//use 
<template>
  <div>
    <VEcharts width="200px" height="300px" :option="option" ref="vEchartsRef"/>
    <VEcharts width="200px" height="300px" :option="lineOption" ref="lineRef"/>
 

  </div>
</template>
<script setup lang="ts">
import {useBarConfig,useLineConfig,useAreaBarConfig} from './config'
const {vEchartsRef,option}=useBarConfig()
const {vEchartsRef:lineRef,option:lineOption}=useLineConfig()

</script>

<style scoped></style>


// src/views/charts/config/index.ts 批量导入的文件
export {useChartConfig as useBarConfig} from './useBarConfig'
export {useChartConfig as useLineConfig} from './useLineConfig'
export {useChartConfig as useAreaBarConfig} from './useAreaBarConfig'
//views/charts/config/useBarConfig.ts 条形图例子1 hooks写法
import {ECOption} from '@/common/components/VEcharts/echarts'
import VEcharts from '@/common/components/VEcharts/index.vue'
export const useChartConfig=()=>{
  const vEchartsRef=ref<InstanceType<typeof VEcharts>>()
  const option:ECOption ={
    title: {
     text: 'ECharts 入门示例'
   },
   tooltip: {},
   xAxis: {
     data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
   },
   yAxis: {},
   series: [
     {
       name: '销量',
       type: 'bar',
       data: [5, 20, 36, 10, 10, 20]
     }
   ]
 }


 
 onMounted(()=>{
  const r=vEchartsRef.value?.getDom()
  console.log(r);
  
  vEchartsRef.value?.setOption({title:{text:'aw'}})
  
 })
 
  return {vEchartsRef,option}


}
//src/views/charts/config/useLineConfig.ts 柱形图例子2 hooks写法
import {ECOption} from '@/common/components/VEcharts/echarts'
import VEcharts from '@/common/components/VEcharts/index.vue'

export const useChartConfig=()=>{
  const vEchartsRef=ref<InstanceType<typeof VEcharts>>()
  const option:ECOption ={
    tooltip:{},
    xAxis: {

      type: 'category',
      boundaryGap: false,
      data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
      type: 'value'
    },
    series: [
      {
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line',
        areaStyle: {}
      }
    ]
  };
  
 onMounted(()=>{
  const r=vEchartsRef.value?.getDom()
  console.log(r);

 })
 
  return {vEchartsRef,option}
}

另起一个话题

usehooks里面的option数据真实情况是后端返回的。于是结合pinia状态仓库来处理。

//src/store/charts/bar.ts
import { defineStore } from "pinia";
import {ECOption} from '@/common/components/VEcharts/echarts'

export const useBarStore=defineStore('bar',{
  state:()=>({
    option:<ECOption>{}


  }),
  getters:{},
  actions:{
    setOption(){
      //模拟网络请求
      setTimeout(() => {
        this.option={
          title: {
           text: 'ECharts 入门示例'
         },
         tooltip: {},
         xAxis: {
           data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
         },
         yAxis: {},
         series: [
           {
             name: '销量',
             type: 'bar',
             data: [5, 20, 36, 10, 10, 20]
           }
         ]
       }
        
      }, 2000);

    }
  }
})

import VEcharts from '@/common/components/VEcharts/index.vue'
import {useBarStore} from '@/store/charts/bar'
import {storeToRefs} from 'pinia'
export const useChartConfig=()=>{
  const vEchartsRef=ref<InstanceType<typeof VEcharts>>()
  const barStore=useBarStore()
  barStore.setOption() //获取数据请求
  const {option}=storeToRefs(barStore)
 onMounted(()=>{
  const r=vEchartsRef.value?.getDom()
 })
 watch(option,(newVal,oldVal)=>{
   vEchartsRef.value?.setOption(option.value) //watch监视option变换,一旦有变化图表数据重新赋值
 })
  
  return {vEchartsRef,option}


}

至此,数据从store提供->hooks接受数据->vue页面引入组件和hook进行渲染。之所以这样是实现试图(vue文件)-ts逻辑(hooks)-数据(pinia)分离。