el-table的树形结构结合多选框使用,实现单选父子联动,全选,反选功能

Element UI组件的树形结构结合多选框使用,目前是没有的,现在遇到了个需求,需要点击全选按钮,选中全部;点击父节点需选中其所有子节点,点击子节点需反选其父节点。

对上述需求,我封装了一个较为完整的组件来实现:

效果:

实现思路

1.表格增加一列选择项,并使用el-checkbox来勾选,自定义标题(也添上复选框,用来做全选,取消全选功能)

2.需要后端配合返回每列数据含有每列数据的勾选状态的字段,如有需要禁用(那也得返回禁用状态的字段),可配置selectionConfig来自定义勾选状态和禁用状态的字段名

3.实现全选功能:声明一个全选状态的字段,然后遍历table数据,并将每条数据赋值选状态字段的值,如有子节点,递归遍历其子节点并赋值

4.单选其中一列数据:设置其子节点的状态,寻找其父节点,如有父节点,则遍历其父节点所有的子节点,判断是否都为选中状态。接着递归往上遍历其父节点。最后遍历到顶级节点,没有父节点,判断顶级节点是否都为选中,是则将全选状态的字段设为true

 全选功能部分代码

// 全选
setCheckAll() {
  this.tableData.forEach(item => {
    if (!item[this.selectionConfig.disabled]) {
      item[this.selectionConfig.checked] = this.selectAll
    }
    this.setChildren(item, this.selectAll)
  })
}

// 设置子节点
setChildren(row, checked) {
  if (row[this.treeProps.children]) {
    row[this.treeProps.children].forEach(item => {
      if (!item[this.selectionConfig.disabled]) {
        item[this.selectionConfig.checked] = checked
      }
      if (row[this.treeProps.children]) {
        this.setChildren(item, checked)
      }
    })
  }
},

单选功能部分代码

// 单选
setCheck(row) {
  // 设置该单元格所有子节点状态
  this.setChildren(row, row[this.selectionConfig.checked])
  let result = this.setParentCheck(row)
  // 遍历到顶级,顶级无父节点,判断顶级节点是否都为选中
  if (!result) {
    let isAll = this.tableData.every(item => {
      return item[this.selectionConfig.checked]
    })
    this.selectAll = isAll
  }
},
// 设置父节点
setParentCheck(row) {
  // 寻找该行数据的父节点
  let parent = null
  for (let i = 0; i < this.tableData.length; i++) {
    parent = this.findItem(this.tableData[i], row.parentId)
    if (parent != null) {
      break
    }
  }
  // 遍历该行的父节点下,所有的子节点
  if (parent != null) {
    if (parent[this.treeProps.children]) {
      // 子节点是否全都选中,如果是则则勾选该节点
      let isAll = parent[this.treeProps.children].every(item => {
        return item[this.selectionConfig.checked]
      })
      parent[this.selectionConfig.checked] = isAll
      // 递归查找该行父级的父级节点
      this.setParentCheck(parent, parent[this.selectionConfig.checked])
    }
  } else { // 遍历到顶级,停止
    return null
  }
},
// 查递归找该行的父节点
findItem(row, id) {
  if (row.id == id) {
    return row
  }
  if (row[this.treeProps.children]) {
    let parent = null
    for (let i = 0; i < row[this.treeProps.children].length; i++) {
      parent = this.findItem(row[this.treeProps.children][i], id)
      if (parent) {
        break
      }
    }
    return parent
  } else {
    return null
  }
}

完整代码:

