当前位置:首页 > 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实现组件跟随

实现组件跟随的常见方法 使用CSS定位 通过CSS的position: fixed或position: absolute属性实现组件跟随。需要结合JavaScript计算目标元素的位置。 <t…

css制作菜单

css制作菜单

CSS 制作菜单的方法 使用 CSS 可以制作多种风格的菜单,包括水平菜单、垂直菜单、下拉菜单等。以下是几种常见的实现方式。 水平菜单 水平菜单通常用于导航栏,可以通过 display: inlin…

vue实现左右菜单联动实现

vue实现左右菜单联动实现

Vue 实现左右菜单联动 数据绑定与状态管理 在 Vue 中实现左右菜单联动,核心是通过共享状态或事件通信实现数据同步。可以通过 Vuex 或组件间的 props/emit 机制管理联动状态。 //…

react组件如何通讯

react组件如何通讯

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

uniapp 滑动组件

uniapp 滑动组件

uniapp 滑动组件实现方法 使用swiper组件 uniapp内置的swiper组件可以实现滑动效果,适用于轮播图或页面切换场景。 示例代码: <swiper :indicator-d…

css制作二级菜单

css制作二级菜单

CSS制作二级菜单的方法 通过纯CSS实现二级菜单主要依赖HTML结构嵌套和CSS的:hover伪类选择器。以下是具体实现方式: HTML结构 <ul class="main-menu"&g…