在Nuxt.js中用Pinia实现导航hover自动加类及子菜单显隐
在Nuxt.js中用Pinia实现导航悬停显示子菜单
步骤1:完善Pinia Store
更新store.ts,添加跟踪激活导航项的状态和对应的action:
import { defineStore } from "pinia"; import { acceptHMRUpdate } from "pinia"; export const peymantecStore = defineStore({ id: "peymantec-store", state: () => ({ // 存储当前激活的导航项名称,null表示无激活项 activeNav: null as string | null, activeSubmenuClass: "subMenuActive" }), actions: { // 设置激活的导航项 setActiveNav(navName: string) { this.activeNav = navName; }, // 清除激活的导航项 clearActiveNav() { this.activeNav = null; } } }); if (import.meta.hot) { import.meta.hot.accept(acceptHMRUpdate(peymantecStore, import.meta.hot)); }
步骤2:修改Header.vue组件
引入Pinia Store,绑定鼠标事件到store的action,并根据激活状态显示对应子菜单:
<template> <header class="header container-fluid mx-auto my-auto"> <!-- Extra menu links --> <nav class="extra-nav bg-blue-500 w-full h-10 flex mx-auto"></nav> <!-- navbar --> <nav class="main-nav container mx-auto my-auto"> <ul class="mx-auto my-auto headerUl flex"> <!-- logo --> <li> <NuxtLink to="/" class="mx-auto my-auto"> <img class="w-32 h-auto cursor-pointer my-auto mx-auto" src="@/img/peymantec-logo-en.jpg" alt="Peymantec Logo" /> </NuxtLink> </li> <!-- MainMenu --> <li :key="`navItem-${i}`" v-for="(n, i) in mainMenu" class="mx-auto my-auto relative" @mouseover="store.setActiveNav(n.name)" @mouseleave="store.clearActiveNav" > <NuxtLink :to="n.link" class="my-auto mx-auto w-1 py-3 headerLinks justify-between relative flex-row align-middle" > {{ n.name }} <!-- mainSvg --> <div class="inline-flex mx-1 my-auto align-middle"> <svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 30 30" > <path d="M27.963 6.75 30 8.787l-1.018 1.018-12.964 12.963L15 23.787 1.018 9.805 0 8.787 2.037 6.75l1.018 1.018L15 19.713 26.945 7.768l1.018-1.018z" ></path> </svg> </div> </NuxtLink> </li> </ul> </nav> <!-- subMenus --> <!-- EngineeringsubMenus --> <nav class="container mx-auto my-auto w-full subMenu" :class="store.activeNav === 'مهندسی' ? store.activeSubmenuClass : ''" @mouseover="store.setActiveNav('مهندسی')" @mouseleave="store.clearActiveNav" > <div class="flex flex-auto"> <!-- ServicesSub --> <ul class="servicesSub p-2"> <li class="mb-3">خدمات</li> <li :key="`navItem-${i}`" v-for="(n, i) in servicesSub" class="w-full mx-auto my-auto inline-flex px-3 py-3" > <NuxtLink :to="n.link">{{ n.name }}</NuxtLink> </li> </ul> <!-- ExpertiseSub --> <ul class="expertiseSub p-2"> <li class="mb-3">تخصص</li> <li :key="`navItem-${i}`" v-for="(n, i) in expertiseSub" class="w-full mx-auto my-auto inline-flex px-3 py-3" > <NuxtLink :to="n.link">{{ n.name }}</NuxtLink> </li> </ul> <!-- HowWeWorkSub --> <ul class="howWeWorkSub p-2"> <li class="mb-3">ما چطور کار میکنیم؟</li> <li :key="`navItem-${i}`" v-for="(n, i) in howWeWorkSub" class="w-full mx-auto my-auto inline-flex px-3 py-3" > <NuxtLink :to="n.link">{{ n.name }}</NuxtLink> </li> </ul> </div> </nav> <!-- 其他子菜单示例:比如"صنایع"的子菜单 --> <nav class="container mx-auto my-auto w-full subMenu" :class="store.activeNav === 'صنایع' ? store.activeSubmenuClass : ''" @mouseover="store.setActiveNav('صنایع')" @mouseleave="store.clearActiveNav" > <div class="p-4"> <p>صنایع子菜单内容</p> </div> </nav> </header> </template> <script setup> import { peymantecStore } from "@/store"; const store = peymantecStore(); // Main Menu const mainMenu = [ { name: "مهندسی", link: "/engineering", active: false, }, { name: "صنایع", link: "/services", active: false, }, { name: "درباره", link: "/about", active: false, }, { name: "تماس", link: "/contact", active: false, }, ]; // Engineering SubMenu const servicesSub = [ { name: "توسعه نرم افزار", link: "/development", }, { name: "پردازش ابری", link: "/cloud", }, { name: "اینترنت اشیا و هوش مصنوعی", link: "/iot", }, { name: "کسب و کار هوشمند", link: "/smart", }, ]; const expertiseSub = [ { name: "توسعه وب، موبایل و بلاک چین", link: "/services", }, { name: "توسعه واقعیت افزوده و ان اف تی", link: "/about", }, { name: "طراحی تجربه و رابط کاربری", link: "/about", }, { name: "علم داه و کلان داده", link: "/about", }, ]; const howWeWorkSub = [ { name: "کشف نیازهای پروژه", link: "/services", }, { name: "طراحی خلاقانه و توسعه هوشمند", link: "/about", }, { name: "بررسی کیفیت و عملکرد محصول", link: "/about", }, { name: "اجرا و تحویل نهایی پروژه", link: "/about", }, ]; </script> <style lang="scss" scoped> .header { position: relative; top: 0; left: 0; overflow: visible; /* 修改为visible,避免子菜单被截断 */ display: flex; flex-direction: column; } .headerLinks { &:hover { color: #b48648; fill: #b48648; } } /* 子菜单默认隐藏 */ .subMenu { display: none; position: absolute; top: 100%; left: 0; background-color: white; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } /* 激活时显示子菜单 */ .subMenuActive { display: block; } .triangelActive { border-bottom: 1rem solid #b48648 !important; } </style>
关键修改说明
Pinia状态管理:
- 添加
activeNav状态,存储当前悬停的导航项名称,用于匹配对应的子菜单 - 新增
setActiveNav和clearActiveNav两个action,分别用于设置和清除激活状态
- 添加
Header组件修改:
- 引入并初始化Pinia Store
- 给每个主导航项的
<li>绑定@mouseover和@mouseleave事件,调用store的action更新状态 - 子菜单通过
:class绑定,根据store.activeNav的值判断是否添加激活类 - 子菜单自身也绑定鼠标事件,避免鼠标移到子菜单上时触发隐藏
CSS修正:
- 修改
.header的overflow为visible,防止子菜单被截断 - 设置
.subMenu默认display: none,激活时通过.subMenuActive改为display: block - 给子菜单添加绝对定位和阴影,提升视觉效果
- 修改
扩展说明
如果需要给其他导航项(比如"صنایع")添加子菜单,只需复制现有的子菜单结构,修改:class的判断条件为对应的导航项名称即可。
内容的提问来源于stack exchange,提问作者Peyman
相关产品推荐
相关产品推荐

