这似乎对我有用(Nuxt 和 Bootstrap 5),想法是将下拉菜单作为通过数据切换的子组件,这仍在进行中,但似乎有效,我正在学习 Js 和 Vue去吧,所以这可能不是最好的方法。
ThePrimary.vue
<template>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<nuxt-link class="navbar-brand" to="/">Navbar</nuxt-link>
<button
@click="isNavbarCollapsed = !isNavbarCollapsed"
ref="navbar-toggler"
:aria-expanded="[!isNavbarCollapsed ? 'true' : 'false']"
:class="{ collapsed: isNavbarCollapsed}"
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarNavDropdown"
aria-controls="navbarNavDropdown"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div
:class="{ show: !isNavbarCollapsed}"
class="collapse navbar-collapse"
id="navbarNavDropdown"
>
<NavbarNav :items="loadedPrimaryMenu" />
</div>
</div>
</nav>
</template>
<script>
import NavbarNav from '@/components/Navigation/ThePrimary/NavbarNav'
export default {
name: 'TheNavigationPrimary',
data() {
return {
isNavbarCollapsed: true
}
},
computed: {
loadedPrimaryMenu() {
return this.$store.getters.loadedPrimaryMenu
}
},
components: {
NavbarNav
}
}
</script>
<style scoped lang="scss">
</style>
NavbarNav.vue
<template>
<ul class="navbar-nav">
<li
v-for="item in items"
:key="item.id"
class="nav-item"
:class="{ dropdown: hasChildren(item.children) }"
>
<NavLink
v-if="!hasChildren(item.children)"
:attributes="item"
/>
<NavbarNavDropdownMenu
v-else
:item="item"
/>
</li>
</ul>
</template>
<script>
import NavbarNavDropdownMenu from "@/components/Navigation/ThePrimary/NavbarNavDropdownMenu";
import NavLink from '@/components/Navigation/NavLink';
export default {
name: "NavbarNav",
props: {
items: {
type: Array,
required: true,
},
},
data() {
return {
};
},
methods: {
hasChildren(item) {
return item.length > 0 ? true : false;
},
},
components: {
NavbarNavDropdownMenu,
NavLink
}
};
</script>
<style scoped lang="scss">
</style>
NavbarNavDropdownMenu.vue
<template>
<span v-if="item">
<nuxt-link
to="#"
@click.prevent.native="openDropdownMenu"
v-click-outside="closeDropdownMenu"
:title="item.title"
:class="[
item.cssClasses,
{ show: isDropdownMenuVisible }
]"
:id="`navbarDropdownMenuLink-${item.id}`"
:aria-expanded="[isDropdownMenuVisible ? true : false]"
class="nav-link dropdown-toggle"
aria-current="page"
role="button"
data-toggle="dropdown"
>
{{ item.label }}
</nuxt-link>
<ul
:class="{ show: isDropdownMenuVisible }"
:aria-labelledby="`navbarDropdownMenuLink-${item.id}`"
class="dropdown-menu"
>
<li v-for="item in item.children" :key="item.id">
<NavLink
:attributes="item"
class="dropdown-item"
/>
</li>
</ul>
</span>
</template>
<script>
import NavLink from '@/components/Navigation/NavLink';
export default {
name: "DropdownMenu",
props: {
item: {
type: Object,
required: true,
},
},
data() {
return {
isDropdownMenuVisible: false,
};
},
methods: {
openDropdownMenu() {
this.isDropdownMenuVisible = !this.isDropdownMenuVisible;
},
closeDropdownMenu() {
this.isDropdownMenuVisible = false;
}
},
components: {
NavLink
}
};
</script>
<style scoped lang="scss">
</style>
NavLink.vue
<template>
<component
v-bind="linkProps(attributes.path)"
:is="attributes"
:title="attributes.title"
:class="[ attributes.cssClasses ]"
class="nav-link active"
aria-current="page"
prefetch
>
{{ attributes.label }}
</component>
</template>
<script>
export default {
name: 'NavLink',
props: {
attributes: {
type: Object,
required: true
}
},
methods: {
linkProps (path) {
if (path.match(/^(http(s)?|ftp):\/\//) || path.target === '_blank') {
return {
is: 'a',
href: path,
target: '_blank',
rel: 'noopener'
}
}
return {
is: 'nuxt-link',
to: path
}
}
}
}
</script>
<style scoped lang="scss">
</style>
点击外部时关闭下拉菜单
import Vue from 'vue'
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
el.clickOutsideEvent = function (event) {
if (!(el == event.target || el.contains(event.target))) {
vnode.context[binding.expression](event);
}
};
document.body.addEventListener('click', el.clickOutsideEvent)
},
unbind: function (el) {
document.body.removeEventListener('click', el.clickOutsideEvent)
},
});