vue实现 tree(被迫)

1,803 阅读2分钟

原先用的 el-tree 好好的,既好看又方便,但是产品呐就是会给咱找活做,非要搞横着的。身为打工人😭为了讨饭吃只能委屈求全

效果图:

QQ20211217-141950@2x.png

大致就是手动实现一个tree 🐶

🤪🤪作为一个“卷心菜”

我能想到的只有使用递归

所以接下来xdm就会看到一堆递归来实现的tree(有啥别的思路和更好的方法请在评论区评论哦 🙏)

好啦废话不多说了,我们来分析vue实现 tree 的几个步骤

  • 未知层级,能够按照横向树形来展示出来
  • 点击节点,能够改变当前节点的勾选状态
  • 点击一个含有子节点的节点,不仅仅改变当前节点的状态,还能根据当前节点的状态改变其子孙节点的状态
  • 点击有父节点的节点,不仅仅改变当前节点的状态,还能根据当前节点的状态改变其父爷节点的状态

我们一层一层来实现

首先展示还是很简单的,我们直接用组件自调用就可实现(组件递归)

但是要⚠注意的是name记得要写上,相当于 components的的注册

WeChat07edc0adcf92b4c53dfc45e06d173e9e.png 这就展示出来了
是有点丑hhh
凑合看 image.png

第二点,也很简单,我们使用 v-model 绑定节点的勾选状态字段就好了

image.png

好了,我们要开始函数递归了,当节点状态改变时,我们要去更新他所有的子节点状态,这一看就能看懂就不多讲(这里的函数传值都是值传递即对treeList进行直接操作) image.png

最后我们要去更新父节点的状态: 首先我们需要找到当前节点的直接父节点,我们能拿到当前节点直接父节点的id, 使用递归去找直接父节点

image.png image.png

但是这里就出现问题了,你会发现一直找不到,一直找不到,当你打印 treeList 时,你会惊奇的发现,当前节点竟然是 treeList 的一个根节点,这是啥情况呢?

其实这就是组件自调用的问题,你跟节点所在的组件的treeList是上一级组件节点的children,所以导致在当前组件的 treeList 里永远找不到父节点。

那么问题来了,我们怎么找父节点呢,之前说了我们在当前组件是找不到了,那么我们去父组件呢?答案是 ofcourse

我们稍微改一下,点击当前节点时,如果当前节点有 parentId 那么咱们去触发父组件的changeParentNode 我们会发现!! 呀成功了,直接父节点的状态同步了

image.png image.png

当然呢不光是直接父节点,还有祖父节点 ... 一直到根节点,这里又是递归哦 🤪

image.png

最后贴上tree组件代码

<template>
  <div>
    <div
      v-for="item in treeList"
      :key="item.id"
      style="display: flex;"
      :class="item.floor === 1 ? 'margin' : 'margin-1'"
    >
      <div style="width: 200px">
        <span>{{ item.displayName }}</span>
        <el-checkbox v-model="item.isGrants" style="margin-left: 10px;" @change="changeGrants(item.parentId, item)" />
      </div>
      <div>
        <tree :tree-list="item.children" @changeParentNode="changeParentNode" />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Tree',
  props: {
    treeList: {
      type: Array,
      default: () => []
    }
  },
  methods: {
    changeGrants(parentId, item) {
      // 更新子节点的状态
      this.checkNode(item.children, item.isGrants)
      if (parentId) {
        // 更新父节点的状态
        this.$emit('changeParentNode', parentId)
      }
    },
    checkNode(children, checked) {
      children.forEach(item => {
        item.isGrants = checked
        if (item.children.length) {
          this.checkNode(item.children, item.isGrants)
        }
      })
    },
    changeParentNode(parentId) {
      this.findNode(parentId, this.treeList)
    },

    findNode(parentId, treeList) {
      treeList.forEach(item => {
        if (item.id === parentId) {
          if (item.children.some(item => item.isGrants)) {
            item.isGrants = true
          } else {
            item.isGrants = false
          }
          // 如果还有prentId
          if (item.parentId) {
            this.$emit('changeParentNode', item.parentId)
          }
        } else {
          if (item.children.length) {
            this.findNode(parentId, item.children)
          }
        }
      })
    }
  },
  mounted() {}
}
</script>

<style>
.margin {
  padding-top: 20px;
  padding-bottom: 20px;
  border-bottom: 1px solid #eee;
}
.margin-1 {
  padding-top: 5px;
  padding-bottom: 5px;
}
</style>