Element cascader级联多选器增加对于某一子层的全选功能

目录

背景说明

最终实现效果

全选判断

实现单选

全选功能

使用自定义插槽后的样式问题

添加的checkedbox点lable就能改变状态的问题


背景说明

公司业务要求实现element 级联选择器中对于某一子层级实现全选效果(父子不联动),并且所有数据是默认全部选上的。

最终实现效果

其中一个子节点取消以后

全选判断

因为数据已经是全部选上了,所以默认提供的change事件回调参数getCheckedNodes方法 没什么用了。

在页面初始化的时候把全选当作一个节点加入mechanismList.list里面(作用是为了方便控制状态,全选节点的信息设置成不会影响业务就行)

下面是这段初始化代码(ps:不是我写的,随便看看就好)

这段代码大致作用就是在除了根节点以外所有子节点所在层级的第一个位置加上一个全选节点

getBranchTree() {
      serve
        .getBranchTreeList()
        .then((res) => {
          mechanismList.data.length = 0
          mechanismList.data.push(...res)
          mechanismList.list.length = 0
          mechanismList.list.push(...res)
          mechanismList.list.map((item) => {
            if (item.childBranchList) {
              item.childBranchList.unshift({ branchName: '全选', branchId: saveBranchId.value })
              saveBranchId.value = saveBranchId.value - 1
              item.childBranchList.map((item1) => {
                if (item1.childBranchList) {
                  item1.childBranchList.unshift({
                    branchName: '全选',
                    branchId: saveBranchId.value
                  })
                  saveBranchId.value = saveBranchId.value - 1
                  item1.childBranchList.map((item2) => {
                    if (item2.childBranchList) {
                      item2.childBranchList.unshift({
                        branchName: '全选',
                        branchId: saveBranchId.value
                      })
                      saveBranchId.value = saveBranchId.value - 1
                    }
                  })
                }
              })
            }
          })
          mechanismList.list.map((item) => {
            idNode.parent.push(item.branchId)
            saveChangeList.parent.push(item.branchId)
            if (item.childBranchList) {
              item.childBranchList.map((item1) => {
                idNode.parent.push(item1.branchId)
                saveChangeList.parent.push(item1.branchId)
                if (item1.childBranchList) {
                  item1.childBranchList.map((item2) => {
                    idNode.parent.push(item2.branchId)
                    saveChangeList.parent.push(item2.branchId)
                    if (item2.childBranchList) {
                      item2.childBranchList.map((item3) => {
                        idNode.parent.push(item3.branchId)
                        saveChangeList.parent.push(item3.branchId)
                      })
                    }
                  })
                }
              })
            }
          })
          idNode.children = idNode.parent
          //idNode.newparent = idNode.parent

          saveChangeList.children = saveChangeList.parent
          saveChangeList1.parent = saveChangeList.parent
          selectLengthOld.value = idNode.parent.length

          // saveChangeListnew.children = saveChangeList.children
          // saveChangeListnew.parent = saveChangeList.parent

          // saveChangeList1new.parent = saveChangeList1.parent
          // selectLengthOldnew.value = idNode.newparent.length
          mechanismListnew.list.length = 0
          mechanismListnew.list.push(...mechanismList.list)
          mechanismListnew.data.length = 0
          mechanismListnew.data.push(...mechanismList.data)
        })
        .catch((err) => {})
    },

加入这个全选后我们就可以在自定义插槽中比较方便的区分出全选节点和其他节点的区别。

然后我的做法是在级联选择器的插槽中自定义了一个全选选中框(自己放了一个checkedBox进去)

<el-cascader
                v-if="casaderBeing"
                ref="getNode"
                v-model="idNode.parent"
                :options="mechanismList.list"
                :show-all-levels="false"
                :props="props"
                :debounce="300"
                size="default"
                collapse-tags
                popper-class="popper"
                clearable
                filterable
                :filter-method="filterMechanism"
                @expand-change="expandChange"
              >
                <template #default="{ node, data }">
                  <template v-if="data.branchName != '全选'">
                    <el-checkbox
                      class="newcheckbox"
                      v-model="node.checked"
                      :checked="node.checked"
                      @click.stop.prevent="select(node, 'parent')"
                      ><span @click.stop.prevent="select(false)">{{
                        data.branchName
                      }}</span></el-checkbox
                    >
                  </template>
                  <template v-else>
                    <el-checkbox
                      class="newcheckbox"
                      v-model="node.checked"
                      :checked="node.checked"
                      @click.stop.prevent="selectAll(node, 'parent')"
                      ><span>{{ data.branchName }}</span></el-checkbox
                    >
                  </template>
                </template>
              </el-cascader>
  const props = {
    checkStrictly: true,
    multiple: true,
    value: 'branchId',
    label: 'branchName',
    children: 'childBranchList'
  }

这么做是因为不使用自定义插槽的话,无法获取到当前点击层级的节点(node)信息(也可能是我不知道别的获取方法)。

在给checkedbox设置上click事件并把node传进去后,我们就能获取当前点击节点的信息了

