偏方处理:级联下拉选想要把选中的父子结点都传给后端,并拒绝二维数组怎么办

32 阅读4分钟

众所周知,elementUI里面,级联下拉选数据只有两种形式。

只传叶子结点的值

['code1','code2']

或者,把路径上的值也传递过去

[['parent1','code1'],['parent1','code2']]

但是现在后端想要这样的扁平数据结构:

['parent1','code1','code2']

至于原因,别问,问就是,历史原因

思路

怎么实现呢?
如果是在传参的时候,把二维数组转换成一维数组,回显的时候就没办法回显,我总不能把一维数组转换成二维数组。
第二个想法是在change事件手动选中子结点,然后选中父节点。(找了个偏门,用的是没有暴露出来的doCheck方法)
这个的问题在于,选中子结点没问题,但是在选中父节点的时候,因为change里面有【选中所有子结点】的逻辑存在,导致父节点的所有子结点(即当前结点的所有兄弟结点)也都被选中了
这不是我们想看到的。
为了解决这个,我们提出一个折中的方案(不能魔改组件太多)
change事件里面通过代码手动选中子结点,在提交的时候通过path把父组件的值取出来、去重传给后端即可。

至于一点显示小问题,产品不在意,还是toB,后端只要数据,又不想刷数据(也没法刷,影响太大了),在这些压力下,一点显示小问题,无人在意。也就这么办了。

话不多说,来看看代码

html

提前说一下,下面的emitPath: false, checkStrictly: true,是必须的。

作用: 父子结点互不关联主要是回显的时候,会根据传回来的值回显样式&& 只返回当前结点的值不然就是二维数组了

<el-cascader
  ref="codeRef"
  options={this.options}
  {...{
    props: {
      value: this.codeArr,
      props: {
        multiple: true,
        value: "code",
        label: "name",
        emitPath: false,
        checkStrictly: true,
      },
    },
  }}
  clearable
  collapseTags
  filterable
  onChange={this.handleChange}
></el-cascader>

处理函数

// 当列表发生变化时,调用此方法更新选中状态并同步视图
    handleChange(newArr) {
      console.log("自己点击也会触发这个方法吗");
      // 调用辅助函数 findCurrentOption 找到新增或取消的部门信息
      const current = this.findCurrentOption(newArr, this.codeArr);
      // 更新当前部门列表
      this.codeArr = newArr;
      // 如果没有变化,直接返回
      if (!current) return;

      // 延迟执行,确保 DOM 更新完成
      this.$nextTick(() => {
        // 根据当前值找到对应的节点
        const targetNode = this.$refs.codeRef.panel.getNodeByValue(current.value);
        // 如果未找到目标节点,直接返回
        if (!targetNode) return;

        // 根据类型递归勾选或取消子节点
        if (current.type === "checked") {
          this.checkedChildren([targetNode], true); // 勾选所有下级节点
        } else if (current.type === "cancel") {
          this.checkedChildren([targetNode], false); // 取消所有下级节点
        }
        
        //   拿出来是为了统一注释。这个就是手动选中上级结点的代码了
        //   // 根据类型递归勾选或取消子节点
        //   if (current.type === "checked") {
        //     this.checkedParent(targetNode, true); // 勾选所有上级节点
        //   } else if (current.type === "cancel") {
        //     this.checkedParent(targetNode, false); // 取消所有上级节点
        //   }

        // 重新计算多选值以更新视图
        this.$refs.organCodeRef.$refs.panel.calculateMultiCheckedValue();
      });
    },

    // 辅助函数:比较新旧数组,返回新增或取消的信息
    findCurrentOption(newArr, oldArr) {
      const catchNewArr = [...newArr];
      const catchOldArr = [...oldArr];

      // 如果新数组长度大于旧数组,表示有新增部门
      if (catchNewArr.length > catchOldArr.length) {
        for (let i = 0; i < catchNewArr.length; i++) {
          const targetIndex = catchOldArr.indexOf(catchNewArr[i]);
          // 如果在旧数组中找不到当前值,表示新增
          if (targetIndex === -1) {
            return {
              value: catchNewArr[i], // 新增的值
              type: "checked", // 类型为新增
            };
          }
        }
      } else {
        // 如果新数组长度小于旧数组,表示有取消部门
        for (let i = 0; i < catchOldArr.length; i++) {
          const targetIndex = catchNewArr.indexOf(catchOldArr[i]);
          // 如果在新数组中找不到当前值,表示取消
          if (targetIndex === -1) {
            return {
              value: catchOldArr[i], // 取消的值
              type: "cancel", // 类型为取消
            };
          }
        }
      }
    },

    // 辅助函数:递归勾选或取消节点及其子节点
    checkedChildren(list = [], chekced = true) {
      if (!Array.isArray(list)) return;
      list.forEach((item) => {
        console.log("???", list, item);
        item.doCheck(chekced); // 设置当前节点的选中状态(true选中; false取消选中)
        if (item.children.length > 0) {
          // 如果存在子节点,递归调用
          this.checkedChildren(item.children, chekced);
        }
      });
    },
    // // 辅助函数:递归勾选或取消节点及其上级节点
    // checkedParent(item, chekced = true) {
    //   if (item.parent) {
    //     item.parent.doCheck(chekced); // 如果有上级结点,则设置上级节点的选中状态(true选中; false取消选中)
    //     // 如果存在上级节点,递归调用
    //     this.checkedParent(item.parent, chekced);
    //   }
    // },

提交

submit的时候,处理选中的结点,获取选中结点的path

 // 提交申请
    async submitApprove() {
      // 表单校验
      this.$refs.form.validate(async (valid) => {
        if (!valid) return;
        
        // 选中的结点
        const checkedNodes = this.$refs.organCodeRef?.getCheckedNodes() || [];
        // 获取选中结点的path(包括父级及当前结点)
        activityOrganCodes = _.uniq(_.flattenDeep(_.map(checkedNodes, "path")));
      });
},