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>

四、效果展示

​编辑

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

相关推荐
YUJIANYUE5 分钟前
查立得万用查分安卓版(web环境+查询系统免安装单文件一键运行包)
android·前端
慧都小项34 分钟前
从 Figma 到 Qt:Qtitan 能减少哪些界面还原工作?
前端·qt·嵌入式·figma·图形界面·工业界面·qtitan
YWL37 分钟前
OpenLayers导出功能:地图截图+GeoJSON导出完整方案,从Canvas合并到PDF生成
前端·vue·openlayers
苦瓜小生1 小时前
【前端】【力扣与手撕】十天带你刷完前端算法与手撕,全是最简单好记的最优解法!day4
前端·数据结构·算法·leetcode·面试
mayaairi1 小时前
Vue2 实战进阶:表单数据收集、指令系统与自定义指令全解析
前端·javascript·vue.js
计算机魔术师1 小时前
实测GPT-6 Astra后,我发现过去十年的专业门槛正在失效
前端
虎头金猫1 小时前
Pic Smaller 本地部署:压缩图片、转换格式,再搭建自己的 Web 工具
运维·前端·开源·aigc·ai编程·ai写作
hunterandroid1 小时前
Android ANR 排查实战:从线上告警到主线程卡点定位
android·前端
图扑软件1 小时前
下篇・换墨|主题/多语言/移动端,一套组件全覆盖
前端·javascript·ui·性能优化·数据可视化
程序员老赵1 小时前
Docker 部署 TeX Live:轻松搭建 LaTeX 论文排版编译平台
前端·docker·latex