导航菜单 (NavMenu)
# 导航菜单 (NavMenu)
Element-UI 的导航菜单组件用于为网站提供导航功能的菜单。它支持水平和垂直两种模式,并提供丰富的配置选项以满足不同的导航需求。
提示
导航菜单(NavMenu)组件官方文档:https://element.eleme.cn/#/zh-CN/component/menu (opens new window)
导航菜单结构
<el-menu>
:定义导航菜单的容器。<el-menu-item>
:定义导航菜单的项,每一项都有一个唯一的index
属性,用于标识和导航。<el-submenu>
:定义导航菜单的子菜单,index
属性用于标识子菜单,slot="title"
定义子菜单的标题内容。
# 1. 基本用法
基本语法:在 Vue 组件中使用 <el-menu>
标签创建一个导航菜单。通过子组件 <el-menu-item>
和 <el-submenu>
定义菜单项和子菜单。
导航栏开启路由模式
<el-menu>
的 router
属性可以用来开启路由模式,启用 Vue Router 模式后,菜单项(<el-menu-item>
)的 index
属性可以直接指定路径实现导航功能,而 <el-submenu>
的 index
仅用于标识子菜单,不会触发路由跳转。
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="vertical"
:router="true"
>
<el-menu-item index="/processing-center">处理中心</el-menu-item>
<el-submenu index="workspace">
<template slot="title">我的工作台</template>
<el-menu-item index="/workspace/option1">选项1</el-menu-item>
<el-menu-item index="/workspace/option2">选项2</el-menu-item>
<el-menu-item index="/workspace/option3">选项3</el-menu-item>
<el-menu-item index="/workspace/option4">选项4</el-menu-item>
</el-submenu>
<el-menu-item index="/message-center">消息中心</el-menu-item>
<el-menu-item index="/order-management">订单管理</el-menu-item>
</el-menu>
</template>
<script>
export default {
data() {
return {
activeIndex: '/processing-center'
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
default-active
:设置默认激活的菜单项,通过index
进行标识。mode
:设置导航菜单的模式,可选值为horizontal
(水平)和vertical
(垂直)。router
:启用 Vue Router 模式,将index
作为路径进行导航跳转。<template slot="title">
:<el-submenu>
提供的插槽,用于定义子菜单的标题内容。
# Menu 属性
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
mode | 模式 | string | horizontal(水平) / vertical(垂直) | vertical |
collapse | 是否水平折叠收起菜单(仅 vertical(垂直) 模式可用) | boolean | — | false |
background-color | 菜单的背景色(仅支持 hex 格式) | string | — | #ffffff |
text-color | 菜单的文字颜色(仅支持 hex 格式) | string | — | #303133 |
active-text-color | 当前激活菜单的文字颜色(仅支持 hex 格式) | string | — | #409EFF |
default-active | 当前激活菜单的 index | string | — | — |
default-openeds | 当前打开的 sub-menu 的 index 数组 | array | — | — |
unique-opened | 是否只保持一个子菜单的展开 | boolean | — | false |
menu-trigger | 子菜单打开的触发方式(仅 horizontal(水平) 模式可用) | string | hover(悬停) / click(单击) | hover |
router | 是否使用 vue-router 模式 | boolean | — | false |
collapse-transition | 是否开启折叠动画 | boolean | — | true |
# Menu 方法
方法名称 | 说明 | 参数 |
---|---|---|
open | 展开指定的 sub-menu | index: 需要打开的 sub-menu 的 index |
close | 收起指定的 sub-menu | index: 需要收起的 sub-menu 的 index |
# Menu 事件
事件名称 | 说明 | 回调参数 |
---|---|---|
select | 菜单激活回调 | index: 选中菜单项的 index, indexPath: 选中菜单项的 index path |
open | sub-menu 展开回调 | index: 打开的 sub-menu 的 index, indexPath: 打开的 sub-menu 的 index path |
close | sub-menu 收起回调 | index: 收起的 sub-menu 的 index, indexPath: 收起的 sub-menu 的 index path |
# SubMenu(子菜单) 属性
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
index | 唯一标志 | string/null | — | null |
popper-class | 弹出菜单的自定义类名 | string | — | — |
show-timeout | 展开 sub-menu 的延时 | number | — | 300 |
hide-timeout | 收起 sub-menu 的延时 | number | — | 300 |
disabled | 是否禁用 | boolean | — | false |
popper-append-to-body | 是否将弹出菜单插入至 body 元素 | boolean | — | 一级子菜单:true / 非一级子菜单:false |
# Menu-Item(菜单项) 属性
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
index | 唯一标志 | string | — | — |
route | Vue Router 路径对象 | object | — | — |
disabled | 是否禁用 | boolean | — | false |
# Menu-Group 属性
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
title | 分组标题 | string | — | — |
# 2. 常见导航菜单示例
# select 事件
当用户点击菜单项时,select
事件会被触发。这是菜单中最常用的事件,通常用于处理菜单项的激活状态或执行与菜单项相关的逻辑操作。
回调参数:
index
: 被选中菜单项的索引(index
)。这是一个字符串,表示当前被选中的菜单项的路径或标识符。indexPath
: 被选中菜单项的完整路径(index path
),这是一个数组,包含从根菜单到被选中菜单项的所有索引。
使用场景:
- 在导航栏中使用该事件来更新当前的激活状态,确保用户的操作得到正确反馈。
- 例如,当用户点击侧边栏的某一菜单项时,可以通过
select
事件获取到该菜单项的index
,然后将其存储到 Vuex 中或进行其他处理,以保持应用程序的导航状态同步。
<template>
<el-menu :default-active="$store.state.activeIndex" @select="handleSelect">
<el-menu-item index="/home">Home</el-menu-item>
<el-menu-item index="/about">About</el-menu-item>
<el-menu-item index="/contact">Contact</el-menu-item>
</el-menu>
</template>
<script>
export default {
methods: {
handleSelect(index, indexPath) {
console.log('选中菜单项的 index:', index);
console.log('选中菜单项的 index path:', indexPath);
this.$store.commit('EditActiveIndex', index); // 更新 Vuex 中的激活状态
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# open 事件
当一个 sub-menu
(子菜单)被展开时,open
事件会被触发。这个事件可以用于记录或管理哪些子菜单处于展开状态。
回调参数:
index
: 被打开的子菜单的索引(index
)。这是一个字符串,标识当前被展开的子菜单。indexPath
: 被打开子菜单的完整路径(index path
),这是一个数组,包含从根菜单到被展开子菜单的所有索引。
使用场景:
- 例如,当用户展开某个子菜单时,可以通过
open
事件记录当前展开的子菜单,以便在用户重新进入页面时恢复之前的展开状态。
<template>
<el-menu @open="handleOpen" @close="handleClose">
<el-submenu index="1">
<template slot="title">Menu 1</template>
<el-menu-item index="1-1">Option 1</el-menu-item>
<el-menu-item index="1-2">Option 2</el-menu-item>
</el-submenu>
<el-submenu index="2">
<template slot="title">Menu 2</template>
<el-menu-item index="2-1">Option 1</el-menu-item>
<el-menu-item index="2-2">Option 2</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
export default {
methods: {
handleOpen(index, indexPath) {
console.log('展开的子菜单 index:', index);
console.log('展开的子菜单 index path:', indexPath);
// 可以在此记录展开的菜单,用于恢复状态
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# close 事件
当一个 sub-menu
(子菜单)被收起时,close
事件会被触发。这个事件可以用于记录或管理哪些子菜单处于收起状态。
回调参数:
index
: 被收起的子菜单的索引(index
)。这是一个字符串,标识当前被收起的子菜单。indexPath
: 被收起子菜单的完整路径(index path
),这是一个数组,包含从根菜单到被收起子菜单的所有索引。
使用场景:
- 例如,当用户收起某个子菜单时,可以通过
close
事件更新子菜单的状态,记录哪些子菜单已被收起,从而在下一次用户访问时提供一致的界面体验。
<template>
<el-menu @open="handleOpen" @close="handleClose">
<el-submenu index="1">
<template slot="title">Menu 1</template>
<el-menu-item index="1-1">Option 1</el-menu-item>
<el-menu-item index="1-2">Option 2</el-menu-item>
</el-submenu>
<el-submenu index="2">
<template slot="title">Menu 2</template>
<el-menu-item index="2-1">Option 1</el-menu-item>
<el-menu-item index="2-2">Option 2</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
export default {
methods: {
handleClose(index, indexPath) {
console.log('收起的子菜单 index:', index);
console.log('收起的子菜单 index path:', indexPath);
// 可以在此记录收起的菜单,用于恢复状态
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 水平导航菜单
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
:router="true"
>
<el-menu-item index="/processing-center">处理中心</el-menu-item>
<el-submenu index="2">
<template slot="title">我的工作台</template>
<el-menu-item index="/workspace/option1">选项1</el-menu-item>
<el-menu-item index="/workspace/option2">选项2</el-menu-item>
<el-menu-item index="/workspace/option3">选项3</el-menu-item>
<el-menu-item index="/workspace/option4">选项4</el-menu-item>
</el-submenu>
<el-menu-item index="/message-center">消息中心</el-menu-item>
<el-menu-item index="/order-management">订单管理</el-menu-item>
</el-menu>
</template>
<script>
export default {
data() {
return {
activeIndex: '/processing-center'
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- 基础用法:展示水平导航菜单,通过
default-active
设置默认激活的菜单项,并使用router
属性启用 Vue Router 模式。 - 路由配置:在 Vue Router 配置中定义相应的路径,例如:
const routes = [
{ path: '/processing-center', component: ProcessingCenter },
{ path: '/workspace/option1', component: Option1 },
{ path: '/workspace/option2', component: Option2 },
{ path: '/workspace/option3', component: Option3 },
{ path: '/workspace/option4', component: Option4 },
{ path: '/message-center', component: MessageCenter },
{ path: '/order-management', component: OrderManagement }
];
2
3
4
5
6
7
8
9
# 垂直导航菜单
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
:router="true"
>
<el-menu-item index="/processing-center">处理中心</el-menu-item>
<el-submenu index="2">
<template slot="title">我的工作台</template>
<el-menu-item index
="/workspace/option1">选项1</el-menu-item>
<el-menu-item index="/workspace/option2">选项2</el-menu-item>
<el-menu-item index="/workspace/option3">选项3</el-menu-item>
<el-menu-item index="/workspace/option4">选项4</el-menu-item>
</el-submenu>
<el-menu-item index="/message-center">消息中心</el-menu-item>
<el-menu-item index="/order-management">订单管理</el-menu-item>
</el-menu>
</template>
<script>
export default {
data() {
return {
activeIndex: '/processing-center'
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
- 垂直导航菜单:设置
mode
为vertical
,展示垂直导航菜单。 - 路由配置:同上例,在 Vue Router 配置中定义相应的路径。
# 带图标导航菜单
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
:router="true"
>
<el-menu-item index="/processing-center">
<i class="el-icon-menu"></i>
<span slot="title">处理中心</span>
</el-menu-item>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-setting"></i>
<span>我的工作台</span>
</template>
<el-menu-item index="/workspace/option1">选项1</el-menu-item>
<el-menu-item index="/workspace/option2">选项2</el-menu-item>
<el-menu-item index="/workspace/option3">选项3</el-menu-item>
<el-menu-item index="/workspace/option4">选项4</el-menu-item>
</el-submenu>
<el-menu-item index="/message-center">
<i class="el-icon-message"></i>
<span slot="title">消息中心</span>
</el-menu-item>
<el-menu-item index="/order-management">
<i class="el-icon-tickets"></i>
<span slot="title">订单管理</span>
</el-menu-item>
</el-menu>
</template>
<script>
export default {
data() {
return {
activeIndex: '/processing-center'
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
- 带图标导航菜单:通过
<i>
标签添加图标,为菜单项和子菜单设置图标。 - 路由配置:同上例,在 Vue Router 配置中定义相应的路径。
# 可折叠导航菜单
通过 Element-UI 实现。用户可以点击按钮在菜单的展开和收起状态之间切换。
<template>
<div>
<!-- 可折叠控制按钮 -->
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
<el-radio-button :label="false">展开</el-radio-button>
<el-radio-button :label="true">收起</el-radio-button>
</el-radio-group>
<!-- 菜单 -->
<el-menu
default-active="1-4-1"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
:collapse="isCollapse">
<!-- 子菜单1 -->
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span slot="title">导航一</span>
</template>
<el-menu-item-group>
<span slot="title">分组一</span>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<span slot="title">选项4</span>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<!-- 菜单项 -->
<el-menu-item index="2">
<i class="el-icon-menu"></i>
<span slot="title">导航二</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
</el-menu>
</div>
</template>
<script>
export default {
data() {
return {
isCollapse: true // 控制菜单折叠状态
};
},
methods: {
handleOpen(key, keyPath) {
console.log(key, keyPath); // 展开菜单时触发的事件
},
handleClose(key, keyPath) {
console.log(key, keyPath); // 收起菜单时触发的事件
}
}
}
</script>
<style>
/* 菜单的样式 */
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 200px;
min-height: 400px;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
- 可折叠导航菜单:通过
collapse
属性(是否水平折叠收起菜单)和按钮点击事件,实现菜单的折叠和展开。 - 路由配置:同上例,在 Vue Router 配置中定义相应的路径。
# 带分组导航菜单
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
:router="true"
>
<el-menu-item index="/processing-center">处理中心</el-menu-item>
<el-submenu index="2">
<template slot="title">我的工作台</template>
<el-menu-item index="/workspace/option1">选项1</el-menu-item>
<el-menu-item index="/workspace/option2">选项2</el-menu-item>
<el-menu-item index="/workspace/option3">选项3</el-menu-item>
<el-menu-item index="/workspace/option4">选项4</el-menu-item>
</el-submenu>
<el-menu-item-group title="分组一">
<el-menu-item index="/group1/option5">选项5</el-menu-item>
<el-menu-item index="/group1/option6">选项6</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组二">
<el-menu-item index="/group2/option7">选项7</el-menu-item>
<el-menu-item index="/group2/option8">选项8</el-menu-item>
</el-menu-item-group>
</el-menu>
</template>
<script>
export default {
data() {
return {
activeIndex: '/processing-center'
};
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
- 带分组导航菜单:通过
<el-menu-item-group>
标签为菜单项进行分组。 - 路由配置:在 Vue Router 配置中定义相应的路径,例如:
const routes = [
{ path: '/processing-center', component: ProcessingCenter },
{ path: '/workspace/option1', component: Option1 },
{ path: '/workspace/option2', component: Option2 },
{ path: '/workspace/option3', component: Option3 },
{ path: '/workspace/option4', component: Option4 },
{ path: '/message-center', component: MessageCenter },
{ path: '/order-management', component: OrderManagement },
{ path: '/group1/option5', component: Option5 },
{ path: '/group1/option6', component: Option6 },
{ path: '/group2/option7', component: Option7 },
{ path: '/group2/option8', component: Option8 }
];
2
3
4
5
6
7
8
9
10
11
12
13
# 3. 菜单点击跳转功能实现
# 声明式导航
在导航菜单中,通过设置 index
属性来实现路由跳转,这种方式称为声明式导航。上面示例中所有导航菜单项均采用声明式导航,这种方式利用Element-UI框架提供的组件中自带的router属性(简单)。
# 编程式导航
除了声明式导航,还可以使用 Vue Router 提供的编程式导航。通过在方法中调用 this.$router.push
或 this.$router.replace
方法实现路由跳转。
编程式导航的方法有几个注意点:
home 页重定向问题:
- 当点击首页栏时,地址栏显示的是
/
,希望显示home
组件,所以需要在路由配置中使用redirect
实现重定向。
- 当点击首页栏时,地址栏显示的是
点击相同路由时会报错:
- 当点击相同路由时,控制台会报错,但不影响功能。可以通过在跳转方法中添加逻辑判断解决该问题。
jumpTo(item) {
// 防止点击相同路由时报错
if (this.$route.path !== item.path && !(this.$route.path === "/home" && item.path === "/")) {
this.$router.push(item.path);
}
}
2
3
4
5
6
- 这个jumpto方法就解决了点击相同路由时会爆红的问题,其中的逻辑主要有如下两条:
- 如果要跳转的路由和当前路由一致则不跳转
- 如果要跳转的路由为
/
,而当前的路由为/home
则不跳转(也就是下面要说的路由重定向的情况)
- 需要在路由配置中使用
redirect
实现重定向,解决首页栏的重定向问题。
# 路由配置
import Vue from 'vue';
import Router from 'vue-router';
import Home from '@/components/Home.vue';
import About from '@/components/About.vue';
import Contact from '@/components/Contact.vue';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
redirect: '/home' // 重定向根路径到 /home
},
{
path: '/home',
component: Home
},
{
path: '/about',
component: About
},
{
path: '/contact',
component: Contact
}
]
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- 路由重定向:在路由配置中使用
redirect
属性,将根路径/
重定向到/home
。 - 路由配置:定义
home
、about
、contact
等路径对应的组件。
通过以上方法,可以解决点击相同路由时报错和路由重定向的问题,确保导航菜单在使用 Vue Router 时的正常工作。
总结
- 灵活的属性配置:通过
mode
、collapse
、background-color
等属性可以定制导航菜单的行为和样式。 - 丰富的事件处理:支持
select
、open
、close
等事件,允许开发者在导航菜单操作时进行自定义处理。 - 多样的菜单样式:支持水平、垂直、带图标、带分组等多种导航菜单样式,满足不同的导航需求。
- 声明式与编程式导航:Element-UI 导航菜单支持声明式和编程式导航,灵活适应不同场景的需求。
# 4. 动态路由导航菜单实现
通过动态路由菜单,可以根据菜单数据生成导航栏,并支持有子菜单和无子菜单的导航项。
# 菜单数据
首先,我们需要定义菜单数据。菜单数据包含路由路径(path
)、唯一标识(name
)、菜单名称(label
)、图标(icon
)和 组件所在路径(url
)。子菜单通过 children
属性定义。
data() {
return {
// 控制菜单是否折叠
isCollapse: false,
// 菜单数据
menuData: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'MallManage/MallManage'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'UserManage/UserManage'
},
{
label: '其他',
icon: 'location',
name:'setting', //名字必须要有
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'Other/PageOne'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'Other/PageTwo'
}
]
}
]
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 计算属性
通过计算属性,将菜单数据分类为有子菜单和无子菜单两类。
computed: {
// 筛选没有子菜单的项
noChildren() {
return this.menuData.filter(item => !item.children);
},
// 筛选有子菜单的项
hasChildren() {
return this.menuData.filter(item => item.children);
}
}
2
3
4
5
6
7
8
9
10
# 一级菜单的实现
首先,我们实现没有子菜单的一级菜单。使用 v-for
指令遍历 noChildren
,生成 <el-menu-item>
。
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
:router="true"
>
<!-- 遍历无子菜单的项 -->
<el-menu-item
v-for="item in noChildren"
:key="item.name"
:index="item.path"
>
<!-- 动态设置图标 -->
<i :class="`el-icon-${item.icon}`"></i>
<!-- 显示标签 -->
<span slot="title">{{ item.label }}</span>
</el-menu-item>
</el-menu>
</template>
<script>
export default {
data() {
return {
// 控制激活的菜单项
activeIndex: '/processing-center',
// 菜单数据
menuData: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'MallManage/MallManage'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'UserManage/UserManage'
},
{
label: '其他',
icon: 'location',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'Other/PageOne'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'Other/PageTwo'
}
]
}
]
};
},
computed: {
// 筛选没有子菜单的项
noChildren() {
return this.menuData.filter(item => !item.children);
},
// 筛选有子菜单的项
hasChildren() {
return this.menuData.filter(item => item.children);
}
}
};
</script>
<style>
/* 添加样式以增强用户体验 */
.el-menu-vertical-demo {
background-color: #fff;
border-right: 1px solid #ebeef5;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 二级菜单的实现
接下来,实现包含子菜单的二级菜单。使用 v-for
指令遍历 hasChildren
,生成 <el-submenu>
,并在其中遍历子菜单项生成 <el-menu-item>
。
<template>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
:router="true"
>
<!-- 遍历有子菜单的项 -->
<el-submenu
v-for="item in hasChildren"
:key="item.name"
:index="item.name"
>
<template slot="title">
<!-- 动态设置图标 -->
<i :class="`el-icon-${item.icon}`"></i>
<!-- 显示标签 -->
<span slot="title">{{ item.label }}</span>
</template>
<!-- 遍历子菜单项 -->
<el-menu-item
v-for="child in item.children"
:key="child.name"
:index="child.path"
>
<!-- 动态设置子菜单项图标 -->
<i :class="`el-icon-${child.icon}`"></i>
{{ child.label }}
</el-menu-item>
</el-submenu>
</el-menu>
</template>
<script>
export default {
data() {
return {
// 控制激活的菜单项
activeIndex: '/processing-center',
// 菜单数据
menuData: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home',
url: 'Home/Home'
},
{
path: '/mall',
name: 'mall',
label: '商品管理',
icon: 'video-play',
url: 'MallManage/MallManage'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user',
url: 'UserManage/UserManage'
},
{
label: '其他',
icon: 'location',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting',
url: 'Other/PageOne'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting',
url: 'Other/PageTwo'
}
]
}
]
};
},
computed: {
// 筛选没有子菜单的项
noChildren() {
return this.menuData.filter(item => !item.children);
},
// 筛选有子菜单的项
hasChildren() {
return this.menuData.filter(item => item.children);
}
}
};
</script>
<style>
/* 添加样式以增强用户体验 */
.el-menu-vertical-demo {
background-color: #fff;
border-right: 1px solid #ebeef5;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 动态路由导航菜单(最终版本)
最后,将一级菜单和二级菜单的实现合并,形成完整的动态导航菜单,菜单数据(menuData
)后面可以存到后端数据库,根据登录人的权限获取对应的菜单数据。
# 1. 导出静态菜单数据
将静态菜单数据保存在mock/menuData.js
文件中并导出
const menuData =[
{
path: '/home',
name: 'dashboard',
label: '主页',
icon: 's-home',
url: 'Home/home'
},
{
path: '/report',
name: 'reports',
label: '报告管理',
icon: 'document',
url: 'Reports/Reports'
},
{
label: '设置',
icon: 'setting',
name: 'setting',
children: [
{
path: '/user',
name: 'profile',
label: '个人资料',
icon: 'user',
url: 'Settings/Profile'
},
{
path: '/setting',
name: 'security',
label: '安全设置',
icon: 'lock',
url: 'Settings/Security'
},
{
path: '/brief',
name: 'brief',
label: '简介',
icon: 'edit-outline',
url: 'Brief/brief'
},
]
},
{
path: '/about',
name: 'about',
label: '关于我们',
icon: 'info',
url: 'About/About'
},
{
path: '/layout',
name: 'layout',
label: '网格布局',
icon: 'info',
url: 'Layout/layout'
}
]
export default menuData
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# 2. 导入数据生成导航栏
<template>
<div>
<!-- 控制菜单是否折叠的按钮 -->
<el-radio-group v-model="isCollapse" style="margin-bottom: 20px;">
<el-radio-button :label="false">展开</el-radio-button>
<el-radio-button :label="true">收起</el-radio-button>
</el-radio-group>
<el-menu
:default-active="activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
:collapse="isCollapse"
:router="true"
>
<!-- 遍历菜单数据,根据是否有子菜单进行不同的渲染 -->
<template v-for="item in menuData">
<!-- 无子菜单项 -->
<el-menu-item
v-if="!item.children"
:key="item.name"
:index="item.path"
>
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
<!-- 有子菜单项 -->
<el-submenu
v-else
:key="item.name"
:index="item.name"
>
<template slot="title">
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</template>
<el-menu-item
v-for="child in item.children"
:key="child.name"
:index="child.path"
>
<i :class="`el-icon-${child.icon}`"></i>
{{ child.label }}
</el-menu-item>
</el-submenu>
</template>
</el-menu>
</div>
</template>
<script>
import menuData from '@/mock/menuData'; //导入菜单数据
export default {
data() {
return {
// 控制激活的菜单项
activeIndex: '/dashboard',
// 控制菜单是否折叠
isCollapse: false,
// 菜单数据
menuData: menuData
};
}
};
</script>
<style>
/* 添加样式以增强用户体验 */
.el-menu-vertical-demo {
background-color: #fff;
border-right: 1px solid #ebeef5;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# 动态添加路由数据
通过 addNewItem
方法,可以动态添加菜单项添保存到后端数据库。调用 this.menuData.push(newItem)
后,Vue 的响应式系统会自动更新视图,不需要刷新页面。
addNewItem() {
this.menuData.push({
path: '/new-path',
name: 'new-item',
label: '新菜单项',
icon: 'new-icon',
url: 'NewComponent/NewComponent'
});
}
2
3
4
5
6
7
8
9
# 根据登录人的权限获取不同的路由
**1. 后端接口:**根据用户权限返回菜单数据。假设后端有一个 getMenuDataForUser
函数,可以根据用户 ID 获取对应的菜单数据。
// Express.js
app.get('/api/menu', (req, res) => {
// 根据用户权限获取菜单数据
const userId = req.user.id; // 假设用户信息存储在 req.user 中
const menuData = getMenuDataForUser(userId); // 根据用户 ID 获取菜单数据的函数
res.json(menuData);
});
2
3
4
5
6
7
2. 前端实现:
- 在
created
生命周期钩子中调用fetchMenuData
方法,获取菜单数据。 - 使用 Axios 发起 GET 请求,获取菜单数据并设置到
menuData
中。 - Vue 的响应式系统会自动检测到
menuData
的变化,并重新渲染菜单。
<script>
import axios from 'axios';
export default {
data() {
return {
activeIndex: '/dashboard',
isCollapse: false,
menuData: []
};
},
methods: {
async fetchMenuData() {
try {
const response = await axios.get('/api/menu');
this.menuData = response.data;
} catch (error) {
console.error('获取菜单数据失败', error);
}
}
},
created() {
this.fetchMenuData();
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 5. 封装组件:侧边导航栏
# 侧边导航栏组件(SideMenu.vue)
子组件通过 props
接收数据:子组件 SideMenu
接收 menuData
、activeIndex
、isCollapse
、logo
和 title
作为 props
。
<template>
<div class="sidebar-content">
<!-- 顶部图标区域 -->
<div v-if="logo || title" class="grid-content logo-container">
<img v-if="logo" :src="logo" alt="网站logo" class="logo">
<span v-if="title" class="logo-text">{{ title }}</span>
</div>
<!-- 菜单导航栏 -->
<el-menu :default-active="localActiveIndex" class="el-menu-vertical-demo" mode="vertical" @open="handleOpen"
@close="handleClose" :collapse="isCollapse" :router="true" @select="selection">
<!-- 遍历菜单数据,根据是否有子菜单进行不同的渲染 -->
<template v-for="item in menuData">
<!-- 无子菜单项 -->
<el-menu-item v-if="!item.children" :key="item.name" :index="item.path">
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
<!-- 有子菜单项 -->
<el-submenu v-else :key="item.name" :index="item.name">
<template slot="title">
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</template>
<el-menu-item v-for="child in item.children" :key="child.name" :index="child.path">
<i :class="`el-icon-${child.icon}`"></i>
{{ child.label }}
</el-menu-item>
</el-submenu>
</template>
</el-menu>
</div>
</template>
<script>
import { watch } from 'vue';
export default {
props: {
menuData: {
type: Array,
required: true
},
activeIndex: {
type: String,
required: true
},
isCollapse: {
type: Boolean,
required: false,
default: false
},
logo: {
type: String,
required: false
},
title: {
type: String,
required: false
}
},
data() {
return {
defaultOpeneds: [], // 默认展开的子菜单
localActiveIndex: this.activeIndex, // 使用本地变量代替直接修改 prop
};
},
methods: {
handleOpen(key, keyPath) {
console.log('展开的子菜单:', key, keyPath);
},
handleClose(key, keyPath) {
console.log('收起的子菜单:', key, keyPath);
},
selection(index, indexPath) { // 菜单项选择的回调函数,index是当前路由路径
this.localActiveIndex = index;
}
},
created() {
const currentPath = this.$route.path;
this.localActiveIndex = currentPath; // 组件刚激活时,设置当前路径为激活状态
},
watch: {
localActiveIndex(newVal) {
console.log('watch:',newVal);
this.localActiveIndex = newVal;
},
immediate: true, // 立即执行回调
}
};
</script>
<style>
/* 设置侧边栏背景色 */
.scrollable-aside {
background-color: #242b3e !important;
}
/* 设置滚动区域和滚动条样式 */
.sidebar-content {
overflow-y: auto;
height: 100vh;
/* 设置高度为视口高度 */
}
.sidebar-content::-webkit-scrollbar {
width: 8px;
/* 滚动条宽度 */
}
.sidebar-content::-webkit-scrollbar-track {
background: #242b3e;
/* 滚动条轨道背景色 */
}
.sidebar-content::-webkit-scrollbar-thumb {
background-color: #555;
/* 滚动条滑块背景色 */
border-radius: 10px;
/* 滑块圆角 */
border: 2px solid #242b3e;
/* 滑块边框 */
}
/* 顶部图标区域样式 */
.logo-container {
height: 60px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
/* 增强阴影效果 */
background-color: #242b3e;
/* 确保背景色与侧边栏一致 */
z-index: 2;
/* 确保阴影效果在前 */
position: relative;
/* 确保定位正确 */
}
.logo {
height: 40px;
margin-right: 8px;
/* 添加右边距,使文字与图标分开 */
}
.logo-text {
color: white;
font-size: 15px;
font-weight: bold;
}
/* 菜单的样式 */
.el-menu-vertical-demo:not(.el-menu--collapse) {
min-height: 500px;
height: calc(100vh - 60px);
/* 计算高度,减去顶部图标区域的高度 */
}
/* 菜单项样式 */
.el-menu {
border-right: solid 0px #e6e6e6 !important;
height: 100%;
background-color: #222b40;
}
.el-submenu__title {
color: #ccc;
height: 50px;
line-height: 50px;
}
.el-menu-item {
color: #ccc;
height: 50px;
line-height: 50px;
background-color: #222b40 !important;
/* 必须加上这个背景色,否则鼠标离开会出现白色 */
}
.el-menu-item.is-active {
color: #3399ff !important;
}
.el-menu--inline .el-menu-item {
padding-left: 50px !important;
}
.el-submenu__title:hover {
background-color: #222 !important;
}
.el-menu-item:not(.is-active):hover {
background-color: #222 !important;
}
.el-menu-item:hover {
background-color: #222 !important;
}
.el-submenu__icon-arrow {
margin-top: -5px;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# vuex版本
为什么推出vuex版本呢?
- 我最近遇到了一个问题,就是在封装的导航栏组件中,使用编程式导航跳转路由时,发现导航栏的菜单项无法正确监听到路由的变化,导致菜单激活状态不能同步更新。这种情况下,路由虽然跳转了,但对应的菜单项却没有被激活,非常不方便。
- 为了解决这个问题,我决定将菜单的激活状态存储在 Vuex 中。这样无论是在任何组件中使用编程式导航,只要跳转路由时同步更新 Vuex 中的激活状态,导航栏就能正确地反映当前的路由状态,完美解决了激活状态不同步的问题。
1. store/index.js如下
// 引入 Vue 核心库
import Vue from 'vue';
// 引入 Vuex
import Vuex from 'vuex';
// 应用 Vuex 插件
Vue.use(Vuex);
// 准备 state 对象——保存具体的数据
const state = {
activeIndex:'/home',
};
// 准备 actions 对象——响应组件中用户的动作
const actions = {};
// 准备 mutations 对象——修改 state 中的数据
const mutations = {
EditActiveIndex(state,val) {
state.activeIndex = val;
}
};
// 创建并导出 store
export default new Vuex.Store({
actions,
mutations,
state
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2. VueX版本侧边导航栏组件(SideMenu.vue)
<template>
<div class="sidebar-content">
<!-- 顶部图标区域 -->
<div v-if="logo || title" class="grid-content logo-container" :class="{ 'logo-collapsed': isCollapse }">
<img v-if="logo" :src="logo" alt="网站logo" class="logo">
<span v-if="title && !isCollapse" class="logo-text">{{ title }}</span>
</div>
<!-- 菜单导航栏 -->
<el-menu :default-active="$store.state.activeIndex"
class="el-menu-vertical-demo"
mode="vertical"
@open="handleOpen"
@close="handleClose"
:collapse="isCollapse"
:router="true"
@select="selection"
:collapse-transition="false">
<!-- 遍历菜单数据,根据是否有子菜单进行不同的渲染 -->
<template v-for="item in menuData">
<!-- 无子菜单项 -->
<el-menu-item v-if="!item.children" :key="item.name" :index="item.path">
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
<!-- 有子菜单项 -->
<el-submenu v-else :key="item.name" :index="item.name">
<template slot="title">
<i :class="`el-icon-${item.icon}`"></i>
<span slot="title">{{ item.label }}</span>
</template>
<el-menu-item v-for="child in item.children" :key="child.name" :index="child.path">
<i :class="`el-icon-${child.icon}`"></i>
{{ child.label }}
</el-menu-item>
</el-submenu>
</template>
</el-menu>
</div>
</template>
<script>
export default {
props: {
menuData: {
type: Array,
required: true
},
activeIndex: {
type: String,
required: false
},
isCollapse: {
type: Boolean,
required: false,
default: false
},
logo: {
type: String,
required: false
},
title: {
type: String,
required: false
}
},
data() {
return {};
},
methods: {
handleOpen(key, keyPath) {
console.log('展开的子菜单:', key, keyPath);
},
handleClose(key, keyPath) {
console.log('收起的子菜单:', key, keyPath);
},
selection(index, indexPath) {
console.log(index);
this.$store.commit('EditActiveIndex', index);
},
},
created() {
const currentPath = this.$route.path;
this.$store.commit('EditActiveIndex', currentPath); // 组件刚激活时,设置当前路径为激活状态
},
watch: {
// 监听路由的变化
'$route.path'(newVal) {
console.log('Route changed:', newVal);
this.$store.commit('EditActiveIndex', newVal); // 根据新的路径更新 Vuex 中的 activeIndex
}
}
};
</script>
<style>
/* 确保侧边栏的背景色一致 */
.scrollable-aside,
.sidebar-content,
.logo-container {
background-color: #242b3e !important;
flex-shrink: 0; /* 确保侧边栏不收缩 */
}
/* 设置侧边栏背景色 */
.scrollable-aside {
background-color: #242b3e !important;
}
/* 设置滚动区域和滚动条样式 */
.sidebar-content {
height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden; /* 隐藏水平滚动条 */
overflow-y: hidden; /* 仅在需要时显示垂直滚动条 */
}
/* 顶部图标区域样式 */
.logo-container {
height: 60px;
display: flex;
align-items: center;
justify-content: flex-start; /* 使内容与左侧对齐 */
padding-left: 20px; /* 与菜单项的 padding 对齐 */
background-color: #222b40;
z-index: 2;
position: relative;
transition: width 0.3s, height 0.3s;
overflow: hidden; /* 保证在收起状态下内容不溢出 */
min-width: 60px; /* 确保在收起时宽度不写死,但也不影响页面布局 */
flex-shrink: 0; /* 避免收缩 */
}
.logo-collapsed {
justify-content: center;
background-color: #222b40 !important;
width: 60px; /* 固定宽度防止引起布局问题 */
height: 60px; /* 保证高度一致 */
flex-shrink: 0;
padding-left: 0; /* 去掉收起时的 padding */
}
/* 保证 logo 在收起时的尺寸合适 */
.logo {
height: 30px;
width: 30px; /* 保证logo在收起状态下的大小更小 */
margin-right: 0; /* 去掉右边距 */
transition: height 0.3s, width 0.3s;
}
.logo-collapsed .logo {
height: 20px; /* 收起时的logo高度 */
width: 20px; /* 收起时的logo宽度 */
}
/* 保证文本仅在展开时显示 */
.logo-text {
color: white;
font-size: 15px;
font-weight: bold;
margin-left: 10px; /* 确保文本与图标间有适当的间距 */
display: none; /* 默认隐藏文本 */
}
.logo-container:not(.logo-collapsed) .logo-text {
display: block; /* 仅在展开状态下显示文本 */
}
/* 菜单的样式 */
.el-menu-vertical-demo:not(.el-menu--collapse) {
min-height: 500px;
height: calc(100vh - 60px);
/* 计算高度,减去顶部图标区域的高度 */
width: auto; /* 避免固定宽度 */
transition: all 0.3s; /* 添加平滑过渡效果 */
}
.el-menu-vertical-demo.el-menu--collapse {
width: 60px; /* 收起时固定宽度 */
}
/* 菜单项样式 */
.el-menu {
border-right: solid 0px #e6e6e6 !important;
height: 100%;
background-color: #222b40;
flex-shrink: 0; /* 确保不缩放 */
transition: all 0.3s; /* 添加平滑过渡效果 */
}
.el-submenu__title {
color: #ccc;
height: 50px;
line-height: 50px;
transition: all 0.3s; /* 添加平滑过渡效果 */
}
.el-menu-item {
color: #ccc;
height: 50px;
line-height: 50px;
padding-left: 20px; /* 与顶部 logo-container 保持一致 */
background-color: #222b40 !important;
/* 必须加上这个背景色,否则鼠标离开会出现白色 */
transition: all 0.3s; /* 添加平滑过渡效果 */
}
.el-menu-item.is-active {
color: #3399ff !important;
}
.el-menu--inline .el-menu-item {
padding-left: 50px !important;
}
.el-submenu__title:hover {
background-color: #222 !important;
}
.el-menu-item:not(.is-active):hover {
background-color: #222 !important;
}
.el-menu-item:hover {
background-color: #222 !important;
}
.el-submenu__icon-arrow {
margin-top: -5px;
}
</style>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# 父组件中的使用
父组件传递数据:父组件 App
在使用 SideMenu
组件时,绑定相应的 props
数据。菜单数据(menuData
) 后面我们可以从数据库获取,根据不同人的权限展示不同的菜单。
<template>
<div>
<template>
<el-container style="height: 100vh;">
<!-- 左侧侧边栏 -->
<el-aside :width="isCollapse ? '64px' : '210px'">
<!-- 导航栏组件 -->
<sidebar :menuData="menuData" :activeIndex="activeIndex" :isCollapse="isCollapse" :logo="logo" :title="title">
</sidebar>
</el-aside>
<!-- 主体部分 -->
<el-container>
<!-- 顶部导航栏 -->
<el-header height="60px" style="background-color: pink;">
<div class="bannerbox" style="height: 100%;">
<el-col :span="22">
<div class="manager-header-center">
<div style="display: flex;align-items: center">
<!--收起左侧菜单的按钮-->
<el-button icon="el-icon-menu" size="mini" @click.native="clickCollapse" style="margin-right: 8px">
</el-button>
<!-- 使用 Element UI 的面包屑组件显示导航路径 -->
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/main'}">首页</el-breadcrumb-item>
<i class="el-icon-youjiantou"></i>
<el-breadcrumb-item :to="{ path: $route.path }">{{ $route.meta.name }}</el-breadcrumb-item>
</el-breadcrumb>
</div>
</div>
</el-col>
<el-col :span="2">
<user-dropdown v-model="activeIndex" :avatar-url=$store.state.user.avatar></user-dropdown>
</el-col>
</div>
</el-header>
<!-- 主要内容区域 -->
<el-main style="background-color: white;" class="scrollable-main">
<!-- <tabs></tabs> -->
<router-view></router-view>
</el-main>
</el-container>
</el-container>
</template>
</div>
</template>
<script>
import SideMenu from './components/SideMenu.vue';
import logo from '@/assets/logo.png'; // 注意如果传递的logo是本地地址,必须导入,直接写无效
export default {
name: 'App',
components: {
SideMenu
},
data() {
return {
activeIndex: '/home/demo1', // 当前激活的菜单项路径
isCollapse: false, // 控制菜单是否折叠
logo: ‘logo’,
title: ‘在线后台管理系统’,
// 菜单数据,包括一个一级菜单和一个带子菜单的二级菜单
menuData: [
{
path: '/home/demo1',
name: 'dashboard',
label: '主页',
icon: 's-home',
url: 'Dashboard/Dashboard'
},
{
path: '/reports/demo2',
name: 'reports',
label: '报告管理',
icon: 'document',
url: 'Reports/Reports'
},
{
label: '设置',
icon: 'setting',
children: [ // 二级子菜单
{
path: '/setting/demo3',
name: 'profile',
label: '个人资料',
icon: 'user',
url: 'Settings/Profile'
}
]
},
{
path: '/about',
name: 'about',
label: '关于我们',
icon: 'info',
url: 'About/About'
}
]
};
},
methods: {
// 侧边菜单点击收缩
clickCollapse() {
console.log("测试菜单栏收起点击");
this.isCollapse = !this.isCollapse;
},
// 处理菜单展开事件
handleMenuOpen(key, keyPath) {
console.log('展开的子菜单:', key, keyPath);
},
// 处理菜单收起事件
handleMenuClose(key, keyPath) {
console.log('收起的子菜单:', key, keyPath);
}
}
};
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 侧边导航栏的两种布局
如果父组件没有传递 logo
或 title
,顶部图标区域将不显示。之所以这样设计的目的是为了应对不同的页面布局。
1. 侧边栏+(顶部+内容)布局:这种布局适合传递 logo
或 title
。
2. 顶部+(侧边栏+内容)布局:这种布局不适合传递 logo
或 title
。
侧边导航栏组件:
props
接收menuData
、activeIndex
、isCollapse
、logo
和title
。handleOpen
和handleClose
方法用于处理子菜单的展开和收起事件,并通过$emit
将事件传递给父组件。- 样式部分设置了滚动条样式、顶部图标区域样式和菜单样式。
父组件:
- 通过
props
传递menuData
、activeIndex
、isCollapse
、logo
和title
给SideMenu
组件。 menuData
包含菜单项的数据,activeIndex
是当前激活的菜单项,isCollapse
控制菜单是否折叠。- 监听
SideMenu
组件的menu-open
和menu-close
事件,通过handleMenuOpen
和handleMenuClose
方法处理这些事件。
- 通过