VxeTable 实现表尾合计行并支持数据实时统计

在后台管理系统中,表格合计行(如统计总分、总年龄等)是非常常见的需求。VxeTable 提供了灵活的定制能力,通过自定义表尾(footer)配合事件监听,可以轻松实现合计行的动态更新。本文将以 vxe-grid 组件为例,演示如何在数据编辑、粘贴等操作后,实时刷新表尾的合计数据。

实现步骤

  1. 开启表尾显示:通过 showFooter: true 让表格底部显示自定义行。
  2. 定义合计数据对象:在 footerData 中放置一个响应式对象,内容与表格列结构对齐。
  3. 监听数据变更事件:
    • edit-closed:单元格编辑结束(无论是确认还是取消)后触发。
    • cell-area-paste:区域粘贴完成后触发。
  4. 封装合计计算方法:遍历表格 data,累加指定数值字段,更新到合计对象中。
  5. 初始化时计算一次:页面渲染后立即调用合计方法,保证初始数据正确。

代码

表尾显示合计行,包含"合计"标识及数值列的汇总结果。

支持单元格编辑(双击激活),编辑完成后自动重新计算合计。

支持区域选取与粘贴,粘贴完成后自动重新计算合计。

html 复制代码
<template>
  <div>
    <vxe-grid v-bind="gridOptions" v-on="gridEvents"></vxe-grid>
  </div>
</template>

<script setup>
import { reactive } from 'vue'
import XEUtils from 'xe-utils'
const sexEditRender = reactive({
  name: 'VxeSelect',
  options: [
    { label: '女', value: '0' },
    { label: '男', value: '1' }
  ]
})
const sumNObj = reactive({
  seq: '合计',
  num: 0,
  age: 0
})
const gridOptions = reactive({
  height: 500,
  showOverflow: true,
  showFooter: true,
  columnConfig: {
    resizable: true
  },
  mouseConfig: {
    area: true // 是否开启区域选取
  },
  areaConfig: {
    multiple: true // 是否启用多区域选取功能
  },
  editConfig: {
    mode: 'cell', // 单元格编辑模式
    trigger: 'dblclick' // 双击单元格激活编辑状态
  },
  keyboardConfig: {
    arrowCursorLock: true, // 方向键光标锁,开启后处于非聚焦式编辑状态,将支持在编辑状态中通过方向键切换单元格。(切换为聚焦编辑状态,可以按 F2 键或者鼠标左键点击输入框,就可以用方向键左右移动输入框的光标)
    isArrow: true, // 是否开启方向键功能
    isShift: true, // 是否开启同时按住方向键以活动区域为起始,向指定方向扩展单元格区域
    isTab: true, // 是否开启 Tab 键功能
    isEnter: true, // 是否开启回车键功能
    isEdit: true, // 是否开启任意键进入编辑(功能键除外)
    isBack: true, // 是否开启Backspace键功能
    isDel: true, // 是否开启删除键功能
    isEsc: true, // 是否开启Esc键关闭编辑功能
    isFNR: true, // 是否开启查找与替换
    isAll: true, // 是否启用快捷键全选
    isClip: true // 是否开启复制粘贴
  },
  columns: [
    { field: 'seq', type: 'seq', fixed: 'left', width: 60 },
    { field: 'name', fixed: 'left', title: '名字', editRender: { name: 'VxeInput' } },
    { field: 'role', title: '角色', editRender: { name: 'VxeInput' } },
    { field: 'sex', title: '性别', editRender: sexEditRender },
    { field: 'num', title: '分数', editRender: { name: 'VxeNumberInput' } },
    { field: 'age', title: '年龄', editRender: { name: 'VxeNumberInput' } },
    { field: 'address', title: '地址', width: 200, editRender: { name: 'VxeInput' } }
  ],
  data: [
    { id: 10001, name: '张三', role: '前端开发', sex: '0', num: 23, age: 28, address: '北京市17号' },
    { id: 10002, name: '李四', role: '测试人员', sex: '1', num: 23, age: 22, address: '江苏省27号' },
    { id: 10003, name: '老六', role: '项目经理', sex: '0', num: 23, age: 32, address: '深圳市19号' },
    { id: 10004, name: '小李', role: '后端开发', sex: '1', num: 456, age: 24, address: '江苏省47号' },
    { id: 10005, name: '老万', role: '设计师', sex: '1', num: 23, age: 42, address: '北京市18号' },
    { id: 10006, name: '小刘', role: '前端开发', sex: '0', num: 23, age: 38, address: '上海市17号' },
    { id: 10007, name: '老徐', role: '测试人员', sex: '1', num: 100, age: 24, address: '北京市19号' },
    { id: 10008, name: '老二', role: '设计师', sex: '0', num: 345, age: 34, address: '上海市11号' },
    { id: 10009, name: '小牛', role: '前端开发', sex: '1', num: 67, age: 52, address: '深圳市29号' },
    { id: 10010, name: '小帅', role: '测试人员', sex: '1', num: 23, age: 44, address: '北京市37号' },
    { id: 10011, name: '老魏', role: '后端开发', sex: '0', num: 56, age: 52, address: '深圳市12号' },
    { id: 10012, name: '小徐', role: '测试人员', sex: '1', num: 23, age: 16, address: '广州市16号' },
    { id: 10013, name: '小张', role: '设计师', sex: '1', num: 69, age: 16, address: '广州市46号' },
    { id: 10014, name: '老冯', role: '前端开发', sex: '0', num: 36, age: 36, address: '广州市66号' },
    { id: 10015, name: '小哥', role: '后端开发', sex: '0', num: 33, age: 47, address: '广州市56号' },
    { id: 10016, name: '李哥', role: '测试人员', sex: '1', num: 2, age: 42, address: '深圳市16号' }
  ],
  footerData: [
    sumNObj
  ]
})
const countFooterSum = () => {
  const { data = [] } = gridOptions
  let countAge = 0
  let countNum = 0
  data.forEach(row => {
    countAge += XEUtils.toNumber(row.age)
    countNum += XEUtils.toNumber(row.num)
  })
  sumNObj.age = countAge
  sumNObj.num = countNum
}
const gridEvents = {
  editClosed () {
    // 编辑完成刷新表尾数据
    countFooterSum()
  },
  cellAreaPaste () {
    // 粘贴完成刷新表尾数据
    countFooterSum()
  }
}
// 初始化刷新表尾数据
countFooterSum()

