2021-11-26 这把zTree让我玩明白了【树形组件】

Vue-Giant-Tree

项目中需要一个 支持移动端,支持懒加载,支持vue,支持单选多选,支持大批量数据的树插件。
最终选择了开源项目 巨树 :基于ztree封装的Vue树形组件,轻松实现海量数据的高性能渲染。
在这里插入图片描述

用起来十分简单,按照教程安装后当做一个普通组件来用就可以。
我们需要给他提供setting配置和nodes节点数据。我们这里采用懒加载的形式(初始默认加载根节点,点击哪里加载哪里)

<template>
  <div class="selectTree">
    <tree
      :setting="setting"
      :nodes="options"
      @onExpand="onExpand"
      @onClick="onClick"
      @onCheck="onCheck"
      @onCreated="onCreated"
    />
  </div>
</template>

<script>
  import tree from "vue-giant-tree";
    export default {
      name: "selectTree",
      components: {
        tree
      },
      data () {
        return {
          setting: {
            treeId: 'myTree',
            check: {
              enable: true,
              chkStyle: this.multiple ? "checkbox" : "radio",
              chkboxType: { "Y": "", "N": "" },
              radioType: 'all'
            },
            async: {
              enable: true
            },
            view: {
              showIcon: false,
              showLine: true
            }
          },
          options: []
        }
      },
      created() {
        this.getSelectList()
      }
    }
</script>

api和zTree一样,所以在使用时,直接去到了zTree的官网。

zTree

需求一: 点击输入框,跳入选择树界面,单选或多选数据后,点击确认,界面收缩,将所选数据的id和text赋给输入框。

需求二: 在树的上方放一个选中池,每次选中或取消树节点,都要和选中池联动。当然在选中池的标签叉掉时,树中的节点也跟着取消。

需求三: 数据的末级节点不需要显示下拉按钮(因为是懒加载的树,只要不请求,谁也不知道这个节点还有没有孩子,所以需要特殊处理)

需求四: 根据数据类型(单位和个人)来控制是否可以选中(也就是说,在树节点加载的时候,就要判断他是否展示单选或多选按钮)

需求五: 点击节点label可以展开节点

需求六: 展开节点请求数据时,图标加载样式(关于懒加载树添加加载图标,找一个gif图片,作为class名的背景图。然后在expand操作加载时,通过id找到相应的options加入iconSkin属性值(class名),加载完毕的回调里去掉该属性值)
在这里插入图片描述
在这里插入图片描述

问题来了

我的懒加载是这么实现的

  1. 组件初始化时先请求一次数据得到根节点,把数据中有用的name,id等属性map给绑定的树绑定的options数组。
    这里的nocheck就是控制我们的节点是否展示选择按钮的属性,在此时我们就把他配置到options中。树节点在渲染时就会根据他进行显示。
    同时,我们如果删除该节点数据中的children属性,就等于告知组件,此节点没有子节点,那么也就不会展示下拉箭头。(测了很多声明属性,还是这个好使)
  2. 每次在点击展开按钮触发执行onExpand方法,我们再次根据节点id进行请求数据,并对属性一通处理,然后根据id获取到当前节点在options的位置,将数据塞入相应的children~
        // 获取人员级联下拉菜单
        getSelectList () {
          listPersonnel('0').then(res => {
            this.options = res.data.map(item => {
              if (item.type === this.treeType) {
                delete item.children
              }
              return {
                name: item.label,
                nocheck: item.type !== this.treeType,
                // checked: this.checkedData.some(key => key.id == item.id), // 这种boolean方式赋值造成无法响应式回显
                ...item
              }
            })
          })
        },
        // 加载子节点
        mapOptions (arr, id, list) {
          for(let i in arr) {
            if (arr[i].id === id) {
              if(list.length) {
                arr[i].open = true
                const curNode = arr[i].children
                curNode.splice(0)
                curNode.push(...list)
              } else {
                arr[i].isParent = false
              }
            } else {
              this.mapOptions(arr[i].children, id, list)
            }
          }
        },
        // 懒加载
        onExpand (evt, treeId, treeNode) {
        if (treeNode.children && !treeNode.children.length){ // 防止重复请求同个节点数据
          listPersonnel(treeNode.id).then(res => {
            if (res.code === 200) {
              const arr = res.data.map(child => {
                if (child.type === this.treeType) {
                  delete child.children
                }
                return {
                  name: child.label,
                  nocheck: child.type !== this.treeType,
                  // checked: this.checkedData.some(key => key.id == child.id),
                  ...child
                }
              })
              
              this.mapOptions(this.options, treeNode.id, arr) // ========问题出在这,每次展开都会强制按初始化的options重新渲染所有节点哦,但不会他并不会更新绑定到data里的那个options数据。

              this.checkedData.forEach(item => {  // 后来加上了这段,好使!!!checkedData是选择节点的集合,可用于初始化回显选中
                this.initChecked(this.options, item.id, true)
              })
              
            }
          })
          }
        },

问题一: 我们选中的节点,在每次expand加载子节点之后就会消失???
解决: 每次展开都会强制按初始化的options重新渲染所有节点哦,但不会他并不会更新绑定到data里的那个options数据。

加入此方法(根据id找到options的数据自己更新一下):
        initChecked (options, curId, check) {
          options.forEach(item => {
            if (!this.multiple) {
              item.checked = false
            }
            if (item.id === curId) {
              item.checked = check
            } else {
              if(item.children) {
                this.initChecked(item.children, curId, check)
              }
            }
          })
        },

问题二: 点击文字展开
解决:

