跳至主要內容

ECharts

俞文健大约 3 分钟

基本配置

// 引入核心
import * as charts from "echarts/core"

// 引入图表
import {
  BarChart,  // 柱状图
  LineChart, // 折线图
  PieChart,  // 扇形图
  MapChart,  // 地图
  EffectScatterChart
} from "echarts/charts"

// 图表类型
import type {
  BarSeriesOption,
  LineSeriesOption,
  PieSeriesOption,
  MapSeriesOption,
  EffectScatterSeriesOption
} from "echarts/charts"

// 引入组件
import {
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent
} from "echarts/components"

// 组件类型
import type {
  TitleComponentOption,
  TooltipComponentOption,
  LegendComponentOption,
  GridComponentOption
} from "echarts/components"

// 布局组件和过渡组件
import { LabelLayout, UniversalTransition } from "echarts/features"

// 渲染器
import { CanvasRenderer } from "echarts/renderers"

import type { ComposeOption } from "echarts/core"

// 注册类型
export type ECOption = ComposeOption<
  | BarSeriesOption
  | LineSeriesOption
  | PieSeriesOption
  | MapSeriesOption
  | EffectScatterSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | LegendComponentOption
  | GridComponentOption
>

// 注册组件
charts.use([
  BarChart,
  LineChart,
  PieChart,
  MapChart,
  EffectScatterChart,
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer
])

export default charts

组件封装

<script setup lang="ts">
  import { ref, watch, onMounted } from "vue"
  import charts, { type ECOption } from "@/utils/charts"
  import { merge, cloneDeep } from "lodash"
  
  const props = withDefaults(defineProps<{
    option: ECOption
    width?: string
    height?: string
  }>(), {
    width: "100%",
    height: "100%"
  })
  
  const initOption: ECOption = {
    title: {},
    xAxis: {},
    yAxis: {
      axisLine: {
        show: true // 显示轴线
      },
      axisTick: {
        show: true // 显示刻度线
      }
    },
    tooltip: {}, // 提示框
    legend: {}, // 图例
    grid: {
      left: 10, // 左侧距离
      right: 10, // 右侧距离
      containLabel: true // 是否包含标签
    }
  }
  
  const chartRef = ref<HTMLDivElement>()
  
  const chart = ref<charts.ECharts>()
  
  onMounted(() => {
    // 初始化 charts 实例
    chart.value = charts.init(chartRef.value)
  })
  
  watch(() => props.option, (option: ECOption) => {
    // 渲染图表
    chart.value?.setOption(merge(cloneDeep(initOption), option))
  }, {
    deep: true
  })
  
  defineExpose({
    name: "Chart",
    chart
  })
</script>

<template>
  <div ref="chartRef" :style="{ width, height }" />
</template>

柱状图

<script setup lang="ts">
  const option: ECOption = {
    title: {
      text: "柱状图" // 标题
    },
    xAxis: {
      data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"] // 数据
    },
    series: [
      {
        name: "柱状图",
        type: "bar",
        data: [5, 20, 36, 10, 10, 20] // 对应 x 轴数量
      }
    ]
  }
</script>

<template>
  <Chart :option="option" width="400px" height="300px" />
</template>

折线图

<script setup lang="ts">
  const option: ECOption = {
    title: {
      text: "折线图" // 标题
    },
    xAxis: {
      data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] // 数据
    },
    series: [
      {
        name: "折线图",
        type: "line",
        data: [150, 230, 224, 218, 135, 147, 260] // 对应 x 轴数量
      }
    ]
  }
</script>

<template>
  <Chart :option="option" width="400px" height="300px" />
</template>

饼图

<script setup lang="ts">
  const option: ECOption = {
    title: {
      text: "饼图" // 标题
    },
    series: [
      {
        name: "饼图",
        type: "pie", // 饼图
        data: [
          { value: 735, name: "Direct" },
          { value: 580, name: "Email" },
          { value: 484, name: "Union Ads" }
        ],
        radius: ["10%", "30%"], // 半径
        center: ["50%", 150] // 位置
      }
    ]
  }
</script>

<template>
  <Chart :option="option" width="400px" height="300px" />
</template>

中国地图

<script setup lang="ts">
  import Chart from "@/components/Chart/index.vue"
  import charts, { type ECOption } from "@/utils/charts.ts"
  import china from "@/china.json" // https://github.com/yezongyang/china-geojson
  
  charts.registerMap("china", china as any)
  
  const option: ECOption = {
    title: {
      show: false
    },
    xAxis: {
      show: false
    },
    yAxis: {
      show: false
    },
    legend: {
      show: false
    },
    tooltip: {
      show: false
    },
    geo: {
      map: "china", // 中国地图
      itemStyle: {
        areaColor: "rgba(104, 216, 254, .5)", // 地图颜色
        borderColor: "rgba(104, 216, 254, .8)" // 描边颜色
      },
      label: {
        color: "#fff" // 文本颜色
      },
      emphasis: {
        itemStyle: {
          areaColor: "rgba(104, 216, 254)" // 高亮地图颜色
        },
        label: {
          color: "#fff" // 高亮文本颜色
        }
      }
    },
    series: [
      {
        type: "effectScatter", // 散点
        coordinateSystem: "geo", // 经纬坐标系
        data: [
          { value: [116, 40], name: "北京" }, // 北京坐标
          { value: [121, 31], name: "上海" }, // 上海坐标
          { value: [114, 30], name: "武汉" } // 武汉坐标
        ],
        itemStyle: {
          color: "#5470c6" // 散点颜色
        },
        label: {
          show: true, // 是否显示文本
          formatter: "{b}", // 显示字段 (name)
          color: "#5470c6", // 文本颜色
          position: "left", // 文本位置
          fontSize: 10 // 文本大小
        }
      },
      {
        type: "lines", // 散点连线
        coordinateSystem: "geo", // 经纬坐标系
        data: [
          {
            // 北京飞上海
            coords: [
              [116, 40],
              [121, 31]
            ]
          },
          {
            // 北京飞武汉
            coords: [
              [116, 40],
              [114, 30]
            ]
          }
        ],
        lineStyle: {
          color: "#5470c6", // 线的颜色
          width: 2,
          curveness: 0.2
        },
        effect: {
          show: true,
          symbol: "arrow", // 线的特效 (箭头)
          symbolSize: 5
        }
      }
    ]
  }
</script>

<template>
  <Chart :option="option" width="600px" height="400px" />
</template>