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

相关推荐
假如让我当三天老蒯20 小时前
Options API(选项式 API) 和 Composition API(组合式 API)
前端·vue.js·面试
秃头网友小李4 天前
前端难点:keep-alive 缓存什么?RouterView 的 key 为什么要带 scopeId?
前端·vue.js
徐小夕4 天前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法
奋斗吧程序媛5 天前
补充一个小知识点:有关@click.native
前端·vue.js
英勇无比的消炎药5 天前
一行命令背后:TinyRobot CLI 如何重构 AI 对话接入的效率范式
vue.js·aigc
jay神5 天前
基于 FastAPI + Vue 的宠物领养管理系统
前端·vue.js·python·毕业设计·fastapi·宠物
一杯奶茶¥5 天前
水果销售网站 CRM客户信息管理系统 超市管理系 酒店管理系统 健身房管理系统 在线音乐网站 校园招聘系统
java·vue.js·spring boot·mysql·spring·java项目
英勇无比的消炎药5 天前
一站式搞定品牌风格:TinyRobot 主题定制从入门到精通
vue.js
尽欢i5 天前
Vue3 customRef 封神教程:防抖、本地存储、自动埋点一套搞定,模板干干净净
前端·javascript·vue.js
因_崔斯汀5 天前
Vue 模板编译:HTML 是怎么变成 JS 的?
前端·vue.js