Vue3 + AntV G6 实战:手把手教你绘制可折叠的财务科目生态图

张开发
2026/4/15 22:20:28 15 分钟阅读

分享文章

Vue3 + AntV G6 实战:手把手教你绘制可折叠的财务科目生态图
Vue3 AntV G6 实战构建智能财务科目可视化系统在当今数据驱动的商业环境中财务数据的可视化呈现已成为企业决策的重要支撑。传统表格展示方式难以直观反映复杂的科目层级关系而基于Vue3和AntV G6的技术组合能够打造出交互丰富、视觉直观的财务科目生态图。本文将深入探讨如何利用这套技术栈构建一个支持动态折叠、金额展示和智能布局的专业级财务可视化解决方案。1. 环境搭建与基础配置1.1 创建Vue3项目与安装依赖现代前端开发中Vite已成为构建工具的首选。我们首先创建一个基于Vite的Vue3项目npm create vitelatest financial-visualization --template vue-ts cd financial-visualization npm install antv/g6 vue-demi关键依赖说明antv/g6: AntV系列的专业图可视化引擎vue-demi: 帮助库兼容Vue2/Vue3的辅助工具1.2 初始化G6图表容器在Vue组件中我们需要准备图表渲染的DOM容器template div classvisualization-container div refgraphContainer classgraph-wrapper/div /div /template script setup langts import { ref, onMounted } from vue const graphContainer refHTMLDivElement() /script style scoped .graph-wrapper { width: 100%; height: 600px; border: 1px solid #e8e8e8; border-radius: 4px; } /style2. 财务科目数据结构设计2.1 符合会计标准的树形结构财务科目数据通常呈现严格的层级关系以下是一个符合会计准则的数据结构示例interface FinancialNode { id: string code: string // 科目编码 name: string // 科目名称 amount: number // 科目金额 level: number // 科目层级 isLeaf?: boolean // 是否末级科目 children?: FinancialNode[] } const mockData: FinancialNode { id: root, code: 1001, name: 资产类, amount: 125000000, level: 0, children: [ { id: c1, code: 100101, name: 流动资产, amount: 75000000, level: 1, children: [ { id: c11, code: 10010101, name: 货币资金, amount: 50000000, level: 2, isLeaf: false } ] } ] }2.2 数据转换与预处理实际业务中数据可能需要从后端API获取并进行转换const transformApiData (apiData: any): FinancialNode { return { id: apiData.accountId, code: apiData.accountCode, name: apiData.accountName, amount: apiData.balance, level: apiData.level, children: apiData.children?.map(transformApiData) } }3. 高级图表配置与自定义节点3.1 注册专业财务节点类型G6的强大之处在于允许完全自定义节点样式const registerFinancialNode () { G6.registerNode(financial-node, { draw(cfg, group) { const { name, amount, level, collapsed } cfg const width 240 const height 60 // 基础矩形 const rect group.addShape(rect, { attrs: { x: -width/2, y: -height/2, width, height, fill: getLevelColor(level), radius: 4, shadowColor: rgba(0,0,0,0.1), shadowBlur: 6 } }) // 科目名称 group.addShape(text, { attrs: { text: name, x: -width/2 15, y: -10, fontSize: 14, fontWeight: bold, fill: #333 } }) // 金额显示 group.addShape(text, { attrs: { text: formatAmount(amount), x: width/2 - 15, y: 15, fontSize: 12, textAlign: right, fill: amount 0 ? #f5222d : #52c41a } }) // 折叠按钮 if(cfg.children?.length) { addCollapseButton(group, width, height, collapsed) } return rect } }) } const getLevelColor (level: number) { const colors [#f6ffed, #e6f7ff, #fff2e8, #f9f0ff] return colors[level % colors.length] } const formatAmount (amount: number) { return new Intl.NumberFormat(zh-CN, { style: currency, currency: CNY, minimumFractionDigits: 2 }).format(amount) }3.2 响应式布局配置针对财务科目特点优化树形布局const getLayoutConfig () ({ type: dendrogram, direction: TB, nodeSep: 40, rankSep: 100, radial: false })4. Vue3与G6深度集成实践4.1 组合式API封装图表逻辑利用Vue3的Composition API封装可复用的图表逻辑import { ref, onMounted, onUnmounted, watch } from vue export function useFinancialGraph(containerRef: RefHTMLDivElement, initialData: FinancialNode) { const graph refG6.TreeGraph() const currentData refFinancialNode(initialData) const initGraph () { if (!containerRef.value) return registerFinancialNode() graph.value new G6.TreeGraph({ container: containerRef.value, width: containerRef.value.clientWidth, height: containerRef.value.clientHeight, modes: { default: [drag-canvas, zoom-canvas] }, defaultNode: { type: financial-node }, layout: getLayoutConfig() }) graph.value.data(currentData.value) graph.value.render() graph.value.fitView() } const handleResize () { if (graph.value containerRef.value) { graph.value.changeSize( containerRef.value.clientWidth, containerRef.value.clientHeight ) graph.value.fitView() } } onMounted(() { initGraph() window.addEventListener(resize, handleResize) }) onUnmounted(() { window.removeEventListener(resize, handleResize) graph.value?.destroy() }) watch(currentData, (newData) { if (graph.value) { graph.value.changeData(newData) graph.value.fitView() } }) return { graph, currentData } }4.2 业务组件集成示例在业务组件中使用封装好的图表逻辑script setup langts import { ref } from vue import { useFinancialGraph } from ./useFinancialGraph import { fetchFinancialData } from ./api const containerRef refHTMLDivElement() const { currentData } useFinancialGraph(containerRef, {}) // 加载数据 const loadData async () { const res await fetchFinancialData() currentData.value res.data } /script template div classfinancial-dashboard div refcontainerRef classgraph-container/div button clickloadData刷新数据/button /div /template5. 性能优化与高级功能5.1 大数据量优化策略当处理大型企业财务数据时需要考虑性能优化const optimizeLargeData (graph: G6.TreeGraph) { // 1. 启用虚拟渲染 graph.get(canvas).set(localRefresh, false) // 2. 分级加载 graph.on(collapse-text:click, (e) { const item e.item if(item.getModel().collapsed) { loadChildrenData(item.getModel().id).then(children { item.getModel().children children graph.refreshItem(item) }) } }) // 3. 简化非活跃节点 graph.on(viewportchange, () { const nodes graph.getNodes() nodes.forEach(node { const bbox node.getBBox() const viewCenter graph.getPointByCanvas( graph.getWidth()/2, graph.getHeight()/2 ) const distance Math.sqrt( Math.pow(bbox.centerX - viewCenter.x, 2) Math.pow(bbox.centerY - viewCenter.y, 2) ) if(distance 800) { node.getContainer().hide() } else { node.getContainer().show() } }) }) }5.2 交互增强功能为财务图表添加专业交互功能const addInteractions (graph: G6.TreeGraph) { // 金额汇总高亮 graph.on(node:mouseenter, (e) { const node e.item const amount node.getModel().amount graph.getNodes().forEach(n { if(n.getModel().amount amount * 10) { n.getContainer().set(highlight, true) } }) }) // 右键菜单 graph.on(node:contextmenu, (e) { e.preventDefault() showContextMenu(e.item.getModel()) }) // 快捷键支持 document.addEventListener(keydown, (e) { if(e.key Escape) { graph.fitView() } }) }6. 企业级应用扩展6.1 多视图协同分析构建复杂的财务分析仪表盘template div classfinancial-analysis div classmain-graph FinancialGraph :datamainData / /div div classdetail-panel AccountDetail v-ifselectedNode :nodeselectedNode / TrendChart :datatrendData / /div /div /template script setup const selectedNode ref(null) const handleNodeClick (node) { selectedNode.value node fetchTrendData(node.id).then(data { trendData.value data }) } /script6.2 审计追踪功能为财务可视化添加变更记录const addAuditTrail (graph: G6.TreeGraph) { const history [] graph.on(afterupdateitem, (e) { history.push({ timestamp: new Date(), nodeId: e.item.getID(), action: update, before: e.cfg.before, after: e.item.getModel() }) }) graph.on(afteradditem, (e) { history.push({ timestamp: new Date(), nodeId: e.item.getID(), action: add, data: e.item.getModel() }) }) return { history, undo: () { const lastAction history.pop() if(lastAction) { // 实现撤销逻辑 } } } }

更多文章