element ui 树状表格选择父节点子节点全选,子节点不全选父节点半选

最近开发了一个需求,element 树状表格,表格全选时所有项(包括所有子节点)都选中,选中树状表格父节点时,这个父节点下所有子节点也都要选中,如果某个父节点下的所有子节点没有全部,则这个父节点处于半选状态

效果图:

1.HTML

<template>
  <el-table
    v-loading="loading"
    :data="orderList"
    @selection-change="handleSelectionChange"
    :row-key='rowKeyFunc'
    :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
    :row-class-name="rowClassNameFun"
    ref="table"
    @select-all="selectAllFun"
    @select="selectFun"
  >
    <el-table-column type="selection" width="55" align="center"/>
    <el-table-column label="运营商" align="center" prop="providerType"  width="120px"/>
    <el-table-column
      label="创建时间"
      align="center"
      prop="gmtCreate"
      width="180"
    >
    </el-table-column>
    <el-table-column
      label="客户编号"
      align="center"
      width="200"
      prop="customerNo"
    />
    <el-table-column
      label="系统单号"
      align="center"
      width="200"
      prop="orderNo"
    />
  </el-table>
</template>

2.JS

<script>
export default {
  name: "index",
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 总条数
      total: 0,
      // 运单管理表格数据
      orderList: [],
      // 查询参数
      queryParams: {

      },
      page: {
        pageNum: 1,
        pageSize: 15,
      },
      oneProductIsSelect:[],
      isFun:false
    };

  },
  created() {
    this.getList();
  },
  methods: {
    // 查询列表
    getList() {
      this.loading = true;
      listOrder(this.queryParams, this.page).then((response) => {
        this.orderList=response.rows
        //这一步是为了将父和子之间有联系,给子定义一个taskId,将子的taskId和父的id一致
        this.orderList.forEach((item, index) => {
          if (item.children) {
            item.children.forEach((cItem, cIndex) => {
              cItem.taskId = item.id;
            });
          }
        });
        // 由于后端返回的数组id不唯一(父里的id和其中一个子的id一样),然后:row-key='id'里面的id要是唯一值,所以处理了一下,将父的id改变,将数组里面的id都唯一,当然,你可以跟后端商量一下,返回给你一个唯一值,这个处理代码就可以省略了
        this.orderList = this.orderList.map((item,index)=>{
          return{
            ...item,
            uuid:`${index}-${this.guid()}`
          }
        })
        this.total = response.total; //页数
        this.loading = false; // 遮罩层
        this.initData(this.orderList)
      });
    },
    //生成唯一ID
    guid() {
      return Number(
        Math.random().toString().substr(3, 3) + Date.now()
      ).toString(36);
    },
    //row-key唯一值
    rowKeyFunc(row){
      if(row.uuid){
        return row.uuid
      }else {
        return row.id
      }
    },
    //初始化数据,将数据都用isSelect标记一下,isSelect为false不选中、true选中、half半选
    initData(data) {
      data.forEach((item) => {
        item.isSelect = false; //默认为不选中
        if (item.children && item.children.length) {
          this.initData(item.children);
        }
      });
    },
    // 判断是不是全选
    checkIsAllSelect() {
      this.oneProductIsSelect = [];
      this.orderList.forEach((item) => {
        this.oneProductIsSelect.push(item.isSelect);
      });
      //判断一级产品是否是全选.如果一级产品全为true,则设置为取消全选,否则全选
      let isAllSelect = this.oneProductIsSelect.every((selectStatusItem) => {
        return true == selectStatusItem;
      });
      return isAllSelect;
    },
    // 全选或者全不选(这个是祖父的勾选)
    selectAllFun(selection) {
      let isAllSelect = this.checkIsAllSelect();
      this.orderList.forEach((item) => {
        item.isSelect = isAllSelect;
        this.$refs.table.toggleRowSelection(item, !isAllSelect);
        this.selectFun(selection, item);
      });
    },
    selectFun(selection, row) {
      this.setRowIsSelect(row);
    },
    setRowIsSelect(row) {
      //当点击父级点复选框时,当前的状态可能为未知状态,所以当前行状态设为false并选中,即可实现子级点全选效果
      if (row.isSelect == "half") {
        row.isSelect = false;
        this.$refs.table.toggleRowSelection(row, true);
      }
      row.isSelect = !row.isSelect;
      //判断操作的是子级点复选框还是父级点复选框,如果是父级点,则控制子级点的全选和不全选
      if (row.children && row.children.length > 0) {
        row.children.forEach((item) => {
          item.isSelect = row.isSelect;
          this.$refs.table.toggleRowSelection(item, row.isSelect);
        });
      } else {
        //操作的是子节点  1、获取父节点  2、判断子节点选中个数,如果全部选中则父节点设为选中状态,如果都不选中,则为不选中状态,如果部分选择,则设为不明确状态
        let parentId = row.taskId;
        this.orderList.forEach((item) => {
          let isAllSelect = [];
          if (item.id == parentId) {
            if(item.children){
              item.children.forEach((databaseSourceListItem) => {
                isAllSelect.push(databaseSourceListItem.isSelect);
              });
            }
            if (
              isAllSelect.every((selectItem) => {
                return true == selectItem;
              })
            ) {
              item.isSelect = true;
              this.$refs.table.toggleRowSelection(item, true);
            } else if (
              isAllSelect.every((selectItem) => {
                return false == selectItem;
              })
            ) {
              item.isSelect = false;
              this.$refs.table.toggleRowSelection(item, false);
            } else{
              item.isSelect ="half";
            }
          }
        });
      }
    },
    rowClassNameFun({row}){
      if(row.isSelect=='half'){
        return "indeterminate";
      }
    },
   handleSelectionChange(selection) {
      let ids = []
      let waybill=[]
      selection.forEach(node => {
        if(node.isSelect==true){//如果上来勾选了
        if(node.children && node.children.length > 0){//判断是否有子元素,如果有子,遍历子
            node.children.forEach(child=>{
              if(child.isSelect){
                let index = ids.findIndex(item =>
                  item.outOrderNo === child.outOrderNo
                  && item.packageNo === child.packageNo
                );
                if(index === -1){
                  ids.push({
                    outOrderNo: child.outOrderNo,
                    packageNo: child.packageNo
                  })
                }
                if (!waybill.includes(child.trackNumber)) {
                  waybill.push(child.trackNumber);
                }
              }
            })
        }else {//如果没有子,
          console.log('选中')
          let index = ids.findIndex(item =>
            item.outOrderNo === node.outOrderNo
            && item.packageNo === node.packageNo
          );
          if(index===-1){
            ids.push({
              outOrderNo: node.outOrderNo,
              packageNo: node.packageNo
            })
          }
          if (!waybill.includes(node.trackNumber)) {
            waybill.push(node.trackNumber);
          }
        }
        }else if(node.isSelect=="half"){//如果是半选,遍历子,将勾选的子进行push
          console.log('半选')
          if(node.children && node.children.length > 0){
            node.children.forEach(child=>{
              if(child.isSelect){
              }
            })
          }else {
          }

        }else {
          console.log('取消')
        }
      })
      this.waybill=waybill
      this.ids = ids
      this.single = this.ids.length !== 1;
      this.multiple = !this.ids.length
    },
  },
};
</script>

