个人支出趋势图:Vue3 + ECharts 把钱花哪看清楚
一、背景:记账之后,最该看的是趋势
前面写过 Vue3 + Pinia 本地记账本(记录每笔)。记下来只是第一步,记完不看的账本没意义。真正有用的,是把数据变成图:这个月比上月多花多少、哪类支出占大头、最近半年是不是在涨。图表比一堆数字直观得多,趋势一眼就能看出来。
本文在 Vue3 里接 ECharts,做一个个人支出趋势图:输入每月各类支出,渲染成折线/柱状图,切换月份看变化。讲清 ECharts 怎么在 Vue3 里初始化、数据怎么喂、响应式更新怎么处理。具体 ECharts API 以官方文档为准。它和前面实时看板(ECharts + WebSocket)是同一套图库,只是数据源换成"本地记录"而非实时推送。
二、在 Vue3 里初始化 ECharts
ECharts 要挂在一个有尺寸的 DOM 容器上,用 ref 拿容器、在 onMounted 初始化:
<script setup>
import { ref, onMounted, watch } from 'vue'
import * as echarts from 'echarts'
const el = ref(null)
let chart = null
onMounted(() => {
chart = echarts.init(el.value) // 容器有尺寸才能 init
chart.setOption(option)
})
</script>
<template>
<div ref="el" style="width:100%;height:360px"></div>
</template>
echarts.init(el.value) 把图表绑到 ref 容器,onMounted 时 DOM 已挂载、容器有尺寸,init 才成功(在 setup 顶层直接 init 会因容器还是 null 失败,所以放 onMounted)。容器必须给明确宽高(这里 height:360px),否则图是 0 尺寸不显示。chart.setOption(option) 把配置(数据+样式)灌进去出图。
三、数据怎么喂:option 结构
ECharts 的图全由一个 option 对象描述,最核心是 xAxis(横轴)、series(数据系列):
const option = {
tooltip: { trigger: 'axis' },
legend: { data: ['餐饮', '交通', '购物'] },
xAxis: { type: 'category', data: ['1月','2月','3月'] },
yAxis: { type: 'value' },
series: [
{ name: '餐饮', type: 'line', data: [1200, 980, 1500] },
{ name: '交通', type: 'line', data: [300, 280, 320] },
{ name: '购物', type: 'bar', data: [500, 800, 600] },
]
}
xAxis.data 是横轴类目(月份),series 每项是一个数据系列:type:'line' 折线、type:'bar' 柱状,data 是对应每个月的值。legend 让多系列可切换显隐,tooltip 鼠标悬停显示数值。把"月份→金额"这种表,变成 option 里的两个数组,ECharts 就画出来了。餐饮用折线、购物用柱状,混搭也行,按你想强调的选。
四、响应式:数据变了图就更新
Vue3 里数据来自记账本(Pinia 的 list),按月份聚合后喂给图。用 watch 监听数据、变化时 setOption:
<script setup>
import { computed, watch } from 'vue'
import { useBillStore } from './store'
const store = useBillStore()
// 按月份聚合各类支出
const option = computed(() => buildOption(store.list))
watch(option, (opt) => { chart && chart.setOption(opt) }, { deep: true })
</script>
computed 从 store.list 派生出 option(聚合逻辑在 buildOption 里:遍历账单、按月份和分类累加)。watch(option) 一旦 option 变(新增一笔账),就 chart.setOption(opt) 重绘,图跟着更新。deep:true 确保对象内部变化也触发。这样"记账本加了笔、趋势图自动变",两个组件通过 Pinia 联动,体验连贯。
五、聚合逻辑:从账单到图表数据
buildOption 干的是"把扁平账单转成 ECharts 要的数组结构":
function buildOption(list) {
const months = [...new Set(list.map(i => i.month))] // 去重月份
const cats = ['餐饮','交通','购物']
const series = cats.map(cat => ({
name: cat, type: 'line',
data: months.map(m => list.filter(i => i.month===m && i.cat===cat)
.reduce((s,i)=>s+Number(i.amount),0))
}))
return { xAxis:{type:'category',data:months}, yAxis:{type:'value'}, series }
}
先取所有出现的月份(去重、排序按需),再对每个分类,按月份累加金额,得到 series 里每个分类对应各月的数据。filter+reduce 是这种"分组求和"的标配写法。真实账单的 month/cat 字段按你记账本的结构来,这里演示结构。聚合函数要处理好"某月某类没数据=0",否则图会缺项错位。
六、几个注意点
第一,容器尺寸与 resize。容器宽高写死 360px 简单,但窗口缩放、图表在隐藏 Tab 里时尺寸会变,图可能不自适应。监听窗口 resize 调 chart.resize(),或容器尺寸变化用 ResizeObserver 触发 resize,图才始终铺满。
第二,数据为空要兜底。list 为空时 months 为空数组,图是空的但别报错。聚合时确保没数据给空 series,ECharts 能渲染空图,不崩。
第三,不要每次重建 chart。setOption 是增量更新,反复 echarts.init 会建多个实例、内存泄漏。一个容器 init 一次,之后只 setOption。组件卸载时 chart.dispose() 释放。
第四,类别太多图会乱。支出分类十几个时,全画一条线互相交织看不清。可考虑"默认只看总额折线 + legend 点选看细分",或把分类收成几大组。图的清晰度和数据量要平衡。
第五,数值精度。金额累加可能出小数(0.1+0.2),展示前 toFixed(2) 或 Math.round,别把浮点误差显示给用户。
七、小结
Vue3 接 ECharts 做支出趋势图:容器用 ref、onMounted 里 echarts.init(容器必须给尺寸)、setOption 灌数据;图全由 option 的 xAxis/series 描述;数据从 Pinia 账单 computed 聚合、watch 到变化就重绘,记账和看图联动。聚合用 filter+reduce 分组求和。注意 resize 自适应、一个容器只 init 一次(卸载 dispose)、空数据兜底。ECharts option 字段与 Vue 生命周期以官方文档为准。
- 点赞
- 收藏
- 关注作者
评论(0)