Vue编写一个自己的树形组件

一、需求情况

有时候用Vue开发的时候,不想使用组件库,但是遇到需要目录显示的情况的时候,想要一个树形递归的组件,可以自己封装定义想要的样式效果与事件,本文讲解如何实现一个自己的树形组件。

二、开发组件

Tree.vue父组件

html 复制代码
    <!--
     * @Author: 羊驼
     * @Date: 2024-03-04 11:43:47
     * @LastEditors: 羊驼
     * @LastEditTime: 2024-03-12 17:16:58
     * @Description: 树形组件的树
    -->
    <template>
      <ul>
        <tree-node
          v-for="node in nodes"
          :key="node.id"
          :node="node"
          :select_id="select_id"
          @node-clicked="handleNodeClicked"
        ></tree-node>
      </ul>
    </template>
    <script>
    import TreeNode from "./TreeNode.vue";
    export default {
      components: {
        TreeNode,
      },
      props: {
        // 节点数据
        nodes: Array,
        // 当前选中的节点
        select_id: String | Number,
      },
      methods: {
        // 定义一个节点点击事件
        handleNodeClicked(clickedNode) {
          this.$emit("node-clicked", clickedNode);
        },
      },
    };
    </script>

    <style scoped>
    ul {
      list-style: none;
    }
    </style>

TreeNode 节点组件

html 复制代码
    <!--
     * @Author: 羊驼
     * @Date: 2024-03-04 11:42:24
     * @LastEditors: 羊驼
     * @LastEditTime: 2024-03-12 17:19:30
     * @Description: 树形节点
    -->
    <template>
      <div>
        <li
          :class="{'a-selected':select_id==node.id}"
          class="flex"
          ref="text"
          @click="childClicked(node)"
        >
          <div
            class="icon-box"
            v-if="haveChild"
          >
            <svg
              @click="setExpanded(!isExpanded)"
              t="1709605135944"
              class="icon"
              viewBox="0 0 1024 1024"
              version="1.1"
              xmlns="http://www.w3.org/2000/svg"
              p-id="2647"
              width="25"
              height="25"
              :class="{rotate:isExpanded}"
              :fill="select_id==node.id?'#ccc':'#000'"
            >
              <path
                d="M500.8 604.779L267.307 371.392l-45.227 45.27 278.741 278.613L779.307 416.66l-45.248-45.248z"
                p-id="2648"
              ></path>
            </svg>
          </div>
          <span>{{node.name}}</span>
        </li>
        <ul v-if="haveChild&&isExpanded">
          <tree-node
            v-for="child in node.children"
            :key="child.id"
            :node="child"
            :select_id="select_id"
            @node-clicked="childClicked"
          ></tree-node>
        </ul>
      </div>
    </template>

    <script>
    export default {
      // 因为是递归组件 导入的时候一定要以懒加载的形式 不然会报错的
      components: { "tree-node": () => import("./TreeNode.vue") },
      data() {
        return {
          // 展开情况
          isExpanded: false,
        };
      },
      props: {
        node: Object,
        select_id: String | Number,
      },
      computed: {
        // 判断是否可以展开
        haveChild() {
          return this.node.children && this.node.children.length;
        },
      },
      methods: {
        // 设置展开情况
        setExpanded(value) {
          this.isExpanded = value;
        },
        // 点击情况
        childClicked(clickedNode) {
          this.$emit("node-clicked", clickedNode);
        },
      },
    };
    </script>

    <style scoped>
    .icon-box {
      width: 40px !important;
      display: flex;
    }
    .icon {
      text-align: center;
      margin-right: 10px;
      transition: all 0.2s ease;
      transform: rotate(-90deg);
    }
    ul {
      list-style: none;
      padding-left: 20px !important;
    }
    .flex {
      display: flex;
      align-items: center;
      padding: 10px 20px;
      user-select: none;
    }

    .flex a {
      padding: 0 !important;
    }

    .flex:hover {
      background: #f2f9f5 !important;
      color: #008737 !important;
    }

    .flex:hover a {
      background: #f2f9f5 !important;
      color: #008737 !important;
    }

    .a-selected,
    .a-selected a {
      background: #008737 !important;
      color: #fff !important;
    }
    .rotate {
      transform: rotate(0);
    }
    </style>

三、使用组件

html 复制代码
    <!--
     * @Author: 羊驼
     * @Date: 2024-03-12 16:49:46
     * @LastEditors: 羊驼
     * @LastEditTime: 2024-03-12 17:21:17
     * @Description: 示例
    -->
    <template>
      <div>
        <tree
          :nodes="nodes"
          :select_id="select_id"
          @node-clicked="handleNodeClicked"
        >
        </tree>
      </div>
    </template>

    <script>
    import Tree from "./components/Tree.vue";
    export default {
      // 组件引入
      components: { Tree },
      data() {
        return {
          // 测试数据
          nodes: [
            {
              id: 1,
              name: "测试1",
              children: [
                {
                  id: 2,
                  name: "测试1-1",
                  children: [
                    {
                      id: 3,
                      name: "测试2-1",
                    },
                  ],
                },
                {
                  id: 4,
                  name: "测试1-2",
                },
              ],
            },
            {
              id: 6,
              name: "测试2",
            },
          ],
          select_id: "",
        };
      },
      methods: {
        // 节点选中后
        handleNodeClicked(node) {
          this.select_id = node.id;
        },
      },
    };
    </script>

    <style>
    </style>

四、效果展示

​编辑

最简版本 后续有什么需求 可以自己定义插槽和事件实现

相关推荐
斯~内克2 小时前
Electron 菜单系统深度解析:从基础到高级实践
前端·javascript·electron
数据知道2 小时前
【YAML】一文掌握 YAML 的详细用法(YAML 备忘速查)
前端·yaml
dr李四维2 小时前
vue生命周期、钩子以及跨域问题简介
前端·javascript·vue.js·websocket·跨域问题·vue生命周期·钩子函数
旭久2 小时前
react+antd中做一个外部按钮新增 表格内部本地新增一条数据并且支持编辑删除(无难度上手)
前端·javascript·react.js
windyrain2 小时前
ant design pro 模版简化工具
前端·react.js·ant design
浪遏2 小时前
我的远程实习(六) | 一个demo讲清Auth.js国外平台登录鉴权👈|nextjs
前端·面试·next.js
GISer_Jing3 小时前
React-Markdown详解
前端·react.js·前端框架
太阳花ˉ3 小时前
React(九)React Hooks
前端·react.js
拉不动的猪4 小时前
vue与react的简单问答
前端·javascript·面试