在实际业务中,一个表格往往需要同时支持多种选择操作——例如“批量删除”和“批量导出”各自独立勾选,或者需要根据角色、权限展示不同的复选框列。vxe-table 提供了强大的插槽机制,允许你完全自定义表头和单元格的内容,从而轻松实现多列独立复选框,并且每一列的全选、半选状态都可以由业务逻辑单独控制。
本文通过一个完整的示例,演示如何在 vxe-grid 中实现两列互不干扰的复选框,每列都有独立的表头全选及半选状态,满足复杂业务场景的需求。
实现效果
- 第一列复选框:独立状态,支持全选/半选/全不选。
- 第二列复选框:与第一列完全隔离,同样支持独立的全选/半选逻辑。
- 表头复选框:点击时全选或清空当前列的所有行;当该列部分行选中时,表头显示半选状态(indeterminate)。
- 这种设计非常适合需要同时收集多种用户选择(例如“待审批”、“待导出”)的场景。
核心实现思路
- 利用插槽自定义列
- 在 columns 配置中,通过 slots.header 和 slots.default 分别指定表头和单元格使用的具名插槽。
- 行数据存储选中状态
- 每行数据中分别存储 checked1、checked2 等布尔值,与 v-model 双向绑定。
- 动态计算全选/半选状态
- 监听行内复选框的 change 事件,实时统计当前列选中的行数,从而更新对应表头复选框的 v-model 和 indeterminate 状态。
- 批量全选/反选
- 表头复选框变化时,遍历当前列所有行,统一设置其选中值,并重新计算全选/半选状态。
代码

<template>
<div>
<vxe-grid v-bind="gridOptions">
<template #checked1_header>
<vxe-checkbox v-model="isAllChecked1" :indeterminate="isIndeterminate1" @change="changeAll1Event"></vxe-checkbox>
</template>
<template #checked1_default="{ row }">
<vxe-checkbox v-model="row.checked1" @change="updateChecked1State"></vxe-checkbox>
</template>
<template #checked2_header>
<vxe-checkbox v-model="isAllChecked2" :indeterminate="isIndeterminate2" @change="changeAll2Event"></vxe-checkbox>
</template>
<template #checked2_default="{ row }">
<vxe-checkbox v-model="row.checked2" @change="updateChecked2State"></vxe-checkbox>
</template>
</vxe-grid>
</div>
</template>
<script setup>
import { ref, reactive} from 'vue'
// 全选状态(true: 全选, false: 全不选)
const isAllChecked1 = ref(false)
const isAllChecked2 = ref(false)
// 半选状态(true: 部分选中, false: 非部分选中)
const isIndeterminate1 = ref(false)
const isIndeterminate2 = ref(false)
const gridOptions = reactive({
border: true,
height: 500,
rowConfig: {
keyField: 'id',
isHover: true
},
columns: [
{ field: 'checked1', title: '', width: 60, slots: { header: 'checked1_header', default: 'checked1_default' } },
{ field: 'name', title: 'Name' },
{ field: 'checked2', title: '', width: 60, slots: { header: 'checked2_header', default: 'checked2_default' } },
{ field: 'role', title: 'Role' },
{ field: 'sex', title: 'Sex' },
{ field: 'address', title: 'Address' }
],
data: [
{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc', checked1: false, checked2: false },
{ id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou', checked1: false, checked2: false },
{ id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai', checked1: false, checked2: false },
{ id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 23, address: 'test abc', checked1: false, checked2: false },
{ id: 10005, name: 'Test5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai', checked1: false, checked2: false },
{ id: 10006, name: 'Test6', role: 'Designer', sex: 'Women', age: 23, address: 'test abc', checked1: false, checked2: false },
{ id: 10007, name: 'Test7', role: 'PM', sex: 'Women', age: 38, address: 'Shanghai', checked1: false, checked2: false },
{ id: 10008, name: 'Test8', role: 'Designer', sex: 'Man', age: 24, address: 'test abc', checked1: false, checked2: false },
{ id: 10009, name: 'Test9', role: 'Test', sex: 'Man', age: 35, address: 'Shanghai', checked1: false, checked2: false },
{ id: 10010, name: 'Test10', role: 'Develop', sex: 'Women', age: 31, address: 'Shanghai', checked1: false, checked2: false },
{ id: 10011, name: 'Test11', role: 'PM', sex: 'Women', age: 45, address: 'Shanghai', checked1: false, checked2: false },
{ id: 10012, name: 'Test12', role: 'Test', sex: 'Man', age: 39, address: 'Guangzhou', checked1: false, checked2: false },
{ id: 10013, name: 'Test13', role: 'Develop', sex: 'Women', age: 38, address: 'Shanghai', checked1: false, checked2: false }
]
})
// 更新第一列复选框的全选和半选状态
const updateChecked1State = () => {
const { data = [] } = gridOptions
const total = data.length
if (total === 0) {
isAllChecked1.value = false
isIndeterminate1.value = false
return
}
const checkedCount = data.filter((row) => row.checked1).length
isAllChecked1.value = checkedCount === total
isIndeterminate1.value = checkedCount > 0 && checkedCount < total
}
// 更新第二列复选框的全选和半选状态
const updateChecked2State = () => {
const { data = [] } = gridOptions
const total = data.length
if (total === 0) {
isAllChecked2.value = false
isIndeterminate2.value = false
return
}
const checkedCount = data.filter((row) => row.checked2).length
isAllChecked2.value = checkedCount === total
isIndeterminate2.value = checkedCount > 0 && checkedCount < total
}
// 全选/取消全选第一列
const changeAll1Event = () => {
const { data = [] } = gridOptions
const newValue = isAllChecked1.value
data.forEach((row) => {
row.checked1 = newValue
})
// 批量更新后手动更新一次状态(避免循环中频繁触发,直接最后同步状态)
updateChecked1State()
}
// 全选/取消全选第二列
const changeAll2Event = () => {
const { data = [] } = gridOptions
const newValue = isAllChecked2.value
data.forEach((row) => {
row.checked2 = newValue
})
updateChecked2State()
}
updateChecked1State()
updateChecked2State()
</script>
说明
- 插槽命名规范
- 表头插槽名称:[列字段]_header
- 单元格插槽名称:[列字段]_default
- 在 columns 中通过 slots 对象关联这些插槽名称。
- 全选与半选状态管理
- 全选状态 (v-model):true 表示当前列所有行都被选中。
- 半选状态 (indeterminate):true 表示当前列有部分行被选中,但不是全部。
- vxe-checkbox 组件原生支持这两个属性,直接绑定即可。
- 性能注意事项
- 行内复选框的 @change 事件中调用 updateCheckedXState 会遍历整个数组计算选中数量,对于大量数据(如万级)可能会影响性能。建议在数据量较大时使用防抖,或改用更高效的计算方式(如维护一个选中集合)。
- 支持动态数据
- 如果表格数据会动态增删(例如通过 gridOptions.data 重新赋值),需要在数据变化后重新调用 updateCheckedXState 来同步表头状态。
© 版权声明
THE END


![表情[baoquan]-拾光赋](https://blogs.ink/wp-content/themes/zibll/img/smilies/baoquan.gif)


暂无评论内容