el-tree包含下级回显半选状态一些问题

上一篇文章如何在el-tree懒加载并且包含下级的情况下进行数据回显-02
说了一下在包含下级的时候,数据的回显,通过nodesMap进行赋值,这次说一下在做这个需求的过程中遇到的一些问题:

loadNode(node, resolve) {
  // 处理回显主要是通过,store里面的nodesMap,nodesMap是一个对象,里面的键是id,值是id对应的node节点信息,包括是否全选checked, indeterminate
  this.nodeStore = node.store || {};
  if (this.orgPower) {
    new Promise((_resolve) =>
      _resolve({
        data: {
          name: "总行",
          orgRefno: "01",
        },
      })
    ).then((res) => {
      this.orgPower = false;
      this.powerOrgPk = res.data.orgRefno
      // 这里主要通过nodesMap来处理没有懒加载数据的回显
      // if (
      //   this.nodesMap[res.data.orgRefno] &&
      //   node.store.nodesMap[res.data.orgRefno]
      // ) {
      //   node.store.nodesMap[res.data.orgRefno].checked =
      //     this.nodesMap[res.data.orgRefno].checked;
      //   node.store.nodesMap[res.data.orgRefno].indeterminate =
      //     this.nodesMap[res.data.orgRefno].indeterminate;
      // }
      // // 这里主要是用来处理第一次是半选,没有点击展开按钮,加载数据,回显不了的问题
      // this.containlow &&
      //   node.store.nodesMap[res.data.orgRefno] &&
      //   (this.powerOrgPkIsIndeterminate =
      //     node.store.nodesMap[res.data.orgRefno].indeterminate);
      return resolve([res.data]);
    });
  } else {
    new Promise((_resolve) =>
      _resolve(this.handleNodeData(node.data.orgRefno))
    ).then((res) => {
      return resolve(res.data);
      const { data = [] } = res;
      // 添加parentKey属性,以便于在左侧不选中数据的时候,将右侧下级的数据也取消掉
      const parentKey = [node.data && node.data.orgRefno || '']
      let parent = node.parent;
      while(parent && parent.data) {
        parentKey.push(parent.data && parent.data.orgRefno || '')
        parent = parent.parent
      }
      data.forEach(item => {
        item.parentKey = parentKey.join(',')
      })
      let superInNotSelect = false;
      // 如果上级没有进行选择(这里要改变一下treeCheckKeyList的数据)
      if(!node.checked && !node.indeterminate && this.containlow) {
        superInNotSelect = true;
        data.forEach(item => {
          // 这里处理一下 treeCheckKeyList, 因为这里有一个bug:在手动触发 checked事件的时候,nodesMap中的checked标识会根据是否选中进行改变
          /**
           * bug复现
           *  步骤一:第一级不选,第二级不选,第三级有一个是全选
           *  步骤二:第一级全选,第二级全不选(checked事件),会展开之前选中的第三级,这时候会发现,第三级会被莫名奇妙的选上(treeCheckKeyList包含第三级),第二级成半选状态
           */
          // 注意这里处理treeCheckKeyList要在resolve方法之前
          const findIndex = this.treeCheckKeyList.findIndex(key => key === item.orgRefno)
          if(findIndex > -1) {
            this.treeCheckKeyList.splice(findIndex, 1)
          }
        })
        this.cacheSearchTreeData = this.leftSelectTreeData;
      }
      resolve(res.data);
      let num = 0;
      let sonHasIndeteminate = false;
      data.forEach((item) => {
        // 这里主要是通过 nodesMap来处理没有懒加载数据的回显
        if (
          this.nodesMap[item.orgRefno] &&
          node.store.nodesMap[item.orgRefno]
        ) {
          // 回显全选
          node.store.nodesMap[item.orgRefno].checked =
            this.nodesMap[item.orgRefno].checked;
          // 回显半选
          this.containlow &&
            (node.store.nodesMap[item.orgRefno].indeterminate =
              this.nodesMap[item.orgRefno].indeterminate);
          if(this.containlow && node.store.nodesMap[item.orgRefno].indeterminate) {
             sonHasIndeteminate = true;
          }
          if (
            (this.containlow &&
              node.store.nodesMap[item.orgRefno].checked) ||
            node.store.nodesMap[item.orgRefno].indeterminate
          ) {
            num++;
          }
        }
        // 这里主要用来处理右侧回显的问题,之前是通过点击复选框,进行右侧回显的,现在要做成点击复选框,子节点也跟着选中,所以要把子节点也要弄到已选列表里面
        if(this.containlow) {
          if(node.checked) {
            const index = this.leftSelectTreeData.findIndex(_item => _item.orgRefno === item.orgRefno)
            // 如果没有存在右侧,则将子节点添加进去
            if(index === -1) {
              this.leftSelectTreeData.push(item)
            }
            if(node.store.nodesMap[item.orgRefno]) {
              node.store.nodesMap[item.orgRefno].checked = true
              node.store.nodesMap[item.orgRefno].indeterminate = true
            }
          } else {
            if(node.store.nodesMap[item.orgRefno] && !node.indeterminate) {
              node.store.nodesMap[item.orgRefno].checked = false
            }
          }
        }
      })
      // 这里主要用来处理是否半选(在没有懒加载的时候)
      // 这里这样处理主要是因为,有一个这样的bug,如果第一级是半选的状态,第二级只有一个半选的状态,那么第一级在点击的时候,半选状态在赋值的时候消失
      // 把这里注释掉,把 nodesMap改成 可以复现这个bug
      // nodesMap: {
      //   '01': { 'checked': false, indeterminate: true, name: '总行'},
      //   '0101': { 'checked': false, indeterminate: true, name: '测试0101'},
      //   '010101': { 'checked': true, indeterminate: true, name: '测试010101'}
      // }
      // 这里在这样处理的时候也会有一个问题
      // 这里需要增加一个变量来处理三层联动问题,如果第一级是半选,第二级也是半选,第三级也是半选,逐个向下点击,如果下级有一个是全选的,并且只有这一个,就会初选上层半选全会变成不选
      // this.containlow &&
      //   (node.indeterminate = num > 0 && num !== data.length);
      this.containlow &&
        (node.indeterminate = num > 0 && num !== data.length && !node.checked || sonHasIndeteminate);
      if (this.containlow && node.indeterminate) {
        // 这里处理的问题和上面的问题类似:如果是第一层是半选状态(没有全选状态),第二层级也是只有半选状态没有全选状态,第三层级也是这样
        // 展开第一层级没有问题,但是展开第二层级的时候,就会有第一层级的半选状态消失的问题
        let parent =
          node.store.nodesMap[node.data.orgRefno] &&
          node.store.nodesMap[node.data.orgRefno].parent;
        while (parent) {
          parent.indeterminate = true;
          parent = parent.parent;
        }
      }
      return;
    });
  }
},
  1. 为什么在展开下级的时候,上级勾选状态会受影响?
    因为下级数据勾选回显都是在resolve方法之后的,在执行resolve方法的时候,源码中有这样一个方法