我们将node打印出来

  1. 可以看到里面有父节点(parent)的信息和子节点(children)信息,以及一个checked(用于控制checkedbox的选中状态,要将其绑定到我们添加的checkedbox上)
  2. 有了父节点的信息我们就可以去到父节点来观察其所有子节点的状态(也就当前点击节点所在的层级)

以下代码中所有idNode[val]全当成idNode.parent,级联多选器已经选中的节点数组

对idNode[val]的push操作相当于选中加进去的节点,splice相当于取消选中

  //全选按钮是否勾选
function whetherSelect(node, val) {

//有父节点
    if (node.parent) {
      //查找全选节点
      let selectAllNode = node.parent.children.find((item) => item.label == '全选')
      //全选节点在idNode中的下标
      let index = idNode[val].findIndex((item) => item == selectAllNode.value)
      //判断当前全选状态
      if (selectAllNode.checked == false) {
        //首先假定为全选状态
        selectAllNode.checked = true
        if (index != -1) {
          idNode[val].push(a.value)
        }
      }

      node.parent.children.forEach((item) => {
        if (item.checked == false) {
          //判断为非全选状态
          selectAllNode.checked = false
        }
      })
      if (selectAllNode.checked == false) {
        if (index != -1) {
          idNode[val].splice(index, 1)
        }
      }
    }
  }

实现单选

实现全选的判断后,非全选节点的单选功能就比较简单了

把节点的状态取反就可以了,在函数最后用上全选判断的函数就可以了

  1. 在非全选节点上绑定点击事件select(要注意使用stop.prevent阻止checkedbox的默认事件,否则checkedbox点了没效果,因为checkedbox绑定checked后自己也会去修改节点的状态,会和下面在函数里面修改状态产生冲突)
  2. 可能会出现单选节点的状态被正确修改了,但是级联多选器上的tag并没有增加上去

    可以使用级联多选器上面的calculateCheckedValue(重新计算的方法,通过ref去获取),这个方法element组件说明中并没有提到,和同事试了好久才发现的
//单选
  /**
   *
   * @param {当前点击数据节点} node
   * @param {处理哪个数组} val
   */
  function select(node, val) {
    //当node为false时说明是通过点击lable触发的(默认不使用lable)
    if (!node) {
      return
    }

    if (node.checked) {
      node.checked = false
      //勾选状态判断
      let index = idNode[val].findIndex((e) => e == node.value)
      if (index != -1) {
        //是否有找到下标
        idNode[val].splice(index, 1)
      }
    } else {
      node.checked = true
      idNode[val].push(node.value)
    }
    //判断全选
    whetherSelect(node, val)
    //更新选中节点数量
    getNode.value.cascaderPanelRef.calculateCheckedValue()
  }

全选功能

最后就是去实现全选功能了,也是比较简单

  1. 先判断全选节点的状态,如果是要取消全选则把这一层所有节点的checked设置为false
  2. 然后把他们从级联多选器已经选中的节点数组(idNode[val])中去掉就可以了
function selectAll(node, val) {
    //勾选状态判断
    if (!node.checked) {
      //全选
      node.parent.children.forEach((nodeItem) => {
        if (!nodeItem.checked) {
          nodeItem.checked = true
          idNode[val].push(nodeItem.value)
        }
      })
    } else {
      //取消全选
      let index = -1
      node.parent.children.forEach((nodeItem) => {
        nodeItem.checked = false
        index = idNode[val].findIndex((e) => e == nodeItem.value)
        if (index != -1) {
          idNode[val].splice(index, 1)
        }
      })
    }
    //更新选中节点数量
    getNode.value.cascaderPanelRef.calculateCheckedValue()
  }

使用自定义插槽后的样式问题

在使用了自定义插槽后我们会发现出现了两个checkedbox

我们得想办法把左边的隐藏掉保留我们右边想要的

  • 给我们自定义的checkbox添加一个叫newcheckbox的类名
  • 通过元素选择器我看能看见左边的checkedbox和我们添加的checkedbox 是相邻的
  • 但是我们添加的checkedbox被包裹在了el-cascader-node__label里面
  • 所以我们就需要找出所有与包含了newcheckbox的el-cascader-node__label相邻的.el-checkbox并把它们隐藏掉

最后我们使用如下的样式,就可以将左边的checkbox隐藏掉

注意:不可以使用Vue的scoped,这会让样式失效

至于为什么使用了scoped会让样式不起作用,我暂时也没有弄清楚(即使是使用:deep()),希望有大佬能来解答一下

  .el-cascader-node > .el-checkbox:has(+ span.el-cascader-node__label > .newcheckbox) {
    display: none;
  }

添加的checkedbox点lable就能改变状态的问题

如果只想点击选择框去改变状态,可以将checkedbox的文字套一层span标签

然后设置一个没效果的点击事件并阻止默认事件即可

(网上好像并没有找到比较好的解决办法,也没有深入去研究)

<el-checkbox
                      class="newcheckbox"
                      v-model="node.checked"
                      :checked="node.checked"
                      @click.stop.prevent="select(node, 'parent')"
                      ><span @click.stop.prevent="select(false)">{{
                        data.branchName
                      }}</span></el-checkbox
                    >

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值