这个里面需要有个注意的地方,当父里面有两个子,勾选父,@selection-change="handleSelectionChange"的handleSelectionChange会跑三遍,一遍父、一边父与子、一边子

    // 多选框选中数据
    handleSelectionChange(selection) {
      console.log(selection,'selection')
      }

这个里面还是比较混乱的,打印出来三次,这个我是可以理解的,因为勾选了三个,但是打印的这个顺序,我不太懂,由于有个勾选之后,操作批量下载和批量取消,不勾选的时候,是禁止点击批量下载和批量取消的

效果图

原来代码逻辑

    // 多选框选中数据
    handleSelectionChange(selection) {
      console.log(selection,'selection')
      this.single = selection.length !== 1;
      this.multiple = !selection.length
      this.ids = selection.map((item) => item.trackNumber);
    },

 按理说,这个就能实现以上的功能,但是,没有实现,我进行取消勾选,还是打印了三次,最后一次有值,所以以上代码不能实现此功能

 于是我就对上面的代码进行了改进

    // 多选框选中数据
     handleSelectionChange: _.debounce(function(selection) {
        let ids = []
        let waybill=[]
        selection.forEach(node => {
          console.log(node.isSelect,node)
          if(node.isSelect==true){//如果上来勾选了
          if(node.children && node.children.length > 0){//判断是否有子元素,如果有子,遍历子
              node.children.forEach(child=>{
                if(child.isSelect){
                  let index = ids.findIndex(item =>
                    item.outOrderNo === child.outOrderNo
                    && item.packageNo === child.packageNo
                  );
                  if(index === -1){
                    ids.push({
                      outOrderNo: child.outOrderNo,
                      packageNo: child.packageNo
                    })
                  }
                  if (!waybill.includes(child.trackNumber)) {
                    waybill.push(child.trackNumber);
                  }
                }
              })
          }else {//如果没有子,
            let index = ids.findIndex(item =>
              item.outOrderNo === node.outOrderNo
              && item.packageNo === node.packageNo
            );
            if(index===-1){
              ids.push({
                outOrderNo: node.outOrderNo,
                packageNo: node.packageNo
              })
            }
            if (!waybill.includes(node.trackNumber)) {
              waybill.push(node.trackNumber);
            }
          }
          }else if(node.isSelect=="half"){//如果是半选,遍历子,将勾选的子进行push
            if(node.children && node.children.length > 0){
              node.children.forEach(child=>{
                if(child.isSelect){
                }
              })
            }else {
            }

          }else {
          }
        })
        this.waybill=waybill
        this.ids = ids
        this.single = this.ids.length !== 1;
        this.multiple = !this.ids.length
    }, 300),