<template>
  <div>
    <el-table
      :data="tableData"
      :row-key="rowKey"
      :default-expand-all="defaultExpandAll"
      :tree-props="treeProps"
    >
      <!-- 开启树形多选 -->
      <el-table-column v-if="showSelection" width="120" align="left">
        <template #header>
          <el-checkbox
            v-model="selectAll"
            :checked="selectAll"
            @change="setCheckAll"
          />
          {{ selectionName }}
        </template>
        <template slot-scope="scope">
          <el-checkbox
            v-model="scope.row[selectionConfig.checked]"
            :checked="scope.row[selectionConfig.checked]"
            :disabled="scope.row[selectionConfig.disabled]"
            @change="setCheck(scope.row)"
          />
        </template> 
      </el-table-column>
      <el-table-column
        v-for="item in columnConfig"
        :key="item.prop"
        :prop="item.prop"
        :label="item.label"
        :width="item.width ? item.width : ''"
        :align="item.align"
      >
        <template slot-scope="scope">
          {{ item.ownDefinedFn ? item.ownDefinedFn(scope) : scope.row[item.prop] }}
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
  export default {
    props: {
      // 表格树形数据
      tableData: {
        type: Array,
        default: () => ([
          { id: '1', name: '目录1', parentId: '0', checked: false, children: [
            { id: '1-1', name: '目录1-1', parentId: '1', checked: false, children: [
              { id: '1-1-1', name: '目录1-1-1', parentId: '1-1', checked: false, children: [
                { id: '1-1-1-1', name: '目录1-1-1-1', parentId: '1-1-1', checked: false, children: [] },
                { id: '1-1-1-2', name: '目录1-1-1-2', parentId: '1-1-1', checked: false, children: [] },
                { id: '1-1-1-3', name: '目录1-1-1-3', parentId: '1-1-1', checked: false, children: [] },
              ] }
            ] }
          ] },
          { id: '2', name: '目录2', parentId: '0', checked: false, children: [] },
          { id: '3', name: '目录3', parentId: '0', checked: false, children: [] },
          { id: '4', name: '目录4', parentId: '0', checked: false, children: [] },
        ])
      },
      // 列配置项
      columnConfig: {
        type: Array,
        default: () => ([
          {
            prop: 'default',
            label: '默认',
            width: '200',
            align: 'center',
            ownDefinedFn: () => {
              return '默认' // 可返回函数
            }
          }
        ])
      },
      // 数据唯一标识
      rowKey: {
        type: String,
        default: 'id'
      },
      // 默认展开所有节点
      defaultExpandAll: {
        type: Boolean,
        default: true
      },
      // 渲染嵌套数据的配置选项
      treeProps: {
        type: Object,
        default: () => ({
          children: 'children'
        })
      },
      // 默认开启树形多选框
      showSelection: {
        type: Boolean,
        default: true
      },
      // 树形多选框标题
      selectionName: {
        type: String,
        default: ''
      },
      // 多选框配置
      selectionConfig: {
        type: Object,
        default: () => ({
          checked: 'checked',
          disabled: 'disabled'
        })
      }
    },
    data() {
      return {
        selectAll: false, // 全选
      }
    },
    methods: {
      // 全选
      setCheckAll() {
        this.tableData.forEach(item => {
          if (!item[this.selectionConfig.disabled]) {
            item[this.selectionConfig.checked] = this.selectAll
          }
          this.setChildren(item, this.selectAll)
        })
      },
      // 单选
      setCheck(row) {
        // 设置该单元格所有子节点状态
        this.setChildren(row, row[this.selectionConfig.checked])
        let result = this.setParentCheck(row)
        // 遍历到顶级,顶级无父节点,判断顶级节点是否都为选中
        if (!result) {
          let isAll = this.tableData.every(item => {
            return item[this.selectionConfig.checked]
          })
          this.selectAll = isAll
        }
      },
      // 设置子节点
      setChildren(row, checked) {
        if (row[this.treeProps.children]) {
          row[this.treeProps.children].forEach(item => {
            if (!item[this.selectionConfig.disabled]) {
              item[this.selectionConfig.checked] = checked
            }
            if (row[this.treeProps.children]) {
              this.setChildren(item, checked)
            }
          })
        }
      },
      // 设置父节点
      setParentCheck(row) {
        // 寻找该行数据的父节点
        let parent = null
        for (let i = 0; i < this.tableData.length; i++) {
          parent = this.findItem(this.tableData[i], row.parentId)
          if (parent != null) {
            break
          }
        }
        // 遍历该行的父节点下,所有的子节点
        if (parent != null) {
          if (parent[this.treeProps.children]) {
            // 子节点是否全都选中,如果是则则勾选该节点
            let isAll = parent[this.treeProps.children].every(item => {
              return item[this.selectionConfig.checked]
            })
            parent[this.selectionConfig.checked] = isAll
            // 递归查找该行父级的父级节点
            this.setParentCheck(parent, parent[this.selectionConfig.checked])
          }
        } else { // 遍历到顶级,停止
          return null
        }
      },
      // 查递归找该行的父节点
      findItem(row, id) {
        if (row.id == id) {
          return row
        }
        if (row[this.treeProps.children]) {
          let parent = null
          for (let i = 0; i < row[this.treeProps.children].length; i++) {
            parent = this.findItem(row[this.treeProps.children][i], id)
            if (parent) {
              break
            }
          }
          return parent
        } else {
          return null
        }
      }
    }
  }
</script>

<style lang="less" scoped>

</style>

  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值