You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3中如何为含二级子数组的菜单树设置激活状态?

解决方案

问题根源

  1. navItems 是普通常量,非响应式数据,修改 active 后无法触发视图更新
  2. NavBarItem 传递的是点击事件对象而非目标 item,父组件无法定位到要修改的导航项
  3. 未处理嵌套子项的激活状态重置与设置逻辑

代码修改步骤

1. 更新 NavBar.vue,将导航数据改为响应式并实现激活逻辑

<template>
  <nav class="mt-2 px-2">
    <NavBarItem
      @click="setActiveLink"
      :item="item"
      v-for="(item, _index) in navItems"
      :key="item.label"
    />
  </nav>
</template>

<script setup>
import { ref } from 'vue'

// 将navItems改为响应式ref
const navItems = ref([
  {
    href: '#',
    active: false,
    label: 'VehicleBuild',
    icon: vehicleTruckCube,
    children: [
      {
        href: '/vehicle-packages',
        active: false,
        label: 'Paket',
        icon: CubeIcon,
        children: [],
      },
    ],
  }
])

// 递归重置所有导航项的active状态
const resetAllActive = (items) => {
  items.forEach(item => {
    item.active = false
    if (item.children.length) resetAllActive(item.children)
  })
}

// 设置激活项
const setActiveLink = (targetItem) => {
  // 先重置所有项的激活状态
  resetAllActive(navItems.value)
  // 设置目标项为激活
  targetItem.active = true
  
  // 可选逻辑:若需要父项随子项激活,可添加递归向上查找父项并设置active的代码
}
</script>

2. 更新 NavBarItem.vue,传递点击的目标 item 而非事件对象

<script setup>
import { RouterLink } from 'vue-router'
import { computed } from 'vue'
import { Disclosure, DisclosureButton, DisclosurePanel } from '@headlessui/vue'
import { ChevronDownIcon } from '@heroicons/vue/24/outline'
const props = defineProps({
  item: Object,
})

const hasActiveChild = computed(() => {
  function hasActiveItem(items) {
    return items.some(item => item.active || hasActiveItem(item.children))
  }
  return hasActiveItem(props.item.children)
})

const emit = defineEmits(['click'])
</script>

<template>
  <RouterLink
    v-if="!item.children.length"
    :class="[
      'group flex w-full items-center rounded-md py-2 px-3 text-sm',
      'hover:bg-gray-100',
      item.active ? 'text-gray-800 font-semibold' : 'text-gray-600 font-medium',
    ]"
    :to="item.href"
    @click="emit('click', item)" <!-- 传递目标item而非事件对象 -->
  >
    <component
      :class="[
        'w-6 h-6 shrink-0 mr-2 group-hover:text-gray-600',
        item.active ? 'text-gray-600' : 'text-gray-400',
      ]"
      :is="item.icon"
      v-if="item.icon"
    ></component>
    <span>{{ item.label }}</span>
  </RouterLink>

  <Disclosure v-else v-slot="{ open }" :default-open="hasActiveChild">
    <DisclosureButton
      :class="[
        'group flex w-full text-left items-center rounded-md py-2 px-3 text-sm',
        'hover:bg-gray-100',
        open ? 'font-semibold text-gray-800' : 'font-medium text-gray-600',
      ]"
    >
      <component
        :class="[
          'w-6 h-6 shrink-0 mr-2 group-hover:text-gray-600',
          open ? 'text-gray-600' : 'text-gray-400',
        ]"
        :is="item.icon"
        v-if="item.icon"
      ></component>
      <span class="flex-1">{{ item.label }}</span>
      <ChevronDownIcon
        :class="['w-4 h-4 shrink-0', open ? '-rotate-180 text-gray-600' : 'text-gray-400']"
      />
    </DisclosureButton>
    <DisclosurePanel class="ml-4">
      <NavBarItem
        v-for="(child, index) in item.children"
        :item="child"
        :key="index"
        @click="emit('click', $event)" <!-- 直接传递子项触发的事件 -->
      />
    </DisclosurePanel>
  </Disclosure>
</template>

额外说明

  • 若需要父项随子项激活自动激活,可在 setActiveLink 中添加递归向上查找父项并设置 active 的逻辑(需给每个 item 添加 parent 属性或通过递归遍历记录父项)
  • 若希望基于当前路由自动设置激活状态,可结合 useRoute() 钩子,在组件挂载时根据 route.path 匹配 item.href 来设置 active

内容的提问来源于stack exchange,提问作者Daniel Öhling

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 15:15:27