vxe-table 实现多列独立复选框:灵活实现复杂业务逻辑

在实际业务中,一个表格往往需要同时支持多种选择操作——例如“批量删除”和“批量导出”各自独立勾选,或者需要根据角色、权限展示不同的复选框列。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>

说明

  1. 插槽命名规范
  • 表头插槽名称:[列字段]_header
  • 单元格插槽名称:[列字段]_default
  • 在 columns 中通过 slots 对象关联这些插槽名称。
  1. 全选与半选状态管理
  • 全选状态 (v-model):true 表示当前列所有行都被选中。
  • 半选状态 (indeterminate):true 表示当前列有部分行被选中,但不是全部。
  • vxe-checkbox 组件原生支持这两个属性,直接绑定即可。
  1. 性能注意事项
  • 行内复选框的 @change 事件中调用 updateCheckedXState 会遍历整个数组计算选中数量,对于大量数据(如万级)可能会影响性能。建议在数据量较大时使用防抖,或改用更高效的计算方式(如维护一个选中集合)。
  1. 支持动态数据
  • 如果表格数据会动态增删(例如通过 gridOptions.data 重新赋值),需要在数据变化后重新调用 updateCheckedXState 来同步表头状态。

https://vxetable.cn

原文链接:vxe-table 实现多列独立复选框:灵活实现复杂业务逻辑

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容