this.loadData((data) => {
  if (data instanceof Array) {
    if (this.checked) {
      this.setChecked(true, true);
    } else if (!this.store.checkStrictly) {
      // 包含下级,会走这个方法
      reInitChecked(this);
    }
    done();
  }
});

const reInitChecked = function(node) {
  if (node.childNodes.length === 0 || node.loading) return;

  const {all, none, half} = getChildState(node.childNodes);
  if (all) {
    node.checked = true;
    node.indeterminate = false;
  } else if (half) {
    node.checked = false;
    node.indeterminate = true;
  } else if (none) {
    // 这里会影响本级节点
    node.checked = false;
    node.indeterminate = false;
  }

  const parent = node.parent;
  if (!parent || parent.level === 0) return;

  if (!node.store.checkStrictly) {
    // 主要这里会影响父级
    reInitChecked(parent);
  }
};

// 这个方法主要用来判断状态。
export const getChildState = node => {
  let all = true;
  let none = true;
  let allWithoutDisable = true;
  for (let i = 0, j = node.length; i < j; i++) {
    const n = node[i];
    if (n.checked !== true || n.indeterminate) {
      all = false;
      if (!n.disabled) {
        allWithoutDisable = false;
      }
    }
    if (n.checked !== false || n.indeterminate) {
      none = false;
    }
  }

  return { all, none, allWithoutDisable, half: !all && !none };
};

你可能感兴趣的:(vue.js,elementui,javascript)