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>

四、效果展示

​编辑

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

相关推荐
JELEE.1 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl3 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫4 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友4 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理6 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻6 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js
mapbar_front7 小时前
在职场生存中如何做个不好惹的人
前端
牧杉-惊蛰7 小时前
纯flex布局来写瀑布流
前端·javascript·css
一袋米扛几楼988 小时前
【软件安全】什么是XSS(Cross-Site Scripting,跨站脚本)?
前端·安全·xss
向上的车轮8 小时前
Actix Web适合什么类型的Web应用?可以部署 Java 或 .NET 的应用程序?
java·前端·rust·.net