大家好,今天我们来分享一个在前端数据可视化中非常常见的需求:如何使用 Vue3、TypeScript 和 ECharts 创建一个既美观又信息丰富的统计卡片。
我们将实现的效果如下图所示:一个卡片中,左侧是一个紧凑的环形图,中心显示总数;右侧是自定义格式的图例,清晰地展示各分类的数值和名称。这种布局在仪表盘(Dashboard)设计中非常流行。

技术栈
- Vue3: 采用 Composition API 和
<script setup>语法。 - TypeScript: 提供类型安全,增强代码健壮性。
- ECharts: 强大、开源的数据可视化库。
- Element Plus: 用于快速搭建 UI 框架(本例中的
el-card)。
第一步:环境准备
首先,请确保你已经有一个 Vue3 + TypeScript 的项目。如果还没有,可以使用 Vite 快速创建:
npm create vue@latest
然后,安装 ECharts:
npm install echarts
第二步:组件结构 (<template>)
我们的模板结构非常简单。使用 Element Plus 的 el-card 作为卡片容器,内部放置一个 div 元素作为 ECharts 的渲染容器。关键在于使用 ref="chart1" 来获取这个 DOM 元素的引用。
<template>
<div class="layout-padding" style="background-color: #d7e9f6;">
<el-row :gutter="10">
<el-col :span="9">
<el-card style="background-color: #f1f6fb; border-radius: 8px; box-shadow: none; height: 120px;">
<span style="font-weight: bold;">航母数量</span>
<!-- ECharts 图表容器 -->
<div style="height: 60px; margin-left: 30px; margin-top: 8px;" ref="chart1"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
第三步:核心逻辑 (<script setup>)
这是实现我们自定义效果的关键部分。
1. 引入依赖和定义引用
我们需要从 vue 中引入 ref, reactive, onMounted, onUnmounted,并引入 echarts。
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
// 用于存储所有 ECharts 实例,方便统一管理
const state_chart = reactive({
myCharts: [] as echarts.EChartsType[],
});
// 获取图表容器的 DOM 引用
const chart1 = ref<HTMLElement>();
2. 初始化图表函数 init_chart1
这是 ECharts 配置的核心。我们通过 echarts.init 初始化实例,然后通过 setOption 方法配置图表样式和数据。
核心配置解析:
legend: 我们没有使用默认的图例,而是通过formatter函数进行了深度定制。利用 ECharts 的富文本(rich)功能,我们将图例项格式化为“数值在上,名称在下”的布局,使其更紧凑、易读。series:type: 'pie'和radius: ['80%', '100%']: 这是创建环形图(甜甜圈图)的标准配置。radius数组第一个值是内半径,第二个是外半径。center: ['10%', '50%']: 默认饼图在中间,我们通过这个配置将其整体向左移动,为右侧的图例腾出空间,实现左右布局。label: 这是实现中心显示总数的“魔法”。同样使用formatter和rich文本,我们计算出data中所有value的总和,并将其格式化后显示在环形图的中心。data: 这里是我们的数据源。
const init_chart1 = async () => {
if (!chart1.value) return;
const myChart = echarts.init(chart1.value);
const option = {
// ... (此处省略 option 配置,具体见上文完整代码)
};
myChart.setOption(option);
state_chart.myCharts.push(myChart);
};
3. 生命周期管理
onMounted: 在组件挂载到 DOM 后,我们才能获取到ref="chart1"对应的 DOM 元素,所以必须在此钩子中调用init_chart1。onUnmounted: 当组件销毁时,ECharts 实例并不会自动销毁,这会导致内存泄漏。因此,在组件卸载前,我们需要手动调用dispose()方法来销毁所有 ECharts 实例,这是一个非常重要的最佳实践。
// 组件挂载后初始化图表
onMounted(() => {
init_chart1();
});
// 组件卸载前,销毁图表实例,释放内存
onUnmounted(() => {
state_chart.myCharts.forEach(chart => {
chart.dispose();
});
});
完整代码
将以上所有部分组合起来,就是完整的组件代码。你可以直接复制到你的 .vue 文件中运行。
<template>
<div class="layout-padding" style="background-color: #d7e9f6;">
<el-row :gutter="10">
<el-col :span="9">
<el-card style="background-color: #f1f6fb; border-radius: 8px; box-shadow: none; height: 120px;">
<span style="font-weight: bold;">航母数量</span>
<!-- ECharts 图表容器 -->
<div style="height: 60px; margin-left: 30px; margin-top: 8px;" ref="chart1"></div>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, onUnmounted } from 'vue';
import * as echarts from 'echarts';
// 用于存储所有 ECharts 实例,方便统一管理
const state_chart = reactive({
myCharts: [] as echarts.EChartsType[],
});
// 获取图表容器的 DOM 引用
const chart1 = ref<HTMLElement>();
// 初始化图表
const init_chart1 = async () => {
if (!chart1.value) return;
const myChart = echarts.init(chart1.value);
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c} 艘'
},
legend: {
top: '20%',
orient: 'horizontal',
icon: 'circle',
itemWidth: 12, // 设置圆的直径为 12px
itemHeight: 12, // 设置圆的直径为 12px
itemGap: 25,
formatter: function (name) {
var data = option.series[0].data;
var value = 0;
for (var i = 0; i < data.length; i++) {
if (data[i].name === name) {
value = data[i].value;
break;
}
}
return `{value|${value}}\n{name|${name}}`;
},
textStyle: {
rich: {
value: {
fontSize: 20,
fontWeight: 'bold',
color: '#333',
lineHeight: 30,
align: 'center',
padding: [-45, 0, 0, 0],
},
name: {
fontSize: 12,
color: 'black',
align: 'center',
padding: [-30, 0, 0, 0],
}
}
}
},
textStyle: {
rich: {
value: {
fontSize: 14,
fontWeight: 'bold',
color: '#333',
lineHeight: 20,
align: 'center'
},
name: {
fontSize: 12,
color: '#666',
align: 'center'
}
}
}
},
series: [
{
name: '航母数量',
type: 'pie',
// 通过设置内径和外径,实现环形图
radius: ['80%', '100%'],
// 将环形图位置调整到左侧,为右侧图例留出空间
center: ['10%', '50%'],
itemStyle: {
borderColor: '#fff',
borderWidth: 2
},
// 在环形图中心显示总计信息
label: {
show: true,
position: 'center',
formatter: function () {
const data = option.series![0].data as { value: number }[];
const total = data.reduce((sum, item) => sum + item.value, 0);
return `{total|${total}}\n{totalText|总共}`;
},
rich: {
total: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
},
totalText: {
fontSize: 12,
fontWeight: 'normal',
color: '#999'
}
}
},
emphasis: {
scale: true, // 鼠标悬浮时允许放大
label: {
show: true,
fontSize: 16,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
{ value: 3, name: '中国', itemStyle: { color: '#5470c6' } },
{ value: 9, name: '美国', itemStyle: { color: '#91cc75' } },
{ value: 2, name: '日本', itemStyle: { color: '#fac858' } },
{ value: 0, name: '德国', itemStyle: { color: '#ee6666' } },
{ value: 0, name: '俄罗斯', itemStyle: { color: '#73c0de' } }
]
}
]
};
myChart.setOption(option);
state_chart.myCharts.push(myChart);
};
// 组件挂载后初始化图表
onMounted(() => {
init_chart1();
});
// 组件卸载前,销毁图表实例,释放内存
onUnmounted(() => {
state_chart.myCharts.forEach(chart => {
chart.dispose();
});
});
</script>
<style scoped>
.layout-padding {
padding: 16px;
}
</style>
总结
通过本教程,我们学习了如何结合 Vue3 和 ECharts 创建一个高度自定义的环形图卡片。关键点在于:
- 布局控制: 通过
series.center调整图表位置,配合legend的right属性实现左右布局。 - 富文本: 灵活运用
formatter和rich文本来定制label和legend的显示样式,实现标准配置无法达到的效果。 - 生命周期: 正确使用
onMounted和onUnmounted来管理图表实例的创建和销毁,保证应用的性能和稳定性。
希望这篇文章对你有所帮助!快去试试打造属于你自己的数据可视化卡片吧!

138

被折叠的 条评论
为什么被折叠?



