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

如何使用Vue.js组合式API单独切换导航栏子菜单

Vue组合式API实现导航栏子菜单单独切换

你遇到的问题核心是单个布尔变量showSubmenu同时控制了所有子菜单的显示/隐藏,导致点击任意父菜单都会触发所有子菜单同步展开/收起。下面提供两种简单可行的解决方法:


方案一:给每个菜单项添加独立的展开状态属性

直接在菜单数据中为每个项添加isOpen属性,单独控制自身子菜单的状态:

修改后的脚本代码

<script setup>
import Icon from '../Shared/Icon.vue';
import { Link } from '@inertiajs/inertia-vue3';
import { ref } from 'vue';

const showDropdown = ref(false)

const menu = ref([
    {
        id: 1,
        name: 'dashboard',
        icon_name: 'dashboard',
        submenu: [],
        isOpen: false
    },
    {
        id: 2,
        name: 'residentes',
        icon_name: 'down',
        submenu: [
            { name: 'admin. residentes', icon: 'residentes' },
            { name: 'admin. familiares', icon: 'family' }
        ],
        isOpen: false
    },
    {
        id: 3,
        name: 'empleados',
        icon_name: 'down',
        submenu: [
            { name: 'admin. empleados', icon: 'empleados' },
            { name: 'sueldos', icon: 'sueldos' },
            { name: 'jornadas de Trabajo', icon: 'jornadas' },
            { name: 'Permisos', icon: 'permisos' }
        ],
        isOpen: false
    },
    {
        id: 4,
        name: 'usuarios',
        icon_name: 'down',
        submenu: [
            { name: 'admin. usuarios', icon: 'usuarios' },
            { name: 'roles', icon: 'roles' },
            { name: 'acceso', icon: 'premission' }
        ],
        isOpen: false
    },
    {
        id: 5,
        name: 'finanzas',
        icon_name: 'down',
        submenu: [
            { name: 'ingresos', icon: 'ingresos' },
            { name: 'egresos', icon: 'egresos' }
        ],
        isOpen: false
    },
])
</script>

修改后的模板代码

<nav class="" v-if="showDropdown">
    <ul class="">
        <li v-for="item in menu" :key="item.id" class="pb-2">
            <button v-if="item.submenu.length" @click="item.isOpen = !item.isOpen">
                <Icon :name="item.icon_name" class="w-5 h-5 mr-2 inline fill-slate-500" />
                <span class="capitalize"> {{ item.name }}</span>
            </button>
            <Link v-else>
                <Icon :name="item.icon_name" class="w-5 h-5 mr-2 inline fill-slate-500" />
                <span class="capitalize"> {{ item.name }}</span>
            </Link>
            <!-- 子菜单判断条件改为当前项的isOpen状态 -->
            <ul v-if="item.submenu.length && item.isOpen" class="pl-3 pt-1">
                <li v-for="subitem in item.submenu" :key="subitem.name" class="pb-1">
                    <Link class="capitalize">
                        <Icon :name="subitem.icon" class="w-5 h-5 mr-2 inline fill-slate-500" />
                        {{ subitem.name }}
                    </Link>
                </li>
            </ul>
        </li>
    </ul>
</nav>

方案二:用独立变量记录当前展开的菜单ID

如果不想修改原菜单数据结构,可以用一个变量记录当前展开的菜单ID,实现互斥展开(同一时间只展开一个子菜单):

修改后的脚本代码

<script setup>
import Icon from '../Shared/Icon.vue';
import { Link } from '@inertiajs/inertia-vue3';
import { ref } from 'vue';

const showDropdown = ref(false)
const currentOpenMenuId = ref(null) // 记录当前展开的菜单ID

const menu = ref([
    {
        id: 1,
        name: 'dashboard',
        icon_name: 'dashboard',
        submenu: []
    },
    {
        id: 2,
        name: 'residentes',
        icon_name: 'down',
        submenu: [
            { name: 'admin. residentes', icon: 'residentes' },
            { name: 'admin. familiares', icon: 'family' }
        ]
    },
    {
        id: 3,
        name: 'empleados',
        icon_name: 'down',
        submenu: [
            { name: 'admin. empleados', icon: 'empleados' },
            { name: 'sueldos', icon: 'sueldos' },
            { name: 'jornadas de Trabajo', icon: 'jornadas' },
            { name: 'Permisos', icon: 'permisos' }
        ]
    },
    {
        id: 4,
        name: 'usuarios',
        icon_name: 'down',
        submenu: [
            { name: 'admin. usuarios', icon: 'usuarios' },
            { name: 'roles', icon: 'roles' },
            { name: 'acceso', icon: 'premission' }
        ]
    },
    {
        id: 5,
        name: 'finanzas',
        icon_name: 'down',
        submenu: [
            { name: 'ingresos', icon: 'ingresos' },
            { name: 'egresos', icon: 'egresos' }
        ]
    },
])

// 切换菜单展开状态的方法
const toggleSubmenu = (menuId) => {
    currentOpenMenuId.value = currentOpenMenuId.value === menuId ? null : menuId
}
</script>

修改后的模板代码

<nav class="" v-if="showDropdown">
    <ul class="">
        <li v-for="item in menu" :key="item.id" class="pb-2">
            <button v-if="item.submenu.length" @click="toggleSubmenu(item.id)">
                <Icon :name="item.icon_name" class="w-5 h-5 mr-2 inline fill-slate-500" />
                <span class="capitalize"> {{ item.name }}</span>
            </button>
            <Link v-else>
                <Icon :name="item.icon_name" class="w-5 h-5 mr-2 inline fill-slate-500" />
                <span class="capitalize"> {{ item.name }}</span>
            </Link>
            <!-- 判断当前菜单ID是否与记录的ID一致 -->
            <ul v-if="item.submenu.length && currentOpenMenuId === item.id" class="pl-3 pt-1">
                <li v-for="subitem in item.submenu" :key="subitem.name" class="pb-1">
                    <Link class="capitalize">
                        <Icon :name="subitem.icon" class="w-5 h-5 mr-2 inline fill-slate-500" />
                        {{ subitem.name }}
                    </Link>
                </li>
            </ul>
        </li>
    </ul>
</nav>

补充说明

  • 方案一适合需要同时展开多个子菜单的场景,方案二适合同一时间只展开一个子菜单的场景,可根据需求选择。
  • 原代码中子菜单的:key="subitem"存在风险,因为subitem是对象,Vue建议用唯一值(如subitem.name)作为key,已在修改后的代码中调整。

内容的提问来源于stack exchange,提问作者Ubaldo Villaba

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:08:11