// 如果不是末级节点,而且当前还没有进行过请求数据(children为空),就请求数据进行懒加载
        onClick(evt, treeId, treeNode) {
          if (treeNode.children && !treeNode.children.length) {
            this.onExpand(evt, treeId, treeNode)
          }
          this.ztreeObj.expandNode (treeNode)
        },

问题三: 我们是在onCreated (ztreeObj)里获取树的实例对象,这个oncreated会在每次懒加载点开时都会重新刷新,造成获取的内容有问题,所以我们只保存最初的一次就可以了
解决:

        // 获取dom对象
        onCreated (ztreeObj) {
          if (!this.ztreeObj) {
            this.ztreeObj = ztreeObj
          }
        },

问题四: 点击节点选中后,我们可以通过 this.ztreeObj.getCheckedNodes(true) 来获取当前树节点被选中的节点对象集合,但是我发现,他每次获取的都是当前父节点分组的,而不是以全部树为基础。
解决: setting里有个属性 radioType: 'all',配置一下

          setting: {
            treeId: 'myTree',
            check: {
              enable: true,
              chkStyle: this.multiple ? "checkbox" : "radio",
              chkboxType: { "Y": "", "N": "" },
              radioType: 'all'
            },
            async: {
              enable: true
            },
            view: {
              showIcon: false,
              showLine: true
            }
          },

问题五: 现在就差通过tags标签取消选中,意味着我们需要通过id来找到树中相应的dom节点,然后对其的checked状态进行切换。切换状态需要用到this.ztreeObj.checkNode(node, false)两个参数,第一个是节点元素,第二个是想要更新到的状态。

  • 我试着通过this.ztreeObj.getNodeByTId(id)获取节点对象,但是不行,人家这个api要求的id是节点自己生成的treeId,不是咱们数据的id。
  • 然后又试了this.ztreeObj.getChangeCheckedNodes(),这个能用哎。但是当选中一个节点后,再懒加载操作,树被强制刷新后,此方法便不会找到目标节点元素。所以我们换个思路,在每次操作节点后,都将得到的改变数据存起来,放入数据池。用的时候再根据id拿?但是遍历之后通过id拿treeNode发现还是不行哎

zTreeObj.getChangeCheckedNodes
返回全部勾选状态被改变的节点集合 Array
如果需要获取每次操作后全部被改变勾选状态的节点数据,请在每次勾选操作后,遍历所有被改变勾选状态的节点数据,让其 checkedOld = checked 就可以了。

解决:

  onClose (id) {
    this.checkedData = this.checkedData.filter(item => item.id !== id) // 去掉选中池tags
    this.initChecked(this.options, id, false) // 去掉树options中的初始化checked属性,方便下次dom树刷新回显
    const node = this.oldCheck.filter(item => item.id === id) // 遍历到相应的treeNode树了还是不能实时的刷新之后叉掉的item,那么只能证明是ztreeObj的问题
    if (node.length) {
      this.ztreeObj.checkNode(node[0], false) // 更新节点元素的选中状态
    }
  }

问题六: 节点选择框样式调整一下
解决:

  ::v-deep .ztree {
    .button{
      // 多选和单选框
      &.chk{
        height: 20px;
        width: 20px;
        margin-right: 8px;
        // 多选选中
        &:after{
          height: 10px;
          width: 6px;
          top: 3px!important;
          left: 6px!important;
        }
        // 单选框圆角
        &.radio_false_full,
        &.radio_true_full,
        &.radio_true_full_focus,
        &.radio_false_full_focus{
          border-radius: 50%;
          // 单选选中
          &:after{
            height: 10px;
            width: 10px;
            top: 4px!important;
            left: 4px!important;
            border-radius: 50%;
          }
        }
      }
      &:before{
        /*display: none;*/
        /*top: 1px !important;*/
        /*border: 9px solid !important;*/
        /*border-color: transparent transparent transparent #666 !important;*/
      }
    }

    .node_name {
      font-size: 18px;
    }
  }

所以目前基本能实现取消,只要不在操作tags之前进行懒加载操作。
唯一的问题就是在刷新树之后(懒加载),无法再通过checkNode改变节点元素的选中状态。

直接按需引入element的树吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ztree-input是一个通过使用zTree插件实现的下拉菜单功能。根据引用[1]中提供的代码,ztree-input的实现方式是通过在页面上添加一个input框,并绑定一个点击事件来显示下拉菜单。当点击input框时,会触发事件函数"showTree()",该函数会将zTree状图显示在input框下方。同时,通过点击input框右侧的倒三角图标,可以收起或展开下拉菜单。在下拉菜单中选择相应的选项后,选中的值会存储在一个隐藏的input框中,供后续的处理使用。这样实现了一个具有下拉功能的ztree-input。 从引用中可以看到,实现ztree-input还需要引入ztree所需的js和css文件。 最后,引用提供了一个可用的示例来实现点击input框出现下拉菜单的效果。这个示例经过亲测可用。 总结来说,ztree-input是通过使用zTree插件和一些相关的代码实现的一个具有下拉功能的输入框。用户可以点击输入框来显示一个下拉菜单,并在菜单中选择相应的选项。选中的值会存储在隐藏的input框中,方便后续处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [ztree实现下拉功能 input](https://blog.csdn.net/weixin_42599079/article/details/88897426)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [ztree树形菜单实现点击input框下拉功能 (内附有demo)](https://blog.csdn.net/z_zhy/article/details/87938669)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [zTree 实现点击input框出现下拉菜单(亲测可用)上一版的传错了](https://download.csdn.net/download/z_zhy/10997776)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值