|
| 1 | +# 使用 ECharts 实现数据可视化并导出 Excel |
| 2 | + |
| 3 | +## 1. 引言 |
| 4 | + |
| 5 | +在 Web 开发中,数据可视化和数据导出是常见的需求。ECharts 作为强大的前端可视化库,可以帮助开发者构建丰富的图表。而 `xlsx` 库则可以将图表数据转换为 Excel 文件,方便数据分析和分享。 |
| 6 | + |
| 7 | +本文介绍如何封装 `useEcharts` 进行 ECharts 初始化、配置、销毁等操作,并通过 `useEchartsToExcel` 将图表数据导出到 Excel。 |
| 8 | + |
| 9 | +## 2. ECharts 图表封装 |
| 10 | + |
| 11 | +### 2.1 初始化 ECharts |
| 12 | + |
| 13 | +封装 `useEcharts`,提供初始化、配置、重置和销毁 ECharts 实例的方法。 |
| 14 | + |
| 15 | +```ts |
| 16 | +export const useEcharts = () => { |
| 17 | + const chart = shallowRef<echarts.ECharts>(); |
| 18 | + |
| 19 | + const initEcharts = (dom: HTMLElement) => { |
| 20 | + if (!dom || chart.value) return; |
| 21 | + chart.value = echarts.init(dom); |
| 22 | + }; |
| 23 | + |
| 24 | + const setOption = (option: any) => { |
| 25 | + if (chart.value) { |
| 26 | + chart.value.setOption(option); |
| 27 | + } |
| 28 | + }; |
| 29 | + |
| 30 | + const resizeEcharts = () => { |
| 31 | + chart.value?.resize(); |
| 32 | + }; |
| 33 | + |
| 34 | + const disposeEcharts = () => { |
| 35 | + chart.value?.dispose(); |
| 36 | + }; |
| 37 | + |
| 38 | + return { |
| 39 | + chart, |
| 40 | + initEcharts, |
| 41 | + setOption, |
| 42 | + resizeEcharts, |
| 43 | + disposeEcharts, |
| 44 | + }; |
| 45 | +}; |
| 46 | +``` |
| 47 | + |
| 48 | +### 2.2 在 Vue 组件中使用 |
| 49 | + |
| 50 | +```vue |
| 51 | +<script lang="ts" setup> |
| 52 | +import { useEcharts } from "@/hooks/useEcharts"; |
| 53 | +import { useElementSize } from "@vueuse/core"; |
| 54 | +import { debounce } from "lodash"; |
| 55 | +
|
| 56 | +const { initEcharts, setOption, resizeEcharts, disposeEcharts, chart } = useEcharts(); |
| 57 | +
|
| 58 | +const props = defineProps<{ |
| 59 | + option: any; |
| 60 | + debounce?: boolean; |
| 61 | + loading?: boolean; |
| 62 | +}>(); |
| 63 | +
|
| 64 | +const chartRef = ref(); |
| 65 | +const { width, height } = useElementSize(chartRef); |
| 66 | +
|
| 67 | +watch( |
| 68 | + () => props.option, |
| 69 | + () => { |
| 70 | + if (!chartRef.value) return; |
| 71 | + init(); |
| 72 | + }, |
| 73 | + { |
| 74 | + deep: true, |
| 75 | + } |
| 76 | +); |
| 77 | +
|
| 78 | +watch( |
| 79 | + [width, height], |
| 80 | + () => { |
| 81 | + if (props.debounce) { |
| 82 | + debounce(resizeEcharts, 1000)(); |
| 83 | + } else { |
| 84 | + resizeEcharts(); |
| 85 | + } |
| 86 | + }, |
| 87 | + { |
| 88 | + immediate: true, |
| 89 | + deep: true, |
| 90 | + } |
| 91 | +); |
| 92 | +
|
| 93 | +const init = () => { |
| 94 | + nextTick(() => { |
| 95 | + initEcharts(chartRef.value); |
| 96 | + setOption(props.option); |
| 97 | + }); |
| 98 | +}; |
| 99 | +
|
| 100 | +onMounted(() => { |
| 101 | + init(); |
| 102 | +}); |
| 103 | +
|
| 104 | +onUnmounted(() => { |
| 105 | + disposeEcharts(); |
| 106 | +}); |
| 107 | +
|
| 108 | +defineExpose({ |
| 109 | + chart, |
| 110 | +}); |
| 111 | +</script> |
| 112 | +
|
| 113 | +<template> |
| 114 | + <div v-loading="loading" class="wh-full" ref="chartRef"></div> |
| 115 | +</template> |
| 116 | +
|
| 117 | +<style scoped></style> |
| 118 | +``` |
| 119 | + |
| 120 | +## 3. 数据导出到 Excel |
| 121 | + |
| 122 | +### 3.1 导出逻辑封装 |
| 123 | + |
| 124 | +`useEchartsToExcel` 负责解析 ECharts 配置,并将数据转换为 Excel 格式。 |
| 125 | + |
| 126 | +```ts |
| 127 | +export const useEchartsToExcel = () => { |
| 128 | + const fileName = ref(); |
| 129 | + const option = ref<echarts.EChartsOption>(); |
| 130 | + |
| 131 | + const exportToExcel = ({ name, echartsOption }: { name: string; echartsOption: echarts.EChartsOption }) => { |
| 132 | + try { |
| 133 | + fileName.value = name; |
| 134 | + option.value = echartsOption; |
| 135 | + dataToXLSX(); |
| 136 | + } catch (error) { |
| 137 | + console.error(error); |
| 138 | + } |
| 139 | + }; |
| 140 | + |
| 141 | + const dataToXLSX = () => { |
| 142 | + if (!option.value) return; |
| 143 | + const workbook = XLSX.utils.book_new(); |
| 144 | + const worksheet = XLSX.utils.aoa_to_sheet([]); |
| 145 | + const seriesData = option.value.series as Series[]; |
| 146 | + chartAnalysis(seriesData, worksheet); |
| 147 | + XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1'); |
| 148 | + XLSX.writeFile(workbook, `${fileName.value}.xlsx`); |
| 149 | + }; |
| 150 | + |
| 151 | + return { exportToExcel }; |
| 152 | +}; |
| 153 | +``` |
| 154 | + |
| 155 | +### 3.2 处理不同类型的图表数据 |
| 156 | + |
| 157 | +对于基础图表数据: |
| 158 | + |
| 159 | +```ts |
| 160 | +const basicChartAnalysis = (seriesData: Series[], worksheet: XLSX.WorkSheet) => { |
| 161 | + let index = 1; |
| 162 | + seriesData.forEach((series) => { |
| 163 | + if (!series.data) return; |
| 164 | + let seriesRow: [string, ...number[]] = [`${series.name}(${series.extra.unit})`]; |
| 165 | + seriesRow = [...seriesRow, ...series.data.map((item) => Number(item || 0))]; |
| 166 | + XLSX.utils.sheet_add_aoa(worksheet, [seriesRow], { origin: { r: 0, c: index } }); |
| 167 | + index++; |
| 168 | + }); |
| 169 | +}; |
| 170 | +``` |
| 171 | + |
| 172 | +针对饼图数据的处理: |
| 173 | + |
| 174 | +```ts |
| 175 | +const pieChartAnalysis = (seriesData: Series[], worksheet: XLSX.WorkSheet) => { |
| 176 | + const series = seriesData[0]; |
| 177 | + if (!isComplexData(series.data)) return; |
| 178 | + const seriesTitleRow = [fileName.value, ...series.data.map((item) => item.name || '')]; |
| 179 | + const seriesContentRow = [`${series.name}(${series.extra.unit})`, ...series.data.map((item) => Number(item.value || 0))]; |
| 180 | + XLSX.utils.sheet_add_aoa(worksheet, [seriesTitleRow], { origin: { r: 0, c: 0 } }); |
| 181 | + XLSX.utils.sheet_add_aoa(worksheet, [seriesContentRow], { origin: { r: 1, c: 0 } }); |
| 182 | +}; |
| 183 | +``` |
| 184 | + |
| 185 | +### 3.3 在 Vue 组件中使用 |
| 186 | + |
| 187 | +```vue |
| 188 | +<template> |
| 189 | + <button @click="exportData">导出 Excel</button> |
| 190 | +</template> |
| 191 | +
|
| 192 | +<script setup> |
| 193 | +import { useEchartsToExcel } from '@/hooks/useEchartsToExcel'; |
| 194 | +const { exportToExcel } = useEchartsToExcel(); |
| 195 | +const exportData = () => { |
| 196 | + exportToExcel({ |
| 197 | + name: 'Chart Data', |
| 198 | + echartsOption: { |
| 199 | + xAxis: { data: ['A', 'B', 'C'] }, |
| 200 | + series: [{ name: '数据', type: 'bar', data: [10, 20, 30], extra: { unit: '个' } }], |
| 201 | + }, |
| 202 | + }); |
| 203 | +}; |
| 204 | +</script> |
| 205 | +``` |
| 206 | + |
| 207 | +## 4. 总结 |
| 208 | + |
| 209 | +- `useEcharts` 提供了 ECharts 图表的初始化、设置配置、调整大小和销毁的方法。 |
| 210 | +- `useEchartsToExcel` 提供了将 ECharts 数据导出为 Excel 文件的方法,并支持不同类型的图表数据解析。 |
| 211 | +- 通过封装这些功能,可以提高代码复用性,简化数据可视化和数据导出工作。 |
0 commit comments