</script>

说明

响应式的对象

sumNObj 使用 reactive 包裹,并且直接赋值给 footerData。当合计对象的属性发生变化时,表尾会自动重新渲染。

事件选择

  • edit-closed 比 cell-edit 更稳定,因为它会在编辑完全结束后触发(包括取消编辑的情况,但取消不改变数据,合计无影响)。
  • cell-area-paste 覆盖了粘贴操作,确保批量修改数据后合计也是正确的。 如果需要更全面的覆盖(如删除行、拖拽填充等),可根据业务需求补充相应事件(例如 checkbox-change、delete-row)。

vxetable.cn

相关推荐
杨大厨wd2 小时前
Vue3 业务组件封装别只会传 props:如何设计一个真正好用的组件
vue.js
前端那点事2 小时前
Vue3 script setup 语法糖最全教程!零基础吃透+项目落地+面试满分
前端·vue.js
卷帘依旧2 小时前
Vue 响应式原理:Object.defineProperty vs Proxy 深度对比
前端·vue.js
布局呆星3 小时前
Vue3 路由守卫详解:全局守卫、路由独享守卫、组件内守卫
前端·javascript·vue.js
小李子呢02113 小时前
前端八股Vue---ref操作 DOM 元素或组件,调用子组件方法
前端·javascript·vue.js
Yoram3 小时前
Vue3 响应性:跨上下文的传递、转换与作用域控制
前端·vue.js
qingy_20464 小时前
浏览器页面出现竖向滚动条的解决方案
前端·javascript·vue.js
前端小万4 小时前
用 AI 写了个 VSCode 摸鱼插件,从开发到上架全过程
vue.js
蜡台4 小时前
Vue3 + ECharts 实现地图显示,深蓝色科技风地图、涟漪点、向上连线 ,标签
vue.js·科技·echarts·map·地图