当前位置:首页 > VUE

vue实现树形菜单组件

2026-02-21 15:08:48VUE

Vue 树形菜单组件实现方法

递归组件实现基础树形结构

使用 Vue 的递归组件特性可以轻松实现树形菜单。需要定义一个能够调用自身的组件。

<template>
  <ul>
    <li v-for="item in treeData" :key="item.id">
      {{ item.label }}
      <tree-menu 
        v-if="item.children && item.children.length" 
        :treeData="item.children"
      />
    </li>
  </ul>
</template>

<script>
export default {
  name: 'TreeMenu',
  props: {
    treeData: {
      type: Array,
      required: true
    }
  }
}
</script>

添加展开/折叠功能

通过添加一个 isOpen 状态来控制子菜单的显示与隐藏。

<template>
  <ul>
    <li v-for="item in treeData" :key="item.id">
      <span @click="toggle(item)">
        {{ item.label }}
        <span v-if="item.children && item.children.length">
          {{ item.isOpen ? '[-]' : '[+]' }}
        </span>
      </span>
      <tree-menu 
        v-if="item.children && item.children.length && item.isOpen" 
        :treeData="item.children"
        @toggle="toggle"
      />
    </li>
  </ul>
</template>

<script>
export default {
  name: 'TreeMenu',
  props: {
    treeData: {
      type: Array,
      required: true
    }
  },
  methods: {
    toggle(item) {
      this.$emit('toggle', item)
    }
  }
}
</script>

添加选中状态

在树形菜单中实现单选或多选功能。

<template>
  <ul>
    <li 
      v-for="item in treeData" 
      :key="item.id"
      :class="{ 'selected': item.isSelected }"
      @click="selectItem(item)"
    >
      <span @click.stop="toggle(item)">
        {{ item.label }}
        <span v-if="item.children && item.children.length">
          {{ item.isOpen ? '[-]' : '[+]' }}
        </span>
      </span>
      <tree-menu 
        v-if="item.children && item.children.length && item.isOpen" 
        :treeData="item.children"
        @toggle="toggle"
        @select="selectItem"
      />
    </li>
  </ul>
</template>

<script>
export default {
  name: 'TreeMenu',
  props: {
    treeData: {
      type: Array,
      required: true
    }
  },
  methods: {
    toggle(item) {
      this.$emit('toggle', item)
    },
    selectItem(item) {
      this.$emit('select', item)
    }
  }
}
</script>

<style>
.selected {
  background-color: #f0f0f0;
}
</style>

动态加载数据

对于大型树形结构,可以实现按需加载子节点数据。

<template>
  <ul>
    <li v-for="item in treeData" :key="item.id">
      <span @click="toggle(item)">
        {{ item.label }}
        <span v-if="item.hasChildren && !item.children">
          [>]
        </span>
        <span v-else-if="item.children && item.children.length">
          {{ item.isOpen ? '[-]' : '[+]' }}
        </span>
      </span>
      <tree-menu 
        v-if="item.children && item.children.length && item.isOpen" 
        :treeData="item.children"
        @toggle="toggle"
        @load="loadChildren"
      />
    </li>
  </ul>
</template>

<script>
export default {
  name: 'TreeMenu',
  props: {
    treeData: {
      type: Array,
      required: true
    }
  },
  methods: {
    toggle(item) {
      if (item.hasChildren && !item.children) {
        this.$emit('load', item)
      } else {
        this.$emit('toggle', item)
      }
    }
  }
}
</script>

添加拖拽功能

实现树形菜单节点的拖拽排序功能。

<template>
  <ul>
    <li 
      v-for="item in treeData" 
      :key="item.id"
      draggable="true"
      @dragstart="dragStart(item)"
      @dragover.prevent
      @drop="drop(item)"
    >
      <span @click="toggle(item)">
        {{ item.label }}
        <span v-if="item.children && item.children.length">
          {{ item.isOpen ? '[-]' : '[+]' }}
        </span>
      </span>
      <tree-menu 
        v-if="item.children && item.children.length && item.isOpen" 
        :treeData="item.children"
        @toggle="toggle"
        @dragStart="dragStart"
        @drop="drop"
      />
    </li>
  </ul>
</template>

<script>
export default {
  name: 'TreeMenu',
  props: {
    treeData: {
      type: Array,
      required: true
    }
  },
  data() {
    return {
      draggedItem: null
    }
  },
  methods: {
    toggle(item) {
      this.$emit('toggle', item)
    },
    dragStart(item) {
      this.draggedItem = item
    },
    drop(targetItem) {
      if (this.draggedItem !== targetItem) {
        this.$emit('drop', { draggedItem: this.draggedItem, targetItem })
      }
    }
  }
}
</script>

优化树形菜单性能

对于大型树形结构,可以使用虚拟滚动来提高性能。

vue实现树形菜单组件

<template>
  <div class="tree-container">
    <div 
      v-for="item in visibleItems" 
      :key="item.id"
      :style="{ paddingLeft: `${item.level * 20}px` }"
    >
      <span @click="toggle(item)">
        {{ item.label }}
        <span v-if="item.hasChildren">
          {{ item.isOpen ? '[-]' : '[+]' }}
        </span>
      </span>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    treeData: {
      type: Array,
      required: true
    }
  },
  computed: {
    visibleItems() {
      // 实现扁平化处理,只计算当前可见的节点
      return this.flattenTree(this.treeData)
    }
  },
  methods: {
    flattenTree(nodes, level = 0, result = []) {
      nodes.forEach(node => {
        result.push({ ...node, level })
        if (node.isOpen && node.children) {
          this.flattenTree(node.children, level + 1, result)
        }
      })
      return result
    },
    toggle(item) {
      this.$emit('toggle', item)
    }
  }
}
</script>

<style>
.tree-container {
  height: 500px;
  overflow-y: auto;
}
</style>

标签: 组件菜单
分享给朋友:

相关文章

vue轮播组件实现

vue轮播组件实现

Vue 轮播组件实现方法 使用第三方库(如 Swiper) 安装 Swiper 库: npm install swiper 在 Vue 组件中引入 Swiper: <template>…

实现vue组件

实现vue组件

创建 Vue 组件 Vue 组件可以通过单文件组件(.vue 文件)或直接在 JavaScript 中定义。以下是两种常见实现方式。 单文件组件方式 单文件组件包含模板、脚本和样式三部分,适合复杂项…

uniapp组件写法

uniapp组件写法

Uniapp 组件的基本写法 Uniapp 的组件写法与 Vue 类似,支持单文件组件(.vue 文件)。一个基本的组件通常包含三个部分:<template>、<script>…

react组件如何通讯

react组件如何通讯

React 组件通讯方式 React 组件间的通讯方式主要包括以下几种方法,适用于不同场景下的数据传递和状态管理需求。 父子组件通讯(Props 传递) 父组件通过 props 向子组件传递数据或回…

vue table组件实现

vue table组件实现

Vue 表格组件实现方案 基础表格实现 使用<table>标签结合v-for指令渲染数据是最简单的实现方式: <template> <table> &…

vue 实现toast组件

vue 实现toast组件

实现 Vue Toast 组件的方法 创建基础 Toast 组件 在 Vue 项目中创建一个基础 Toast 组件,通常包含消息内容和显示控制逻辑。 <template> &…