吐了啊!产品要求树形多选组件支持单选,同时还要能全选

3,572 阅读1分钟

在树形组件显示复选框的情况下,check-strictly遵循父子不互相关联,通过在非叶子节点插槽增加按钮实现全选功能,从而实现树形组件单选情况下增加了全选功能。

实现效果

​编辑
demo

需求前情

在树形组件支持的原有模式下,要么是遵循父子互相关联,则无法单选一个非叶子节点(父级);要么无法遵循父子不互相关联,此模式下便是单选模式,当遇到大量选择的时候,需要逐个选择则无法满足快捷使用。当前的组件原有设计并没有完美的兼容模式,但是业务提出了这个需求,我们只要思考解决方案。

方案确认

  1. 确认单选模式,开启check-strictly
  2. 增加一个可全选的入口
  3. 全选情况下可反选

代码实现

全选、反选实现

<template>  
  <el-tree-v2  
    ref="treeV2Ref"  
    v-loading="loading"  
    :data="treeData"  
    :props="props.props"  
    :check-strictly="props.checkStrictly"  
    :showCheckbox="props.showCheckbox"  
    @check-change="treeChange"  
  >  
    <template #default="{ node, data }">  
      <span class="prefix" :class="{ 'is-leaf': node.isLeaf }">  
        <el-button  
          v-if="data.children && data.children.length"  
          link  
          type="primary"  
          @click.stop="nodeClick(node, data)"  
          :disabled="node.disabled"  
        >  
          <slot name="select-all-title">  
            [全选]  
          </slot>  
        </el-button>  
        {{ node.label }}  
      </span>  
    </template>  
  </el-tree-v2>  
</template>

<script setup>  
import { uniq, without } from 'lodash-es';

// 获取全部子节点  
/**  
 * @description:   
 * @param {Array} list  
 * @param {Array} result  
 * @return {Array}  
 */  
const getDeepChildren = (list, result = []) => {  
  list.forEach((item) => {  
    if (item.children && item.children.length > 0) {  
      getDeepChildren(item.children, result);  
    }  
    !item.disabled && result.push(item.key);  
  });  
  return result;  
};

/**  
 * @description: 判断存在未勾选  
 * @param {Array} checkedKeys  
 * @param {Array} childrens  
 * @return {Boolean}  
 */  
const isCheckAllChildren = (checkedKeys, childrens) => {  
  const setCheckedKeys = new Set();  
  checkedKeys.forEach((v) => setCheckedKeys.add(v));  
  for (let i = 0; i < childrens.length; i += 1) {  
    if (!setCheckedKeys.has(childrens[i])) {  
      return false;  
    }  
  }  
  return true;  
};  
/**  
 * @description: 全选点击  
 * @param {*} node  
 * @return {*}  
 */  
const nodeClick = (node) => {  
  if (node.disabled) return false;  
  const checkedKeys = treeV2Ref.value?.getCheckedKeys();  
  const childrens = [...getDeepChildren(node.children ?? []), node.key]; // 全选-包含本级  
  nextTick(() => {  
    let list = [];  
    if (isCheckAllChildren(checkedKeys, childrens)) {  
      // 反选  
      list = without(checkedKeys, ...childrens);  
      treeV2Ref.value.setCheckedKeys();  
    } else {  
      list = uniq([...checkedKeys, ...childrens]);  
    }  
    treeV2Ref.value.setCheckedKeys(list);  
    update(list);  
  });  
};  
</script>  

END

如果你有更好的建议和解决方案,可以多多探讨。

组件treeV2