根据上面取到的this.ids、this.waybill,来进行批量取消操作,可根据自己的代码逻辑进行修改

    //取消(取消里面改动点:父packageNo为空,子packageNo有值,后端根据packageNo来进行父与子取消区分)
    handleCancal(row) {
      //操作里面的取消
      let trackNumber
      //批量取消
      let trackNumbers
      let outOrderNo
      let outOrderNos
      if (row.trackNumber) {
        trackNumber = row.trackNumber.split(',')
        outOrderNo = [{
          outOrderNo:row.outOrderNo,
          packageNo:row.packageNo,
        }]
      } else {
        trackNumbers = this.waybill
        outOrderNos=this.ids
      }
      //处理单独取消的弹框换行
      let arry
      let newData = []
      for (let index in trackNumber) {
        newData.push(trackNumber[index] + "<br/>")
      }
      arry = newData.join('')
      //处理批量取消的弹框换行
      let arrys
      let newDatas = []
      for (let index in trackNumbers) {
        newDatas.push(trackNumbers[index] + "<br/>")
      }
      arrys = newDatas.join('')
      let parmas = outOrderNo || outOrderNos
      this.$confirm(
        this.$t('order.surecan') + "<br/>" + (arry || arrys) + this.$t('order.waybill') + "<br/>" + this.$t('order.notee'),
        {
          confirmButtonText: this.$t('operation.confirm'),
          cancelButtonText: this.$t('operation.cancel'),
          type: "warning",
          dangerouslyUseHTMLString: true,
        })
        .then(function () {
          return cancalOrder(parmas);
        })
        .then(() => {
          this.getList();
          this.msgSuccess(this.$t('msg.application'));
        })
        .catch(function () {
        });
    },

3.CSS

半选的样式

  

<style lang="scss" scoped >
/deep/.indeterminate {
  .el-table-column--selection .cell .el-checkbox {
    display: block !important;
  }
  .el-checkbox__input .el-checkbox__inner {
    background-color: #4a97eb !important;
    border-color: #4a97eb !important;
    color: #fff !important;
  }
}
/deep/.indeterminate .el-checkbox__input.is-checked .el-checkbox__inner::after {
  transform: scale(0.5);
}
/deep/.indeterminate .el-checkbox__input .el-checkbox__inner::after {
  border-color: #c0c4cc !important;
  background-color: #c0c4cc;
}

/deep/.indeterminate .el-checkbox__input .el-checkbox__inner::after {
  content: "";
  position: absolute;
  display: block;
  background-color: #fff;
  height: 2px;
  transform: scale(0.5);
  left: 0;
  right: 0;
  top: 5px;
  width: auto !important;
}
</style>

 以上就可以实现树状表格选择父节点子节点全选,子节点不全选父节点半选,但是对于handleSelectionChange,里面处理数据还是很懵的,虽然解决了,但是感觉不是最优的方法,如果有好的解决方法,可以分享下,欢迎分享和指正。

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 16
    评论
Element Plus 的树形表格也可以使用类似的方式实现全选功能。以下是示例代码: ```html <template> <div> <el-checkbox v-model="allChecked" @change="selectAll">全选</el-checkbox> <el-table :data="tableData" style="width: 100%"> <el-table-column type="selection" v-model="checkedItems"></el-table-column> <el-table-column prop="name" label="名称"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> </el-table> </div> </template> <script> import { ref, computed } from 'vue'; import { ElTable, ElTableColumn, ElCheckbox } from 'element-plus'; export default { components: { ElTable, ElTableColumn, ElCheckbox }, setup() { const tableData = ref([ { id: 1, name: 'John', age: 25, children: [ { id: 11, name: 'Mike', age: 22 }, { id: 12, name: 'Mary', age: 20 } ] }, { id: 2, name: 'Tom', age: 30, children: [ { id: 21, name: 'Jack', age: 28 }, { id: 22, name: 'Lucy', age: 26 } ] } ]); const checkedItems = ref([]); const checkAll = ref(false); const allChecked = computed({ get() { return checkedItems.value.length === tableData.value.length; }, set(val) { checkedItems.value = val ? tableData.value.slice() : []; } }); function selectAll() { checkedItems.value = allChecked.value ? [] : tableData.value.slice(); } return { tableData, checkedItems, checkAll, allChecked, selectAll }; } }; </script> ``` 在上面的代码中,我们使用 Element Plus 的 `ElTable` 和 `ElTableColumn` 组件来实现树形表格,用 `type="selection"` 的方式来添加选择列,将其与 `checkedItems` 变量绑定。在全选框中,我们使用 `ElCheckbox` 组件,并将其与 `allChecked` 变量绑定,使用 `@change` 事件监听全选框的状态变化,通过 `selectAll` 方法实现全选功能。 需要注意的是,在树形表格中,每一行数据可能还包含节点,因此我们需要修改计算全选状态的逻辑,同时需要修改全选的实现方式。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿wei程序媛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值