在数据报表或管理系统中,经常需要按某个字段对数据进行分组,并计算每组的统计值(如求和、平均值),同时在表格底部显示所有数据的合计。vxe-table 提供了灵活的聚合配置 aggregateConfig 和自定义计算函数 calcValuesMethod,配合手动表尾 footerData,可以轻松实现这类需求。
本文将通过一个完整的示例,演示如何按角色(role)对数据进行分组,统计每个角色的 num 字段总和,并在表格尾部显示所有数据的 num 总计。
属性说明
| 配置项 / 属性 | 所在位置 | 作用 |
|---|---|---|
| aggregateConfig.groupFields | 表格配置(gridOptions) | 指定分组字段,例如 'role',表格会按该字段对数据进行分组,并在每个分组后插入"分组合计行" |
| aggregateConfig.calcValuesMethod | 表格配置 | 自定义分组统计值计算函数,接收当前分组的子数据(children)和列信息(column),返回统计值 |
| aggFunc | 列配置(columns) | 标记该列需要参与聚合计算,配合分组统计使用 |
| footerData | 表格配置 | 手动指定表尾合计行的数据,适用于简单的总计、平均值等,更复杂时可配合 updateFooter 方法动态计算 |
| rowGroupNode | 列配置 | 标识该列作为分组的显示节点,通常设置为 true,使分组后的行可以展开/折叠(示例中使用) |
分组聚合需要同时设置 aggregateConfig.groupFields 和至少一列的 rowGroupNode: true,同时要聚合的列必须设置 aggFunc: true(或自定义聚合函数)。
代码

html
<template>
<div>
<vxe-grid v-bind="gridOptions"></vxe-grid>
</div>
</template>
<script setup>
import { reactive } from 'vue'
// 表尾合计行数据(手动维护总计)
const footerSummaryRow = reactive({
seq: '总计',
num: 0
})
const gridOptions = reactive({
border: true,
showOverflow: true, // 超出显示省略号
showFooter: true, // 开启表尾
height: 500,
loading: false,
// 分组聚合配置
aggregateConfig: {
groupFields: ['role'], // 按角色字段分组
// 自定义分组统计值的计算方法
calcValuesMethod({ column, children }) {
// 只对 num 列进行求和
if (column.field === 'num') {
let sum = 0
children.forEach(item => {
sum += item.num
})
return sum
}
// 其他列不统计,返回 0 或空字符串
return 0
}
},
// 列定义
columns: [
{ type: 'seq', width: 70, title: '序号' },
{ field: 'name', title: '姓名', minWidth: 150, rowGroupNode: true }, // 分组显示节点
{ field: 'role', title: '角色', width: 120 },
{ field: 'num', title: '数量', width: 100, aggFunc: true }, // 参与聚合
{ field: 'age', title: '年龄', width: 80 },
{ field: 'address', title: '地址', minWidth: 200 }
],
data: [], // 动态加载的数据
footerData: [footerSummaryRow] // 表尾数据
})
// 模拟异步加载数据,并计算表尾总计
const loadData = () => {
gridOptions.loading = true
setTimeout(() => {
const rawData = [
{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Woman', age: 28, num: 63, date: '2025-02-01', address: 'test abc' },
{ id: 10002, name: 'Test2', role: 'Test', sex: 'Man', age: 22, num: 63, date: '2025-01-01', address: 'Guangzhou' },
{ id: 10003, name: 'Test3', role: 'PM', sex: 'Woman', age: 32, num: 10, date: '2025-05-01', address: 'Shanghai' },
{ id: 10004, name: 'Test4', role: 'Designer', sex: 'Man', age: 32, num: 24, date: '2025-01-01', address: 'test abc' },
{ id: 10005, name: 'Test5', role: 'Develop', sex: 'Man', age: 30, num: 98, date: '2025-01-01', address: 'Shanghai' },
{ id: 10006, name: 'Test6', role: 'Designer', sex: 'Man', age: 30, num: 46, date: '2025-03-01', address: 'test abc' },
{ id: 10007, name: 'Test7', role: 'Test', sex: 'Woman', age: 29, num: 35, date: '2025-05-01', address: 'test abc' },
{ id: 10008, name: 'Test8', role: 'PM', sex: 'Woman', age: 35, num: 84, date: '2025-11-01', address: 'test abc' },
{ id: 10009, name: 'Test9', role: 'Test', sex: 'Man', age: 21, num: 25, date: '2025-05-01', address: 'test abc' },
{ id: 10010, name: 'Test10', role: 'PM', sex: 'Woman', age: 28, num: 32, date: '2025-03-01', address: 'test abc' },
{ id: 10011, name: 'Test11', role: 'Test', sex: 'Woman', age: 29, num: 24, date: '2025-03-01', address: 'test abc' },
{ id: 10012, name: 'Test12', role: 'Develop', sex: 'Man', age: 37, num: 28, date: '2025-10-01', address: 'test abc' },
{ id: 10013, name: 'Test13', role: 'Test', sex: 'Woman', age: 24, num: 38, date: '2025-02-01', address: 'test abc' },
{ id: 10014, name: 'Test14', role: 'Develop', sex: 'Man', age: 34, num: 46, date: '2025-08-01', address: 'test abc' },
{ id: 10015, name: 'Test15', role: 'Designer', sex: 'Man', age: 21, num: 48, date: '2025-05-01', address: 'test abc' },
{ id: 10016, name: 'Test16', role: 'Designer', sex: 'Woman', age: 21, num: 59, date: '2025-10-01', address: 'test abc' },
{ id: 10017, name: 'Test17', role: 'Test', sex: 'Man', age: 31, num: 81, date: '2025-12-01', address: 'test abc' },
{ id: 10018, name: 'Test18', role: 'Develop', sex: 'Woman', age: 32, num: 75, date: '2025-10-01', address: 'test abc' },
{ id: 10019, name: 'Test19', role: 'Test', sex: 'Man', age: 37, num: 80, date: '2025-02-01', address: 'test abc' },
{ id: 10020, name: 'Test20', role: 'Develop', sex: 'Man', age: 41, num: 60, date: '2025-03-01', address: 'test abc' }
]
// 计算总计(num 总和)
let totalNum = 0
rawData.forEach(row => {
totalNum += row.num
})
footerSummaryRow.num = totalNum
gridOptions.data = rawData
gridOptions.loading = false
}, 200)
}
// 调用加载
loadData()
</script>
实现思路
- 分组显示:表格会按 role 字段将数据分组,同一角色的数据连续排列,并在每个分组前显示一个可展开/折叠的分组行(分组标题行)。
- 分组统计行:在每个分组的末尾,自动插入一行"分组合计",其中 num 列显示该组内所有 num 值的总和(由 * calcValuesMethod 自定义计算)。 表尾总计:表格底部固定一行(footerData),显示所有数据的 num 总计。该总计需要手动从数据源计算并赋值。
vxe-table 通过 aggregateConfig 和 aggFunc 提供了强大的分组聚合能力,结合自定义计算函数 calcValuesMethod 可以满足任意复杂的统计需求。而表尾合计通过 footerData 手动维护,灵活直观。两者配合使用,能够快速搭建具有分组统计和总计功能的专业数据表格。