• eltree 从叶子节点入手解决选中问题


    0. 缘起

    第一次用el-tree,是一个很阴间的任务,不过可以抄小伙伴的,快乐抄了过来结果发现我这边出了个BUG,省市区,市级选中其下所有的区也被选中。但我一看请求返回值,它的是否选中状态是正确的。所以我需要抓抓代码BUG。

    1. 封装el-tree

    整体封装组件代码见附录

          <self-tree
            :tree-data="treeData"
            :is-check="true"
            :use-plus="true"
            :defaultProps="defaultProps"
            :is-search="false"
            :defaultCheckedKeys="defaultCheckedKeys"
            @check="handleNodeCheck"
            :show-indicator="true"
          />
    

    props传进去一些当前的value名

          defaultProps: {
            children: "list",
            label: "name",
            isLeaf: "leaf",
          },
    

    这一步非常关键,要记住el-tree判断是否选中要从叶子结点看!如果单纯以是否选中(tick为true),就会出现非叶节点选中,其下级全部选中的BUG。

    resolveData(data, newArr, level, regionStr)这个递归函数深度搜索,如果到达该分支叶节点,就进行下一分支的查找。

    data :递归数组 ---newArr: 记 id ---level:深度 --- regionStr:

        // 数据处理
        resolveData(data, newArr, level, regionStr) {
          level++;
          data.forEach((item) => {
            if (level <= 3) {
              if (level === 1) {
                regionStr = "";
                item.region = item.id + "";
              } else {
                item.region = regionStr + "," + item.id;
              }
              item.type = "region";
            }
              
    // important step
            if (item.tick && level === 3) {
              newArr.push("" + item.id);
              this.checkedIdList.push(item.region);
            }
            if (item.list) {
              this.resolveData(item.list, newArr, level, item.region);
            }
          });
        },
    

    获取生成树的数据,同时也是递归开始

        async getAreaConnectList() {
          let res = await getAreaConnectList(this.chosen);
          if (res.code === 200) {
            this.treeDataAll = res.data || [];
          } else {
            this.$message.error("获取区域关联数据失败", res.code);
          }
          let arr = [];
          let level = 0;
          let regionStr = "";
          this.resolveData(this.treeDataAll, arr, level, regionStr);
          this.defaultCheckedKeys = arr || [];
        },
    
            
    
        handleNodeCheck(val) {
          this.checkedIdList = val.checkedNodes.map((item) => {
            return item.region;
          });
        },
    

    2. 心得

    这几天搞什么级联选择器 树结构,和数组打交道很多。要注意数据格式和回显方式,想清楚怎么来 怎么发送。

    el-tree判断是否选中要从叶子结点看!如果单纯以是否选中(tick为true),就会出现非叶节点选中,其下级全部选中的BUG

    附录

    <!--基于el-tree的树形组件-->
    <template>
      <div class="self-tree">
        <el-input v-if="isSearch" ref="treeInputRef" v-model="search" size="small" placeholder="搜索" @change="inputChange"
                  clearable>
          <em slot="suffix" class="el-input__icon el-icon-search"></em>
        </el-input>
        <el-tree :class="['self-tree-common',usePlus ?'self-plus-icon-tree':'',showIndicator?'self-indicator-tree':'']"
                 ref="treeRef"
                 style="margin-top: 10px"
                 :data="treeData"
                 :props="defaultProps"
                 :filter-node-method="filterNode"
                 :show-checkbox="isCheck"
                 :node-key="nodeKey"
                 :indent="showIndicator ? 0 : 16"
                 :icon-class="usePlus ? 'el-icon-circle-plus-outline':''"
                 :default-checked-keys="defaultCheckedKeys"
                 :expand-on-click-node="expandOnClickNode"
                 :default-expand-all="defaultExpandAll"
                 :lazy="lazy"
                 :load="lazyLoad"
                 :draggable="draggable"
                 @node-click="handleNodeClick"
                 @check="handleNodeCheck"
                 @node-contextmenu="handleContextMenu"
                 @node-drag-start="handleDragStart"
                 @node-drag-enter="handleDragEnter"
                 @node-drag-leave="handleDragLeave"
                 @node-drag-over="handleDragOver"
                 @node-drag-end="handleDragEnd"
                 @node-drop="handleNodeDrop"
                 :allow-drop="allowDrop"
                 :allow-drag="allowDrag"
                 highlight-current>
          <span slot-scope="{node,data}">
            <!--添加图标-->
            <span><vab-icon :icon="['fas',data.icon]"></vab-icon>{{ node.label }} <i class="el-icon-circle-close"
                                                                                     style="margin-left: 10px"
                                                                                     @click="removeSelectNode(node,data)"
                                                                                     v-if="node.level !== unDragLevel && showNodeClose"></i></span>
          </span>
        </el-tree>
      </div>
    </template>
    
    <script>
    export default {
      name: "selfTree",
      props: {
        // 节点数据
        treeData: {
          type: Array,
          default: () => []
        },
        // 是否显示搜索框
        isSearch: {
          type: Boolean,
          default: () => true
        },
        // 默认搜索名
        defaultSearch: {
          type: String,
          default: () => "浙江省"
        },
        // 是否开启复选框
        isCheck: {
          type: Boolean,
          default: () => false
        },
        // 节点key值
        nodeKey: {
          type: String,
          default: () => "id"
        },
        // 是否在点击节点的时候展开或者收缩节点
        expandOnClickNode: {
          type: Boolean,
          default: () => true
        },
        // 默认选中的节点
        defaultCheckedKeys: {
          type: Array,
          default: () => []
        },
        // 使用+/- icon图标
        usePlus: {
          type: Boolean,
          default: () => false
        },
        // 显示指示器
        showIndicator: {
          type: Boolean,
          default: () => false
        },
        // 使用懒加载方式
        lazy: {
          type: Boolean,
          default: () => false
        },
        // 懒加载函数
        lazyLoad: {
          type: Function,
          default: () => 1
        },
        defaultProps: {
          type: Object,
          default: () => {
            return {
              children: "children",
              label: "name",
              isLeaf: "leaf"
            };
          }
        },
        // 是否允许拖拽
        draggable: {
          type: Boolean,
          default: () => false
        },
        // 不可拖拽的层级
        unDragLevel: {
          type: Number,
          default: () => 1
        },
        allowDrag: {
          type: Function,
          default: () => true
        },
        allowDrop: {
          type: Function,
          default: () => true
        },
        // 是否默认全部展开
        defaultExpandAll: {
          type: Boolean,
          default: () => false
        },
        // 是否显示节点删除按钮
        showNodeClose: {
          type: Boolean,
          default: () => false
        }
      },
      data() {
        return {
          search: ""
        };
      },
      watch: {
        search(val) {
          this.$refs?.treeRef?.filter(val);
        }
      },
      methods: {
        // 输入框值变化
        inputChange() {
          this.$emit("inputChange", this.search);
        },
        filterNode(value, data) {
          if (!value) return true;
          return data.label ? data.label.indexOf(value) !== -1 : data.name.indexOf(value) !== -1;
        },
        // 处理节点点击事件
        handleNodeClick(data, node, $el) {
          // 复选框模式下,点击事件不触发
          if (this.isCheck) return;
          this.$emit("node-click", data, node);
        },
        // 处理节点复选事件
        handleNodeCheck(data, list) {
          this.$emit("check", list);
        },
        // 处理右键点击事件
        handleContextMenu(event, data, node, $el) {
          this.$emit("node-contextmenu", {event, data, node, el: $el});
        },
        // 新增节点
        appendNode(data, key) {
          let parentNode = this.$refs.treeRef.getNode(key);
          parentNode.isLeaf = false;
          if (parentNode.expanded) {
            if (!parentNode.children) {
              parentNode.children = [];
            }
            this.$refs.treeRef.append(data, parentNode);
          } else {
            parentNode.expand();
          }
        },
        // 删除节点
        removeNode(key) {
          let node = this.$refs.treeRef.getNode(key);
          let parentNode = node.parent;
          this.$refs.treeRef.remove(node);
          parentNode.isLeaf = !(parentNode.childNodes && parentNode.childNodes.length);
          parentNode.expand();
        },
        // 删除选中节点
        removeSelectNode(node, data) {
          this.$emit("on-remove-node", node, data);
        },
        // 更新节点名称
        refreshNode(name, key) {
          let node = this.$refs.treeRef.getNode(key);
          node.data.label = name;
          node.data.name = name;
        },
        handleDragStart(node, ev) {
          this.$emit("node-drag-start", node, ev)
        },
        handleDragEnter(draggingNode, dropNode, ev) {
          this.$emit("node-drag-enter", draggingNode, dropNode, ev)
        },
        handleDragLeave(draggingNode, dropNode, ev) {
          this.$emit("node-drag-leave", draggingNode, dropNode, ev)
        },
        handleDragOver(draggingNode, dropNode, ev) {
          this.$emit("node-drag-over", draggingNode, dropNode, ev)
        },
        handleDragEnd(draggingNode, dropNode, dropType, ev) {
          this.$emit("node-drag-end", draggingNode, dropNode, dropType, ev)
        },
        handleNodeDrop(draggingNode, dropNode, dropType, ev) {
          this.$emit("node-drop", draggingNode, dropNode, dropType, ev)
        }
      },
      mounted() {
        if (this.isSearch) {
          setTimeout(() => {
            this.search = this.defaultSearch;
          }, 500);
        }
      }
    };
    </script>
    
    <style lang="scss">
    .self-indicator-tree {
      .el-tree-node {
        position: relative;
        padding-left: 16px; // 缩进量
      }
    
      .el-tree-node__children {
        padding-left: 16px; // 缩进量
      }
    
      // 竖线
      .el-tree-node::before {
        content: "";
        height: 100%;
         1px;
        position: absolute;
        left: -3px;
        top: -26px;
        border- 1px;
        border-left: 1px dashed #52627C;
      }
    
      // 当前层最后一个节点的竖线高度固定
      .el-tree-node:last-child::before {
        height: 38px; // 可以自己调节到合适数值
      }
    
      // 横线
      .el-tree-node::after {
        content: "";
         24px;
        height: 20px;
        position: absolute;
        left: -3px;
        top: 12px;
        border- 1px;
        border-top: 1px dashed #52627C;
      }
    
      // 去掉最顶层的虚线,放最下面样式才不会被上面的覆盖了
      & > .el-tree-node::after {
        border-top: none;
      }
    
      & > .el-tree-node::before {
        border-left: none;
      }
    
      // 展开关闭的icon
      .el-tree-node__expand-icon {
        font-size: 16px;
    
        &.is-leaf {
          color: transparent;
        }
      }
    }
    
    .self-plus-icon-tree {
      .el-tree-node__expand-icon.expanded {
        transform: rotateX(0deg);
      }
    
      .el-tree-node__expand-icon.expanded:before {
        content: '\e722';
      }
    }
    </style>
    
    
  • 相关阅读:
    分布式系统简介
    java.lang.Object 之 clone() 深拷贝,浅拷贝
    粉丝裂变活动bug
    遇到的bug
    移动端fixed定位在底部,出现键盘后消失
    窗口关闭,打开新页面,刷新等总结
    input 手机数字键盘
    正则重温(学习笔记)
    input的表单验证(不断更新中~~)
    css 不大常见的属性(不断更新中...)
  • 原文地址:https://www.cnblogs.com/lepanyou/p/15932087.html
Copyright © 2020-2023  润新知