封装修改完成header头部导航栏组件为动态tree渲染

This commit is contained in:
hejin 2025-07-24 00:18:40 +08:00
parent 3a561a504e
commit e2e5e73d04
7 changed files with 182 additions and 17398 deletions

97
components/header-nav.vue Normal file
View File

@ -0,0 +1,97 @@
<template>
<div
class="h-100"
:class="level === 0 ? 'd-flex align-items-center' : ''"
>
<div
v-for="(item,index) in nodes"
:key="index"
class="nav-link-item d-flex align-items-center justify-content-center"
>
<nuxt-link
:to="item.to"
v-if="item.children.length === 0"
>
{{ item.label }}
</nuxt-link>
<b-nav-item-dropdown
v-else
:id="item.id"
:text="item.label"
right
@show="isDropdown=true"
v-model="isDropdown"
@hide="dropdownHide($event, item)"
>
<header-nav
:nodes="item.children"
:level="level + 1"
/>
</b-nav-item-dropdown>
</div>
</div>
</template>
<script>
export default {
name: 'HeaderNav',
props: {
level: {
type: Number,
default: 0
},
nodes: {
type: Array,
default: () => []
},
},
data() {
return {
isDropdown: true
}
},
methods: {
dropdownHide(e, item) {
if (this.isDropdown) {
e.preventDefault()
this.isDropdown = false
}
}
}
}
</script>
<style lang="scss" scoped>
@mixin activeNav {
&:hover {
position: relative;
color: #015fe8;
background: linear-gradient( 180deg, rgba(1,95,232,0) 0%, rgba(1,95,232,0.08) 100%);
&::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: .125rem;
background: #015fe8;
}
>a {
color: #015fe8;
}
}
}
.nav-link-item {
height: 100%;
padding: 0 1.4375rem;
>a {
color: #151516;
}
@include activeNav;
}
.active-item {
@include activeNav;
}
</style>

View File

@ -4,14 +4,7 @@
<img src="~assets/image/logo.png" width="265px" :alt="$t('index.corporateName')">
<nav class="d-md-flex align-items-center">
<nuxt-link
class="nav-link-item d-flex align-items-center"
:to="item.href"
v-for="(item,index) in menuList"
:key="index"
>
{{ item.name }}
</nuxt-link>
<HeaderNav :nodes="menuTree" />
</nav>
<div class="search-box">
@ -48,11 +41,11 @@
<nav class="mob-nav hide_scroll_bar d-flex align-items-center">
<nuxt-link
class="nav-link-item d-flex align-items-center"
:to="item.href"
v-for="(item,index) in menuList"
:to="item.to"
v-for="(item,index) in menuTree"
:key="index"
>
{{ item.name }}
{{ item.label }}
</nuxt-link>
</nav>
</header>
@ -62,23 +55,32 @@
</template>
<script>
import { mapState } from 'vuex';
import MenuDrawer from './menu-drawer.vue';
import HeaderNav from './header-nav.vue';
export default {
components: { MenuDrawer },
components: {
MenuDrawer,
HeaderNav
},
data() {
return {
searchVal: '',
menuList: [
{ name: this.$t('menu.Home'), href: '/' },
{ name: this.$t('menu.AboutUs'), href: '/abou-us' },
{ name: this.$t('menu.Products'), href: '/products' },
{ name: this.$t('menu.News'), href: '/news' },
{ name: this.$t('menu.Exhibition'), href: '/exhibition' },
{ name: this.$t('menu.ContactUs'), href: '/contact-us' },
{ name: this.$t('menu.Feedback'), href: '/feedback' },
]
// menuList: [
// { name: this.$t('menu.Home'), href: '/' },
// { name: this.$t('menu.AboutUs'), href: '/abou-us' },
// { name: this.$t('menu.Products'), href: '/products' },
// { name: this.$t('menu.News'), href: '/news' },
// { name: this.$t('menu.Exhibition'), href: '/exhibition' },
// { name: this.$t('menu.ContactUs'), href: '/contact-us' },
// { name: this.$t('menu.Feedback'), href: '/feedback' },
// ]
}
},
computed: {
...mapState(['menuTree'])
}
}
</script>
@ -110,38 +112,8 @@ export default {
}
}
}
@mixin activeNav {
&:hover {
position: relative;
color: #015fe8;
background: linear-gradient( 180deg, rgba(1,95,232,0) 0%, rgba(1,95,232,0.08) 100%);
&::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: .125rem;
background: #015fe8;
}
>a {
color: #015fe8;
}
}
}
nav {
height: 100%;
.nav-link-item {
height: 100%;
padding: 0 1.4375rem;
>a {
color: #151516;
}
@include activeNav;
}
.active-item {
@include activeNav;
}
}
.header-box {
width: 100%;

4
env.js
View File

@ -8,8 +8,8 @@ module.exports = {
// 开发环境 接口请求地址 (http)或(https)://www.a.com(换成你的域名)/api
dev: {
MODE: 'dev',
// VUE_APP_API_URL: 'http://122.51.230.86:8099/',
VUE_APP_API_URL: 'http://192.168.1.4:8099/',
VUE_APP_API_URL: 'http://122.51.230.86:8099/',
// VUE_APP_API_URL: 'http://192.168.1.4:8099/',
VUE_APP_WEBSOCKET: 'ws://localhost:8099/ws/asset/'
},
// 生产环境 接口请求地址 (http)或(https)://www.a.com(换成你的域名)/api 非独立部署默认为空

7
middleware/header.js Normal file
View File

@ -0,0 +1,7 @@
export default async function ({
$axios,
store
}) {
const menuTree = await $axios.$get('/web/category')
store.commit('SET_MENU_TREE', menuTree)
}

View File

@ -78,7 +78,7 @@ export default {
icons: false
},
router: {
middleware: ['i18n', 'footer']
middleware: ['i18n', 'footer', 'header']
},
/*
** Build configuration

17369
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,14 @@
const routeMap = {
'dym': 'separate', // 单页面
'xp': 'inquiry', // 询盘栏目
'wz': 'article', // 文章栏目
'cp': 'product' // 产品栏目
}
export const state = () => ({
locales: ['en', 'zh'],
locale: 'en',
menuTree: [],
footerInfo: {}
})
@ -12,6 +20,25 @@ export const mutations = {
},
SET_FOOTER_INFO (state, info) {
state.footerInfo = info
},
SET_MENU_TREE (state, tree) {
state.menuTree = [
{label:'Home', to: '/', children: []}
]
const fn = (list) => {
list.forEach(item => {
if (item.catgLevel===1) {
item.to = `/${routeMap[item.catgType]}?catgId=${item.id}`
} else {
item.to = `/${routeMap[item.catgType]}/${item.id}?catgId=${item.maxParentId}`
}
if (item.children.length) {
fn(item.children)
}
})
}
fn(tree)
state.menuTree = [...state.menuTree, ...tree]
}
}