ElementUI-cascader-同一个层级的节点具有单选功能

1,300 阅读2分钟

项目地址:地址

演示地址,可以直观看到效果:地址

效果图:设置第二层是互斥层级(节点具有单选功能)

录制_2021_12_20_14_28_56_959.gif

首先,需要了解cascader组件的底层逻辑,以父子关联、互斥层级为第二层为前提。

当用户随便点击了一个节点,那么节点组件cascader-node便会执行以下三个步骤:

  1. 通知其子代更新选中状态
  2. 根据其子代的选中情况带更新自身的情况
  3. 通知其父代更新
  // 用户点击节点
  doCheck(checked) {
    if (this.checked !== checked) {
      if (this.config.checkStrictly) {
        this.checked = checked;
      } else {
        // 通知其子代更新选中状态
        this.broadcast('check', checked);
        // 根据其子代的选中情况带更新自身的情况
        this.setCheckState(checked);
        // 通知其父代更新
        this.emit('check');
      }
    }
  }

目标:同一层级的节点互斥。

我们要分三种情况:

一、点击的节点处于互斥层级,我们直接在第二步的

    // 根据其子代的选中情况带更新自身的情况
    this.setCheckState(checked);

里面将同层级的其他节点给取消。

// 当前选择的节点属于互斥层级,则将其兄弟节点,全取消checked
  mutualExclusion() {
    // 互斥逻辑
    const {checked, indeterminate, level, value, parent, config} = this;
    if (!checked && !indeterminate) return;
    if (config.mutualExcluseLabel && config.mutualExcluseLabel === level) {
      // 当互斥层级设置为1时,会很特殊,此处不讲
      if (level === 1) {
        this.firstLevelNodes.forEach(el => {
          el.value !== value && (el.checked || el.indeterminate) && !el.isDisabled && (el.doCheck(false, true, false));
        });
      } else {
        // 直接看这里,互斥层级非1时,直接通过节点的parent节点拿到节点的兄弟节点,然后设置互斥即可。
        parent.children.forEach(el => {
          el.value !== value && (el.checked || el.indeterminate) && !el.isDisabled && (el.doCheck(false, true, false));
        });
      }
    }
  }

二、点击的节点所处的层级是大于互斥层级时,处理逻辑和情况一一样

三、点击的点击所处的层级是互斥层级的父层级时,得分情况

  • 如果互斥层级已经有选中的节点时,得取消当前点击的节点
  • 如果互斥层级没有选中的节点时,只通知子代的第一个节点更新状态。
broadcast(event, ...args) {
    let {config, level, children} = this;
    const handlerName = `onParent${capitalize(event)}`;

    // 此处要加互斥逻辑,因为父节点的点击,理论上要全选其子代,但使用了互斥逻辑的层级不行
    // 如果是互斥节点的父节点被点击了,且是选中的操作,那么只通知第一个子代更新即可
    if (config.mutualExcluseLabel && level + 1 === config.mutualExcluseLabel && args[0]) {
      if (children[0]) {
        let child = children[0];
        child.broadcast(event, ...args);
        child[handlerName] && child[handlerName](...args);
      }
    } else {
      this.children.forEach(child => {
        if (child) {
          // bottom up
          child.broadcast(event, ...args);
          child[handlerName] && child[handlerName](...args);
        }
      });
    }
  }

异常情况: 其实原生的input组件type为checkbox时,我们这样的修改会产生很多很多的问题,列出来太麻烦,但都被我解决了,建议看官直接用文章顶部的项目地址即可。