之前的导航栏是静态写死的,现在我们需要将其改为动态的:路由修改后,导航栏也随之修改,页面根据路由进行跳转。
分析代码
原SideBar.vue中导航栏代码
<el-menu
default-active="3-1"
class="el-menu-vertical-demo"
@open="isOpen"
@close="isClose"
:collapse="isCollapse">
<el-menu-item index="1">
<div class="nav-first">
<i class="el-icon-setting"></i>
<span slot="title">首页</span>
</div>
</el-menu-item>
<el-submenu index="2">
<template slot="title">
<div class="nav-first">
<i class="el-icon-location"></i>
<span slot="title">导航一</span>
</div>
</template>
<el-menu-item index="2-1">
<span slot="title"><span class="dot"></span>选项一</span>
</el-menu-item>
<el-menu-item index="2-2">
<span slot="title"><span class="dot"></span>选项2</span>
</el-menu-item>
<el-menu-item index="2-3">
<span slot="title"><span class="dot"></span>选项一</span>
</el-menu-item>
<el-submenu index="2-4">
<span slot="title"><span class="dot"></span>选项4</span>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="3">
<template slot="title">
<div class="nav-first">
<i class="el-icon-location"></i>
<span slot="title">测试页面</span>
</div>
</template>
<el-menu-item index="3-1" @click="$router.push('/3-1')">
<span slot="title"><span class="dot"></span>testAxios</span>
</el-menu-item>
<el-menu-item index="3-2" @click="$router.push('/3-2')">
<span slot="title"><span class="dot"></span>testMock</span>
</el-menu-item>
<el-menu-item index="3-3" @click="$router.push('/3-3')">
<span slot="title"><span class="dot"></span>主题测试</span>
</el-menu-item>
</el-submenu>
<!-- <el-menu-item index="4">
<div class="nav-first">
<i class="el-icon-document"></i>
<span slot="title">404</span>
</div>
</el-menu-item> -->
<el-menu-item index="4" disabled>
<div class="nav-first">
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</div>
</el-menu-item>
<el-menu-item index="5">
<div class="nav-first">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</div>
</el-menu-item>
</el-menu>
分析代码得:
每一级导航都有不一样的写法,那我们可以设置一些属性来控制不同级数的导航的不同写法:
并且有些路由比如Login不需要在导航栏显示,所以添加一个属性:isHidden,当isHidden未true时,不在导航栏显示。
导航栏中显示的文字和一级导航需要的图标可以通过添加一个对象meta来控制。
确定路由的写法如下:
- 不需要在导航栏显示
{
path: '/', //http://localhost:8081/#/
name: 'Home',
component: Home, //import的组件名
isHidden:true, //不在导航列表中显示
redirect: '/menu1', //当路由未匹配时重定向,可作初始化显示页面设置
},
{
path: '/Login',
name: 'Login',
component: Login,
isHidden:true //不在导航列表中显示
},
- 一级菜单
{
path: '/menu3',
component: Home, //是Home页面的布局
name: 'menu3',
redirect:'/menu3-1', //当路由未匹配时重定向,可作初始化显示页面设置
meta: {
title: '二级导航menu3',
icon: 'el-icon-document'
},
children: [
{
path: '/menu3-1',
name: 'menu3-1',
meta: {
title: 'menu3-1'
},
component: resolve => require(['@/views/uiMenu/Ui.vue'], resolve),
}
]
}
这里说明一下为什么要这样写,而不能直接像前面——不在导航栏显示 的那样写,因为一级导航页面需要通过Home页面打开,若按照前面的写法,是重新打开一个新页面,比如登录页面,布局和Home完全无关。
- 二级菜单
{
path: '/menu2',
component: Home,
name: 'menu2',
leaf:true,//有二级路由
meta: {
title: '测试页面',
icon: 'el-icon-location'
},
children: [
{
path: '/menu2-1',
name: 'menu2-1',
meta: {
title: 'TestAxios'
},
component: resolve => require(['@/views/testPages/TestAxios.vue'], resolve),
},
{
path: '/menu2-2',
name: 'menu2-2',
meta: {
title: 'TestMock'
},
component: resolve => require(['@/views/testPages/TestMock.vue'], resolve),
},
{
path: '/menu2-3',
name: 'menu2-3',
meta: {
title: '主题测试'
},
component: resolve => require(['@/views/testPages/TestUI.vue'], resolve),
}
]
},
一级和二级的区别在于二级添加了有二级菜单这个属性“ leaf:true ”, 少了重新定向这个“ redirect:'/menu3-1' ”,没有这个属性
- 三级菜单
{
path: '/menu1',
component: Home,
name: 'menu1',
leafThree: true,//有三级路由
meta: {
title: '三级导航1',
icon: 'el-icon-location'
},
children: [
{
path: '/menu1-1',
name: 'menu1-1',
meta: {
title: '三级导航1-1'
},
component: Mrouter,
leaf: true,//有二级路由
children:[
{
path: '/menu1-1-1',
name: 'menu1-1-1',
meta: {
title: '三级导航1-1-1'
},
component: resolve => require(['@/views/testPages/menu1/Menu1-1-1.vue'], resolve),
}
]
},
{
path: '/menu1-2',
name: 'menu1-2',
meta: {
title: '三级导航1-2'
},
component: resolve => require(['@/views/testPages/menu1/Menu1-2.vue'], resolve),
}
]
},
二级和三级的区别在于一级目录添加了有三级菜单这个属性“ leafThree:true ”, 二级children中多了“ leaf: true ” 和 在component处修改为“ component: Mrouter ”,并添加children
修改 router/index.js
import Vue from 'vue'
import Router from 'vue-router'
//页面布局
import Home from '@/views/Home' //页面布局
import Mrouter from '@/views/common/main/Mrouter' //路由承载公共组件
//单个页面
import NotFound from '@/views/err/404' //404页面
import Login from '@/views/login/Login' //登录页面
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/', //http://localhost:8081/#/
name: 'Home',
component: Home, //import的组件名
isHidden:true, //不在导航列表中显示
redirect: '/menu1', //当路由未匹配时重定向,可作初始化显示页面设置
},
{
path: '/Login',
name: 'Login',
component: Login,
isHidden:true //不在导航列表中显示
},
{
path: '/404',
name: 'NotFound',
component: NotFound,
isHidden:true//不在导航列表中显示
},
{
path: '/menu1',
component: Home,
name: 'menu1',
leafThree: true,//有三级路由
meta: {
title: '三级导航1',
icon: 'el-icon-location'
},
children: [
{
path: '/menu1-1',
name: 'menu1-1',
meta: {
title: '三级导航1-1'
},
component: Mrouter,
leaf: true,//有二级路由
children:[
{
path: '/menu1-1-1',
name: 'menu1-1-1',
meta: {
title: '三级导航1-1-1'
},
component: resolve => require(['@/views/testPages/menu1/Menu1-1-1.vue'], resolve),
}
]
},
{
path: '/menu1-2',
name: 'menu1-2',
meta: {
title: '三级导航1-2'
},
component: resolve => require(['@/views/testPages/menu1/Menu1-2.vue'], resolve),
}
]
},
{
path: '/menu2',
component: Home,
name: 'menu2',
leaf:true,//有二级路由
meta: {
title: '测试页面',
icon: 'el-icon-location'
},
children: [
{
path: '/menu2-1',
name: 'menu2-1',
meta: {
title: 'TestAxios'
},
component: resolve => require(['@/views/testPages/TestAxios.vue'], resolve),
},
{
path: '/menu2-2',
name: 'menu2-2',
meta: {
title: 'TestMock'
},
component: resolve => require(['@/views/testPages/TestMock.vue'], resolve),
},
{
path: '/menu2-3',
name: 'menu2-3',
meta: {
title: '主题测试'
},
component: resolve => require(['@/views/testPages/TestUI.vue'], resolve),
}
]
},
{
path: '/menu3',
component: Home,
name: 'menu3',
redirect:'/menu3-1', //当路由未匹配时重定向,可作初始化显示页面设置
meta: {
title: '二级导航menu3',
icon: 'el-icon-document'
},
children: [
{
path: '/menu3-1',
name: 'menu3-1',
meta: {
title: 'menu3-1'
},
component: resolve => require(['@/views/uiMenu/Ui.vue'], resolve),
}
]
}
]
})
router.beforeEach((to, from, next) => {
//登录界面登录成功之后,会把用户信息保存在会话
//存在时间位会话生命周期,页面关闭即失效。
let user = sessionStorage.getItem('user');
if(to.path == '/login') {
//如果是访问登录界面,如果用户会话信息存在,代表已经登录过,跳转到主页
if(user) {
next({ path: '/'})
} else {
next()
}
} else {
//如果访问非登录页面,且用户会话信息不存在,代表为登录,则跳转到登录页面
if(!user) {
next({ path: '/login' })
} else{
next()
}
}
})
export default router
修改sideBar.vue中的导航栏代码
<el-menu
router
default-active="$route.path"
class="el-menu-vertical-demo"
@open="isOpen"
@close="isClose"
:collapse="isCollapse">
<template v-for="(item,index) in routes" v-if="!item.isHidden" >
<!-- 一级菜单 -->
<el-menu-item v-if="!item.leaf && !item.leafThree && item.children.length>0" :index="item.path">
<div class="nav-first">
<i :class="item.meta.icon"></i>
<span slot="title">{{item.meta.title}}</span>
</div>
</el-menu-item>
<!-- 二级菜单 -->
<el-submenu :index="index+''" v-if="item.leaf">
<template slot="title">
<div class="nav-first">
<i :class="item.meta.icon"></i>
<span>{{item.meta.title}}</span>
</div>
</template>
<el-menu-item v-for="child in item.children" :index="child.path" v-if="!child.isHidden" :key="child.path">
<span slot="title"><span class="dot"></span>{{child.meta.title}}</span>
</el-menu-item>
</el-submenu>
<!-- 三级菜单 -->
<el-submenu :index="index+''" v-if="!item.leaf && item.leafThree">
<template slot="title">
<div class="nav-first">
<i :class="item.meta.icon"></i>
<span>{{item.meta.title}}</span>
</div>
</template>
<el-submenu v-for="child in item.children" :index="child.path" :key="child.path" v-if="child.leaf">
<span slot="title"><span class="dot"></span>{{child.meta.title}}</span>
<el-menu-item v-for="childThree in child.children" :index="childThree.path" :key="childThree.path">
<span>{{childThree.meta.title}}</span>
</el-menu-item>
</el-submenu>
<el-menu-item v-for="child in item.children" :index="child.path" v-if="!child.isHidden && !child.leaf" :key="child.path">
<span slot="title"><span class="dot"></span>{{child.meta.title}}</span>
</el-menu-item>
</el-submenu>
</template>
</el-menu>
并添加如下代码:
刷新页面,发现修改成功!并且访问正常。
但是
看着面包屑中英文感觉很不爽,想改成和导航栏一样的文字。so,接着改:
打开面包屑组件并修改“ item.name ” ,将其改为 “ item.meta.title ”:
查看页面,发现修改成功!
但是
面包屑不包含首页?面包屑不能点击跳转页面?接着改吧!
修改面包屑代码:
<template>
<el-breadcrumb separator="/" style="margin-bottom: 24px">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item v-for="item in levelList" :key="item.path">
<a @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
</el-breadcrumb-item>
</el-breadcrumb>
</template>
export default {
data() {
return {
levelList:null
}
},
created() {
this.getBreadList()
},
watch:{
$route(){
this.getBreadList()
}
},
methods:{
getBreadList(){
let matched=this.$route.matched.filter(item=>item.name)//$route.matched 将会是一个包含从上到下的所有对象 (副本)。
// const first=matched[0]
// if(first && first.name !=='home'){//$route.name当前路由名称;$route.redirectedFrom重定向来源的路由的名字
// matched=[{ path: '/', meta: { title: '首页' }}].concat(matched)
// }
this.levelList=matched
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
},
pathCompile(path) {
const { params } = this.$route
var toPath = pathToRegexp.compile(path)//url 的正则表达式,快速填充 url 字符串的参数值。
return toPath(params)
},
},
}
【注】在pathCompile()方法中用到了pathToRegexp,所以需要安装path-to-regexp
进入项目存放的目录,在地址栏中输入cmd,打开cmd窗口,并输入
cnpm install path-to-regexp --save
然后在面包屑组件中引入
import * as pathToRegexp from 'path-to-regexp'
查看页面,发现可以点击了!
参考文献
vue&Element-ui实现的导航菜单:
vue+element-ui面包屑导航: