个人支出趋势图:Vue3 + ECharts 把钱花哪看清楚

举报
静水流深-云海 发表于 2026/09/25 09:25:23 2026/09/25
【摘要】 用 Vue3 + ECharts 做个人支出趋势图:ref+onMounted 初始化图表、option 描述 xAxis/series、从 Pinia 账单 computed 聚合、watch 到变化即 setOption 重绘。含聚合代码与 resize/dispose 等注意点。

一、背景:记账之后,最该看的是趋势

前面写过 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 生命周期以官方文档为准。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。