P4-Vue3后台管理系统-顶部导航与左侧导航联动面包屑
1.概述
上篇文章实现了左侧边栏导航,这篇文章我们开发顶部导航。
2.顶部导航栏设置
2.1.设置导航栏背景色
- 在Main.vue组建中设置Header导航栏背景色
2.1.Header添加按钮
- 官网中找到Button按钮–图标按钮–复制图标按钮代码
- 应用到CommonHeader组件中
- 官网搜索图标替换按钮默认图标
2.2.添加头像下拉菜单
- Element官网找到点击触发下拉菜单
- 复制点击触发下拉菜单代码
- 应用到CommonHeader组件中
- CommonHeader组件完整代码
<template>
<header>
<div class="l-content">
<!-- sezi设置按钮尺寸 -->
<el-button type="primary" icon="el-icon-menu" size="mini"></el-button>
</div>
<!-- 设置导航栏右边头像 -->
<div class="r-content">
<el-dropdown trigger="click">
<span class="el-dropdown-link">下拉菜单<i class="el-icon-arrow-down el-icon--right"></i></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-plus">黄金糕</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus">狮子头</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus-outline">螺蛳粉</el-dropdown-item>
<el-dropdown-item icon="el-icon-check">双皮奶</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-check">蚵仔煎</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</header>
</template>
<script>
export default {}
</script>
<style lang="scss" scoped>
// 设置header为弹性容器
header {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
height: 100%;
// 设置元素两端对齐
justify-content: space-between;
}
</style>
-CommonHeader组件导航栏效果
2.3.添加下拉菜单头像
- 添加下拉头像
- 下拉头像代码
<template>
<header>
<div class="l-content">
<!-- sezi设置按钮尺寸 -->
<el-button type="primary" icon="el-icon-menu" size="mini"></el-button>
</div>
<!-- 设置导航栏右边头像 -->
<div class="r-content">
<el-dropdown trigger="click">
<!-- img:用户头像是从data中动态获取,不是写死的。 -->
<span class="el-dropdown-link"><img :src="userImg" class="user"/></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item icon="el-icon-plus">黄金糕</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus">狮子头</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-plus-outline">螺蛳粉</el-dropdown-item>
<el-dropdown-item icon="el-icon-check">双皮奶</el-dropdown-item>
<el-dropdown-item icon="el-icon-circle-check">蚵仔煎</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</header>
</template>
<script>
export default {
data() {
return {
userImg: require('../assets/images/user.png')
}
}
}
</script>
<style lang="scss" scoped>
// 设置header为弹性容器
header {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
height: 100%;
// 设置元素两端对齐
justify-content: space-between;
}
.r-content {
.user {
// 设置头像宽高
width: 40px;
height: 40px;
// 设置头像为圆形
border-radius: 50%;
}
}
</style>
2.4.设置下拉菜单内容
删除多余的下拉内容,只保留两个下拉选项。同时改名为个人中心和退出。删除两个下拉项的icon图标
3.设置左侧面包屑
3.1.添加面包屑样式
- 复制Element官网面包屑基础用法的代码
- 应用到CommonHeader组件
3.2.设置面包屑对齐方式
- 设置水平居中
3.3.设置面包屑文字颜色
- 查看面包屑内容CSS样式
- 复制CSS的类选择器到CommonHeader组件中
- 面包屑的内容改为白色
3.4.设置面包屑间距
- 设置button按钮和面包屑的外边间距
- 设置margin后的效果
3.5.删除多余的面包屑内容
3.6.CommonHeader组件完整内容
<template>
<header>
<div class="l-content">
<!-- sezi设置按钮尺寸 -->
<el-button type="primary" icon="el-icon-menu" size="mini"></el-button>
<!-- 设置面包屑 -->
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item><a href="/">活动管理</a></el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 设置导航栏右边头像 -->
<div class="r-content">
<el-dropdown trigger="click">
<!-- img:用户头像是从data中动态获取,不是写死的。 -->
<span class="el-dropdown-link"><img :src="userImg" class="user"/></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</header>
</template>
<script>
export default {
data() {
return {
userImg: require('../assets/images/user.png')
}
}
}
</script>
<style lang="scss" scoped>
// 设置header为弹性容器
header {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
height: 100%;
// 设置元素两端对齐
justify-content: space-between;
}
// 设置l-content为弹性容器
.l-content {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
// 设置button按钮外右间距
.el-button {
margin-right: 20px;
}
}
.r-content {
.user {
// 设置头像宽高
width: 40px;
height: 40px;
// 设置头像为圆形
border-radius: 50%;
}
}
</style>
<style lang="scss">
.el-breadcrumb__inner a,
.el-breadcrumb__inner.is-link {
color: #fff;
}
</style>
4.添加状态管理
通过Vuex来管理各个组件的状态。
4.1.新建状态管理文件
在store文件夹下新建tab.js文件来管理导航栏的组件状态
4.2.导入tab文件模块
- tab.js中写状态管理的内容
- index.js导入tab模块
4.3.获取CommonAside点击的item
- 获取菜单item方法思路
- CommonAside组件内容
<template>
<el-menu default-active="2" class="el-menu-vertical-demo" background-color="#33adf0" text-color="#fff" active-text-color="#ffd04b">
<!-- 配置没有二级菜单的菜单路由内容
- 通过v-for循环遍历 noChildren 数据对象,分别取出:
- 菜单路由item.path
- 菜单icon
- 菜单title名称
-->
<el-menu-item :index="item.path" v-for="item in noChildren" :key="item.path" @click="clickMenu(item)">
<i :class="'el-icon-' + item.icon"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
<!-- 配置有二级菜单的菜单路由内容
- 通过v-for循环遍历 hasChildren 数据对象,分别取出:
- 一级菜单的index
- 一级菜单icon
- 一级菜单名字
通过遍历当前一级菜单的二级菜单数据对象,分别取出:
- 二级菜单index
- 二级菜单名字label
- 二级菜单icon
-->
<el-submenu :index="item.label" v-for="(item, index) in hasChildren" :key="index">
<template slot="title">
<i :class="'el-icon-' + item.icon"></i>
<span>{{ item.label }}</span>
</template>
<el-menu-item-group>
<el-menu-item :index="subItem.path" v-for="(subItem, subIndex) in item.children" :key="subIndex" @click="clickMenu(subItem)">
{{ subItem.label }}
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</template>
<script>
export default {
computed: {
// 获取菜单对象中不包含子级的菜单
noChildren() {
return this.asideMenu.filter(item => !item.children)
},
// 获取菜单对象中包含子级的菜单
hasChildren() {
return this.asideMenu.filter(item => item.children)
}
},
data() {
return {
// 配置菜单路由
asideMenu: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home'
},
{
path: '/video',
name: 'video',
label: '视频管理',
icon: 'video-play'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user'
},
// 设置子级菜单路由
{
label: '其他',
icon: 'user',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting'
}
]
}
]
}
},
methods: {
// clickMenu方法获取用户点击左侧导航栏的item状态
clickMenu(item) {
// 调用store中的tab.js文件定义的selectMenu方法
this.$store.commit('selectMenu', item)
}
}
}
</script>
<style lang="scss" scoped>
.el-menu {
height: 100%;
border: none;
}
</style>
- 通过vue插件查看事件给绑定的方法传递参数值
4.4.ComonHeader面包屑接收菜单item数据
- 面包屑获取菜单item原理
- 面包屑获取菜单item代码
<template>
<header>
<div class="l-content">
<!-- sezi设置按钮尺寸 -->
<el-button type="primary" icon="el-icon-menu" size="mini"></el-button>
<!-- 设置面包屑 -->
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>
<!-- 从计算属性的current中获取用户点击菜单的label,然后在面包屑中展示该菜单的label -->
<a href="/">{{ current.label }}</a>
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 设置导航栏右边头像 -->
<div class="r-content">
<el-dropdown trigger="click">
<!-- img:用户头像是从data中动态获取,不是写死的。 -->
<span class="el-dropdown-link"><img :src="userImg" class="user"/></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</header>
</template>
<script>
// 导入vuex状态管理模块
import { mapState } from 'vuex'
export default {
computed: {
// 获取store下tab.js文件selectMenu方法中currentMenu对象数据
...mapState({
current: state => state.tab.currentMenu
})
},
data() {
return {
userImg: require('../assets/images/user.png')
}
}
}
</script>
<style lang="scss" scoped>
// 设置header为弹性容器
header {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
height: 100%;
// 设置元素两端对齐
justify-content: space-between;
}
// 设置l-content为弹性容器
.l-content {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
// 设置button按钮外右间距
.el-button {
margin-right: 20px;
}
}
.r-content {
.user {
// 设置头像宽高
width: 40px;
height: 40px;
// 设置头像为圆形
border-radius: 50%;
}
}
</style>
<style lang="scss">
.el-breadcrumb__inner a,
.el-breadcrumb__inner.is-link {
color: #fff;
}
</style>
4.5.去除首页面包屑
当点击首页时候会重复显示首页,这个时候我们就需要将首页过滤掉
- CommonHeader面包屑添加判断是否展示菜单
- tab.js中判断当前传入的菜单名称是否为home
4.6. 设置当前激活菜单的面包屑样式
- 查看面包屑菜单样式CSS类选择器
- CommonHeader组件中设置该面包屑样式
- 设置面包屑的效果
4.7.设置面包屑完整代码
- tab.js文件
export default {
state: {
menu: [],
currentMenu: null
},
mutations: {
selectMenu(state, val) {
// 判断当前菜单name是否为home,如果是则将currentMenu属性设置为null,如果不是则将val赋值给currentMenu属性
val.name === 'home' ? (state.currentMenu = null) : (state.currentMenu = val)
}
},
actions: {},
modules: {}
}
- CommonAside.vue组件
<template>
<el-menu default-active="2" class="el-menu-vertical-demo" background-color="#33adf0" text-color="#fff" active-text-color="#ffd04b">
<!-- 配置没有二级菜单的菜单路由内容
- 通过v-for循环遍历 noChildren 数据对象,分别取出:
- 菜单路由item.path
- 菜单icon
- 菜单title名称
-->
<el-menu-item :index="item.path" v-for="item in noChildren" :key="item.path" @click="clickMenu(item)">
<i :class="'el-icon-' + item.icon"></i>
<span slot="title">{{ item.label }}</span>
</el-menu-item>
<!-- 配置有二级菜单的菜单路由内容
- 通过v-for循环遍历 hasChildren 数据对象,分别取出:
- 一级菜单的index
- 一级菜单icon
- 一级菜单名字
通过遍历当前一级菜单的二级菜单数据对象,分别取出:
- 二级菜单index
- 二级菜单名字label
- 二级菜单icon
-->
<el-submenu :index="item.label" v-for="(item, index) in hasChildren" :key="index">
<template slot="title">
<i :class="'el-icon-' + item.icon"></i>
<span>{{ item.label }}</span>
</template>
<el-menu-item-group>
<el-menu-item :index="subItem.path" v-for="(subItem, subIndex) in item.children" :key="subIndex" @click="clickMenu(subItem)">
{{ subItem.label }}
</el-menu-item>
</el-menu-item-group>
</el-submenu>
</el-menu>
</template>
<script>
export default {
computed: {
// 获取菜单对象中不包含子级的菜单
noChildren() {
return this.asideMenu.filter(item => !item.children)
},
// 获取菜单对象中包含子级的菜单
hasChildren() {
return this.asideMenu.filter(item => item.children)
}
},
data() {
return {
// 配置菜单路由
asideMenu: [
{
path: '/',
name: 'home',
label: '首页',
icon: 's-home'
},
{
path: '/video',
name: 'video',
label: '视频管理',
icon: 'video-play'
},
{
path: '/user',
name: 'user',
label: '用户管理',
icon: 'user'
},
// 设置子级菜单路由
{
label: '其他',
icon: 'user',
children: [
{
path: '/page1',
name: 'page1',
label: '页面1',
icon: 'setting'
},
{
path: '/page2',
name: 'page2',
label: '页面2',
icon: 'setting'
}
]
}
]
}
},
methods: {
// clickMenu方法获取用户点击左侧导航栏的item状态
clickMenu(item) {
// 调用store中的tab.js文件定义的selectMenu方法
this.$store.commit('selectMenu', item)
}
}
}
</script>
<style lang="scss" scoped>
.el-menu {
height: 100%;
border: none;
}
</style>
- CommonHeader.vue组件
<template>
<header>
<div class="l-content">
<!-- sezi设置按钮尺寸 -->
<el-button type="primary" icon="el-icon-menu" size="mini"></el-button>
<!-- 设置面包屑 -->
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<!-- 通过判断current是否为null设置面包屑是否显示菜单名称 -->
<el-breadcrumb-item :to="current.path" v-if="current">
<!-- 从计算属性的current中获取用户点击菜单的label,然后在面包屑中展示该菜单的label -->
{{ current.label }}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 设置导航栏右边头像 -->
<div class="r-content">
<el-dropdown trigger="click">
<!-- img:用户头像是从data中动态获取,不是写死的。 -->
<span class="el-dropdown-link"><img :src="userImg" class="user"/></span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>个人中心</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</header>
</template>
<script>
// 导入vuex状态管理模块
import { mapState } from 'vuex'
export default {
computed: {
// 获取store下tab.js文件selectMenu方法中currentMenu对象数据
...mapState({
current: state => state.tab.currentMenu
})
},
data() {
return {
userImg: require('../assets/images/user.png')
}
}
}
</script>
<style lang="scss" scoped>
// 设置header为弹性容器
header {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
height: 100%;
// 设置元素两端对齐
justify-content: space-between;
}
// 设置l-content为弹性容器
.l-content {
display: flex;
// 设置弹性容器中元素水平方向居中
align-items: center;
// 设置button按钮外右间距
.el-button {
margin-right: 20px;
}
}
.r-content {
.user {
// 设置头像宽高
width: 40px;
height: 40px;
// 设置头像为圆形
border-radius: 50%;
}
}
</style>
<style lang="scss">
.el-breadcrumb__item {
// 设置面包屑首页样式
.el-breadcrumb__inner {
color: #fff;
}
// 设置面包屑首页后面菜单样式
&:last-child {
.el-breadcrumb__inner {
color: #f4f4f4;
}
}
}
</style>