vue3 项目使用Echarts使用记录
正在加载今日诗词....2024-07-09
按需引入 ECharts 图表和组件
项目中只需要使用 ECharts 中的柱状图、折线图和饼图等基本图表类型,全量引入过大。考虑使用按需引入的方式,仅导入我们用到的图表组件:
添加 echarts 依赖
npm install echarts --save
生成 echarts.js 文件,按需加载的文件
import * as echarts from 'echarts/core' import { BarChart, LineChart, PieChart } from 'echarts/charts' import { TitleComponent, TooltipComponent, GridComponent, // 数据集组件 DatasetComponent, // 内置数据转换器组件 (filter, sort) TransformComponent, // legend图例 LegendComponent, // 工具栏。内置有导出图片,数据视图,动态类型切换,数据区域缩放,重置五个工具。 ToolboxComponent, } from 'echarts/components' import { LabelLayout, UniversalTransition } from 'echarts/features' import { CanvasRenderer } from 'echarts/renderers' import type { // 系列类型的定义后缀都为 SeriesOption BarSeriesOption, LineSeriesOption, PieSeriesOption, } from 'echarts/charts' import type { // 组件类型的定义后缀都为 ComponentOption TitleComponentOption, TooltipComponentOption, GridComponentOption, DatasetComponentOption, LegendComponentOption, ToolboxComponentOption, } from 'echarts/components' import type { ComposeOption } from 'echarts/core' // 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 export type ECOption = ComposeOption< | BarSeriesOption | LineSeriesOption | PieSeriesOption | TitleComponentOption | LegendComponentOption | ToolboxComponentOption | TooltipComponentOption | GridComponentOption | DatasetComponentOption > // 注册必须的组件 echarts.use([ TitleComponent, LegendComponent, ToolboxComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, BarChart, LineChart, PieChart, LabelLayout, UniversalTransition, CanvasRenderer, ]) export { echarts }
这样在 vue 文件中需要使用 ECharts 时可以直接引入封装好的 echarts.ts ,当增加了新的图表类型(如雷达图、热力图、桑基图等)时直接修改 echarts.ts 文件就可以,提高使用按需引入的便捷性
组件封装
Hooks
封装一个统一的 useEChart Hook 函数,主要功能:
- 统一绘制图表方法
- 监听 props 变化,重新绘制
- 监听 resize 变化,执行 resize 事件
- 修改默认图表颜色,用 Antv G2 的配色方案(没话说,就是好看 😄)
- 函数接收 customOptions 修改默认配置,这样方便所有图表共同使用
import type { ShallowRef } from 'vue'
import { onMounted, ref } from 'vue'
import { debounce } from 'lodash'
import type { EChartsType } from 'echarts/types/dist/core'
import { type ECOption, echarts } from '@/utils/echarts'
import type {
DataZoomComponentOption,
LegendComponentOption,
LineSeriesOption,
XAXisOption,
YAXisOption,
} from 'echarts/types/dist/shared'
/** category 默认配色 来源:https://github.com/antvis/G2/blob/v5/src/theme/light.ts */
export const CATEGORY_COLORS: string[] = [
'#1783FF',
'#F0884D',
'#D580FF',
'#7863FF',
'#60C42D',
'#BD8F24',
'#FF80CA',
'#2491B3',
'#17C76F',
]
export default function useChart(props, customOptions?: ECOption) {
/** 所有图表的默认配置 */
const DEFAULT_OPTIONS: ECOption = {
series: [
{
name: '数量',
type: 'line',
emphasis: {
focus: 'self',
},
label: {
show: true,
position: 'inside',
color: '#fff',
},
data: props.data,
},
],
xAxis: [
{
type: 'category',
axisTick: {
show: false,
},
data: props.xAxisData,
},
],
yAxis: [
{
type: 'value',
minInterval: 1,
},
],
legend: {
show: true,
type: 'scroll',
orient: 'horizontal',
top: 25,
left: 'center',
},
}
// 要渲染的Dom元素
const chartDom: Ref<HTMLDivElement | null> = ref(null)
// 渲染的chart对象要用shallowRef
const chart: ShallowRef<EChartsType | null | undefined> = shallowRef(null)
const getHeight = computed(() => {
return typeof props.height === 'number' ? `${props.height}px` : props.height
})
watch(() => props, drawChart, {
deep: true,
})
// 绘制
function drawChart() {
const getSingleOption = (type) =>
props[type] ? props[type] : customOptions?.[type] || DEFAULT_OPTIONS[type]
const series: LineSeriesOption[] = getSingleOption('series')
const xAxis: XAXisOption[] = getSingleOption('xAxis')
const yAxis: YAXisOption[] = getSingleOption('yAxis')
const legend: LegendComponentOption = getSingleOption('legend')
const dataZoom: DataZoomComponentOption[] = getSingleOption('dataZoom')
const options: ECOption = {
// category自定义颜色
// https://echarts.apache.org/zh/option.html#color
color: CATEGORY_COLORS,
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
legend,
grid: {
left: 10,
right: 10,
bottom: props.dataZoom ? 40 : 10,
containLabel: true,
},
// 右上角工具栏
toolbox: {
show: false,
feature: {
magicType: {
type: ['line', 'bar'],
},
dataView: {
readOnly: false,
},
saveAsImage: {},
},
},
dataset: {
source: props.datasetSource,
},
xAxis,
yAxis,
dataZoom,
series,
}
// 开启notMerge保证配置数据不会叠加
chart.value?.setOption(options, {
notMerge: true,
})
}
// 使用防抖debounce函数,减少resize的次数
const chartResizeHandler = debounce(() => chart.value?.resize(), 100)
onMounted(() => window.addEventListener('resize', chartResizeHandler))
onBeforeUnmount(() =>
window.removeEventListener('resize', chartResizeHandler)
)
onMounted(() => {
chart.value = echarts.init(chartDom.value)
drawChart()
})
return {
chartDom,
getHeight,
}
}
组件
// BarChart.vue
<template>
<div ref="chartDom" :style="{ height: getHeight }"></div>
</template>
<script setup lang="ts">
import type {
BarSeriesOption,
DataZoomComponentOption,
LegendComponentOption,
XAXisOption,
YAXisOption,
} from 'echarts/types/dist/shared'
const props = withDefaults(
defineProps<{
/** 数据 */
data?: string | number[]
/** x轴数据 */
xAxisData?: string[]
/** 系列配置 */
series?: BarSeriesOption[]
/** x轴配置 */
xAxis?: XAXisOption[]
/** y轴配置 */
yAxis?: YAXisOption[]
/** 图例配置 */
legend?: LegendComponentOption
/** 区域缩放配置 */
dataZoom?: DataZoomComponentOption[]
/** 图形高度 */
height?: number | string
/** 数据集 */
datasetSource?: any[]
}>(),
{
data: () => [],
xAxisData: () => [],
}
)
const { getHeight, chartDom } = useEChart(props)
</script>
// PieChart.vue
<template>
<div ref="chartDom" :style="{ height: getHeight }"></div>
</template>
<script setup lang="ts">
import type {
DataZoomComponentOption,
LegendComponentOption,
PieSeriesOption,
} from 'echarts/types/dist/shared'
import type { ECOption } from '@/utils/echarts'
const DEFAULT_OPTIONS: ECOption = {
tooltip: {
trigger: 'item',
},
legend: {
orient: 'vertical',
left: 'left',
},
}
const props = withDefaults(
defineProps<{
/** 数据 */
data?: { name: string; value: number }[]
/** 系列配置 */
series?: PieSeriesOption[]
/** 图例配置 */
legend?: LegendComponentOption
/** 区域缩放配置 */
dataZoom?: DataZoomComponentOption[]
/** 图形高度 */
height?: number | string
/** 数据集 */
datasetSource?: any[]
}>(),
{
data: () => [],
}
)
const { chartDom, getHeight } = useEChart(props, DEFAULT_OPTIONS)
</script>
扇形图默认的配置与 useEChart hooks 中提供的默认配置不通用,通过传入 DEFAULT_OPTIONS 传入扇形图的默认配置
使用
<pie-chart
:height="400"
:x-axis="[{ type: 'category' }]"
:dataset-source="dataset"
:series="pieSeries"
/>
<line-chart
:height="400"
:x-axis="[{ type: 'category' }]"
:dataset-source="dataset"
:series="lineSeries"
/>
京ICP备2022027737号
Copyright © 2022 - present @wangxiang