elementUI-cascader-全选功能

先看效果图:

github项目地址:地址

首先,想了解cascader组件的整体结构的,可以先看这篇文章

我们魔改cascader整个组件的流程如下:

  1. 添加全选按钮,在页面上先看到。
  2. 添加全选按钮的点击逻辑。
  3. 根据cascader-node的集合的check状态去更新全选按钮的状态。

第一步:添加全选按钮

cascader-node组件是在cascader-menu组件中渲染的,渲染代码如下:

   renderNodeList(h) {      
     const { menuId} = this;      
     const { isHoverMenu } = this.panel;     
     const events = { on: {} };      
     if (isHoverMenu) {        
        events.on.expand = this.handleExpand;     
     }     
     // 此处就是要渲染的cascader-node集合   
     const nodes = this.nodes.map((node, index) => {     
         const { hasChildren } = node;        
         return (          
             <cascader-node            
                key={ node.uid }           
                node={ node }            
                node-id={ `${menuId}-${index}` }      
                aria-haspopup={ hasChildren }         
                aria-owns = { hasChildren ? menuId : null }        
                { ...events }></cascader-node>       
          );      
      });      
      // 渲染出cascader-node集合,我们只要这里插入全选组件即可。
      return [        
         ...nodes,        
         isHoverMenu ? <svg ref='hoverZone' class='el-cascader-menu__hover-zone'></svg> : null     
      ];   
 }
复制代码

添加全选按钮之后的代码如下:

     return [        
        config.checkAll &&
        index === 0 &&
        <el-checkbox          
           class="checkAll"         
           indeterminate={isIndeterminate}    
           value={checkAll} 
           onChange={handleCheckAllChange}>全选</el-checkbox>,
        ...nodes,       
        isHoverMenu ? <svg ref='hoverZone' class='el-cascader-menu__hover-zone'></svg> : null      
     ];
复制代码

config.checkAll就是给cascader组件传入的props参数。

比如要我要开启全选功能,那么就要传入checkAll: true的参数。

 <el-cascader
    :options="options"
    :props="props"
    collapse-tags
    clearable></el-cascader>

data() {
      return {
        props: { multiple: true, checkAll: true, expandTrigger: 'hover' },
      }
}
复制代码

这个全选item就会被渲染出来。

第二步:添加全选按钮的点击逻辑

建议先看看el-checkbox这个组件的’全选‘、’没选‘、’没全选‘这三中状态的逻辑,地址

再来看一遍cascader-menu的渲染代码块:

  return [        
     config.checkAll && 
     index === 0 &&
     <el-checkbox          
       class="checkAll"          
       indeterminate={isIndeterminate}          
       value={checkAll}          
       onChange={handleCheckAllChange}>全选</el-checkbox>,       
     ...nodes,       
     isHoverMenu ? <svg ref='hoverZone' class='el-cascader-menu__hover-zone'></svg> : null     
  ];
复制代码

我们给el-checkbox组件添加了两个属性和一个方法:

  • value:true->全选,false->待定。
  • indeterminate: 当value为true,该属性无效;当value为false,该属性为false则没选,为true则没全选。
  • onChange:点击该按钮时触发。

先看onChange的触发函数,请仔细查看代码注释:

 handleCheckAllChange() {      
     const { nodes, panel } = this;      
     // 反转全选按钮的状态      
     this.checkAll = !this.checkAll;     
     for (let i = 0; i < nodes.length; i++) {        
        const node = nodes[i];        
        // 触发当前cascader-menu组的所有cascader-node组件的的doCheck方法,效果和背后逻辑与点击cascader-node是一样的。        
        node.doCheck(this.checkAll, true);      
     }      
     // 触发cascader-panel的方法,这方法主要是更新checkValue的值。      
     panel.calculateMultiCheckedValue();    
  },
复制代码

我们在cascader-menu这个组件内写一个方法updateInDeterminate,根据cascader-node集合的check状态来更新全选按钮的状态。

    updateInDeterminate() {      
      const { panel, nodes } = this;      
      if (panel.config.checkAll) {        
        let counter = 0;        
        for (let i = 0; i < nodes.length; i++) {         
            const node = nodes[i];          
            (node.checked || node.indeterminate) && counter++;        
        }        
        this.checkAll = counter === nodes.length;        
        this.isIndeterminate = !(counter === nodes.length || counter === 0);      
        }    
    },
复制代码

至于updateInDeterminate这个方法的触发时机是:

1、cascader-menu创建时

created() {    
    if (this.panel.config.checkAll && this.index === 0) {      this.updateInDeterminate();      this.$on('updateInDeterminate', this.updateInDeterminate);    } 
},
复制代码

2、cascader-menu中传入的node参数变化时

watch: {    
    nodes() {      
        this.panel.config.checkAll && this.index === 0 && this.updateInDeterminate();        }  
}
复制代码

3、cascader-panel组件中checkValue(这个指的是cascader选中的值)变化时

watch: {    
    checkedValue(val) {      
        if (!isEqual(val, this.value)) {       
             this.checkStrictly && this.calculateCheckedNodePaths();        
             // 此处触发        
             this.broadcast('ElCascaderMenu', 'updateInDeterminate');        
             this.$emit('input', val);        
             this.$emit('change', val);      
        }    
    }  
}
复制代码

4、删除cascader组件的tag时

deleteTag(tag) {      
    const { checkedValue, panel } = this;      
    const current = tag.node.getValueByOption();      
    const val = checkedValue.find(n => isEqual(n, current));      
    this.checkedValue = checkedValue.filter(n => !isEqual(n, current));      
    // 如果全选功能开启了,那么删除tag时,就得触发updateInDeterminate函数      
    if (this.config.checkAll) {        
        this.$nextTick(() => {          
            panel.$refs.menu.forEach((menu) => {            
                menu.updateInDeterminate();          
            });       
        });      
    }      
    this.$emit('remove-tag', val);   
},
复制代码

其实,只要在cascader-menu组件中通过deep监听node集合的check属性变化来触发updateInDeterminate函数就行了。但是这样会降低组件性能,所以没采用。

おすすめ

転載: juejin.im/post/7041486698961698824