vue3按需引入Echart

229 阅读3分钟

echarts.ts

// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
import * as echarts from 'echarts/core'

/** 引入柱状图and折线图图表,图表后缀都为 Chart  */
import { BarChart, LineChart } from 'echarts/charts'

// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  // 数据集组件
  DatasetComponent,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
  LegendComponent
} from 'echarts/components'

// 标签自动布局,全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features'

// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from 'echarts/renderers'

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DatasetComponent,
  TransformComponent,
  LegendComponent,
  BarChart,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  LineChart
])

// 导出
export default echarts

main.ts

import echarts from '@/utils/echarts'
import App from './App.vue'

const app = createApp(App)
app.config.globalProperties.$echarts = echarts

组件中使用

import { getCurrentInstance } from 'vue'

const { proxy }: any = getCurrentInstance()
myChart.value = markRaw(proxy.$echarts.init(container.value))

举例echarts组件

<template>
  <div class="echart-main">
    <div class="echart-nav">
      <div class="echart-title">
        <strong>{{ title }}</strong>
      </div>
      <slot name="title" />
    </div>
    <div v-if="props.isError" class="moment">指标采集失败,请刷新或联系管理员排查</div>
    <div v-if="!props.isError && props.chartData.length === 0" class="moment">暂无数据</div>
    <div class="echart" ref="container" v-if="props.chartData.length > 0" />
  </div>
</template>
<script setup lang="ts">
import {
  ref,
  computed,
  getCurrentInstance,
  onMounted,
  onBeforeUnmount,
  watch,
  nextTick,
  markRaw,
  onUnmounted
} from 'vue'
import { useConfigStore } from '@/stores/config'
type ChartDataType = { name: string; xAxis: string; yAxis: string | number; [key: string]: any }
const props = defineProps({
  title: {
    type: String,
    default: '',
    required: true
  },
  yAxisName: {
    type: String,
    default: ''
  },
  chartData: {
    type: Array,
    default: () => [
      { key: 'item1', xAxis: '2023/9/4/ 01:00', yAxis: 12 },
      { key: 'item1', xAxis: '2023/9/4/ 02:00', yAxis: 11 },
      { key: 'item1', xAxis: '2023/9/4/ 03:00', yAxis: 1 },
      { key: 'item1', xAxis: '2023/9/4/ 04:00', yAxis: 12 },
      { key: 'item1', xAxis: '2023/9/4/ 05:00', yAxis: 16 },
      { key: 'item2', xAxis: '2023/9/4/ 01:00', yAxis: 14 },
      { key: 'item2', xAxis: '2023/9/4/ 02:00', yAxis: 32 },
      { key: 'item2', xAxis: '2023/9/4/ 03:00', yAxis: 62 },
      { key: 'item2', xAxis: '2023/9/4/ 04:00', yAxis: 22 },
      { key: 'item2', xAxis: '2023/9/4/ 05:00', yAxis: 12 }
    ]
  },
  idStr: {
    type: String,
    required: true
  },
  isError: {
    type: Boolean,
    default: false
  },
  spanValue: {
    type: Number,
    default: 12
  }
})
const container = ref()
const { proxy }: any = getCurrentInstance()
const myChart = ref<any>(null)
const resize = () => {
  if (myChart.value && myChart.value.resize) {
    myChart.value?.resize()
  }
}
onMounted(() => {
  window.addEventListener('resize', resize)
})
onBeforeUnmount(() => {
  window.removeEventListener('resize', resize)
})
onUnmounted(() => {
  myChart.value && myChart.value.dispose()
  myChart.value = null
})

const configStore = useConfigStore()
const leftWidth = computed(() => {
  return configStore.leftWidth + 'px'
})
watch(
  [() => leftWidth.value, () => props.spanValue],
  () => {
    nextTick(() => {
      resize()
    })
  },
  { deep: true }
)
const initEcharts = async () => {
  const _rawData: ChartDataType[] = props.chartData as ChartDataType[]
  if (_rawData && Array.isArray(_rawData) && _rawData.length > 0) {
    let seriesData: any = []
    let dataKey: any = []
    const xAxis: string[] = [...new Set(_rawData.map(i => i.xAxis))]
    _rawData.forEach((v: ChartDataType) => {
      if (!dataKey.includes(v.key)) {
        dataKey = [...dataKey, v.key]
      }
    })
    dataKey.sort()
    dataKey.forEach((k: string) => {
      seriesData = [
        ...seriesData,
        {
          name: k,
          type: 'line',
          smooth: true,
          showSymbol: false,
          data: _rawData.filter((i: ChartDataType) => i.key === k).map((d: ChartDataType) => d.yAxis)
        }
      ]
    })
    const option = {
      legend: {
        data: dataKey,
        type: 'scroll',
        bottom: 20,
        icon: 'circle',
        itemWidth: 6
      },
      grid: {
        right: 20,
        left: 60,
        bottom: 70,
        top: 20
      },
      tooltip: {
        show: true,
        trigger: 'axis'
      },
      xAxis: {
        type: 'category',
        nameLocation: 'middle',
        splitLine: {
          show: true,
          lineStyle: {
            type: 'solid',
            color: '#f2f2f2',
            width: 1
          }
        },
        axisLine: {
          show: true,
          lineStyle: {
            color: '#e4e7ed'
          }
        },
        axisTick: {
          show: true,
          lineStyle: {
            color: '#f2f2f2'
          }
        },
        axisLabel: {
          color: '#556677',
          fontSize: 12
        },
        data: xAxis
      },
      yAxis: {
        // name: props.yAxisName.length > 5 ? '' : props.yAxisName,
        // nameTextStyle: {
        //   padding: [0, 80, 0, 0],
        //   color: '#262626'
        // },
        // nameGap: -6,
        // nameLocation: 'end',
        type: 'value',
        splitLine: {
          show: true,
          lineStyle: {
            color: '#f5f5f5',
            width: 1,
            type: 'solid'
          }
        },
        axisLine: {
          show: true,
          lineStyle: {
            color: '#e4e7ed'
          }
        },
        axisTick: {
          show: true,
          lineStyle: {
            color: '#f2f2f2'
          }
        },
        axisLabel: {
          color: '#556677',
          width: 50,
          overflow: 'truncate'
        }
      },
      series: seriesData
    }
    nextTick(() => {
      if (myChart.value) {
        myChart.value.dispose()
        myChart.value = null
      }
      myChart.value = markRaw(proxy.$echarts.init(container.value))

      myChart.value.setOption(option, true)
    })
  }
}
watch(
  () => props.chartData,
  () => {
    initEcharts()
  },
  { deep: true, immediate: true }
)

defineExpose({ initEcharts })
</script>
<style scoped lang="scss">
.echart-main {
  width: 100%;
  height: 270px;
  min-width: 200px;
  background: #fff;
  border-radius: 8px;
  box-shadow:
    0 2px 4px 0 rgb(0 0 0 / 1%),
    0 3px 6px 3px rgb(0 0 0 / 1%),
    0 2px 6px 0 rgb(0 0 0 / 3%);
  box-sizing: border-box;
}

.echart-nav {
  display: flex;
  height: 20px;
  padding: 16px 16px 0;
  flex-direction: row;
  align-items: center;
}

.echart-title {
  width: 100%;
  font-size: 14px;
}

.echart-select {
  float: right;
  width: 40%;
  min-width: 300px;
  padding-right: 10px;
  padding-bottom: 10px;
}

.echart {
  width: 100%;
  height: calc(100% - 20px);
}

.moment {
  padding: 40px 20px;
}
</style>