无需 vuex、本地存储实现动态显示三级路由。
目录
一、需求描述:
二、代码
2.1 路由配置
1. 我的一级菜单和二级菜单的路由配置的:
2. 三级菜单的路由配置:
3. 上面有几个变量和要注意的细节:
2.2 封装导航栏
2.3 封装面包屑
2.4 页面布局
一、需求描述:
之前写过一个 element实现动态路由+面包屑,这个需求是嵌套的页面层级太多了,需要在页面中点击某个按钮,跳转到下一个页面(即该页面的子页面),在下一个页面的面包屑中显示所有前一级的路由。面包屑显示关系为:组先 - 父页面 - 当前页面。
现在这个需求是:已确定路由中包含3级菜单,如果有3级菜单,在点击二级菜单的时候,在面包屑中将该二级菜单下所有的3级菜单都显示出来;如果是一级菜单没有子菜单,或者只有二级菜单,那么面包屑中就显示一级菜单名称或二级菜单名称。如下图:
( 上面图2中,“一级标题3”下应该是“二级标题3-1”,不小心写错了)
二、代码
由于项目周期比较紧,代码一切从简。
2.1 路由配置
这里分布例举一下我的一级菜单、二级菜单和三级菜单的路由配置,具体 path、name、meta.title、meta.icon、component、redirect 等值需要自己去设置。
1. 我的一级菜单和二级菜单的路由配置的:
{path: '/menu4',name: 'userRoleManage',meta: {title: "一级标题4", leaf: true, hidden: false,icon:"my-icon-labelManage"},component: () => import('@/components/myLayout'),redirect: "/menu4/menu41",children:[{path: 'menu41',name: 'menu41',meta: {title: "一级标题4", hidden: false},component: () => import('@/views/menu4/menu41.vue')}]
},
{path: '/outWh',name: 'outWh',meta: {title: "一级标题3", leaf: false, hidden: false, icon: 'my-icon-packProduct'},component: () => import('@/components/myLayout'),redirect: "/outWh/list",children:[{path: 'list',name: 'list',meta: {title: "二级标题3-1", hidden: false},component: () => import('@/views/outWh/list.vue')}, {path: 'codeInfo',name: 'codeInfo',meta: {title: "二级标题3-2", hidden: false},component: () => import('@/views/outWh/codeInfo.vue')}]
}
2. 三级菜单的路由配置:
{path: '/basicData',name: 'basicData',meta: {title: "一级标题1", leaf: false, hidden: false,icon:"my-icon-labelManage"},component: () => import('@/components/myLayout'), //组件-封装好的头部、导航等页面整体框架redirect: "/basicData/goodsInfo",children:[{path: 'goodsInfo',name: 'goodsInfo',meta: {title: "二级标题1-1", hidden: false},component: () => import('@/views/basicData/goodsInfo.vue')},{path: 'warehouseInfo',name: 'warehouseInfo',meta: {title: "二级标题1-2", hidden: false, threeMenu: true},component: () => import('@/components/myLayout/breadcrumb'), //引入面包屑组件redirect: "/basicData/warehouseInfo/warehouse",children: [{path: 'warehouse',name: 'warehouse',meta: {title: "三级标题1-2-1", hidden: false},component: () => import('@/views/basicData/warehouse.vue')},{path: 'goodsShelf',name: 'goodsShelf',meta: {title: "三级标题1-2-2", hidden: false},component: () => import('@/views/basicData/goodsShelf.vue')},{path: 'goodsPlace',name: 'goodsPlace',meta: {title: "三级标题1-2-4", hidden: false},component: () => import('@/views/basicData/goodsPlace.vue')}]},{path: 'supplier',name: 'supplier',meta: {title: "二级标题1-3", hidden: false},component: () => import('@/views/basicData/supplier.vue')},{path: 'clientInfo',name: 'clientInfo',meta: {title: "二级标题1-4", hidden: false},component: () => import('@/views/basicData/clientInfo.vue')}]
}
3. 上面有几个变量和要注意的细节:
- leaf 表示一级菜单下是否有子菜单【在一级菜单的 meta 中书写该变量】。true:只有一级菜单,没有子菜单;false:有子菜单(有二级菜单、三级菜单都是 false);
- hidden :该路由配置的信息是否要在导航栏中隐藏起来。true:在导航栏上隐藏起来(即不显示),false:不隐藏(即显示)。
- threeMenu:是否有三级菜单【在二级菜单的 meta 中书写该变量】。true:该二级菜单下有 3 级菜单,false:该二级菜单下没有 3 级菜单。
- 当有 3 级菜单时,就把该 3 级菜单的二级菜单当做 一级菜单 来设置,在二级菜单中添加 component 和 redirect 方法。component 指向组件,redirect 重定向到页面。
- 有 3 级菜单时,就给该 3 级菜单的二级菜单设置:threeMenu:true,没有的可以不设置。要是想要代码统一,就在所有二级菜单中的 meta 中设置一下。
具体一些的,我在 手把手教你用 element 实现导航栏 这篇文章中有介绍。
2.2 封装导航栏
导航栏的封装我直接拿的 手把手教你用 element 实现导航栏 中的代码。
<template><el-menu id="myAside"router:default-active="$route.matched[1].path"active-text-color="#00BF9F"><!-- 侧导航栏 --> <template v-for="(item,itemId) in allRouter"><!-- 节点不隐藏 --><template v-if="!item.meta.hidden"><!-- 是否只有一个节点 leaf:true-是只有一个节点 --><template v-if="item.meta.leaf"><el-menu-item :index="item.redirect" :key="itemId" class="oneLeaf"><i :class="item.meta.icon"></i><span slot="title" class="oneLeftTitle">{{item.meta.title}}</span></el-menu-item></template><!-- 多个节点 --><el-submenu v-else :index="item.redirect" :key="itemId"><template slot="title"><i :class="item.meta.icon"></i><span>{{item.meta.title}}</span></template><el-menu-item-group><template v-for="(child,childId) in item.children"> <template> <el-menu-item :index="item.path +'/'+ child.path" :key="childId" v-if="!child.meta.hidden">{{child.meta.title}}</el-menu-item> </template><!-- 判断是否为3级菜单结束--></template></el-menu-item-group></el-submenu></template></template></el-menu>
</template><script>
// import { mapGetters } from "vuex";export default {data(){return {allRouter: this.$router.options.routes}}
}
</script><style scoped lang="scss">
#myAside{border-right:0;.my-icon-labelManage{display: inline-block;width:20px;height:20px;}.my-icon-labelManage{background: url("~@/assets/menu/labelInit.png") center no-repeat;}.my-icon-labelManage:before{content: "\8d3a";font-size: 22px;visibility: hidden;}.el-submenu.is-active, .el-menu-item.is-active{.my-icon-labelManage{background: url("~@/assets/menu/labelInit_active.png") center no-repeat;} }
}
</style>
不过这里有个细节需要注意一下:
导航栏上激活的选项(即被选中的一级菜单/二级菜单)与 :default-active 属性值有关。当我们点击三级菜单时,导航栏上被激活的是该三级菜单的二级菜单,所以 :default-active 的属性值应该是 二级菜单的 path 值,而不是 当前三级菜单的 $route.path 值。
【这里特地提一下,因为这一步我被卡了一下,三级菜单的面包屑和二级菜单的导航把我绕进去了......想一想怎么描述这个过程,还是有点晕】
另外,:default-active="$route.matched[1].path" 这个属性值还挺神奇的,要是只有一级和二级菜单也可以用这个属性值代替,以后我写导航的激活就用这个值了嘿嘿~。(有需要的可以自己打印看看)
2.3 封装面包屑
原本我打算根据路由配置中二级菜单的 meta.threeMenu 的属性值,来判断被点击的二级菜单中有没有包含三级菜单,但是因为这是写在二级菜单里面,需要循环一下。后面发现 this.$route.matched 有个很方便的特点:
- 路由配置中如果一级路由下没有 children: [],那么 this.$route.matched.length = 1,
- 路由配置中如果一级路由下有二级菜单,写了一个 children,那么 this.$route.matched.length = 2,
- 路由配置中如果二级路由下有三级菜单,又写了一个 children,那么 this.$route.matched.length = 3。
总的来说:路由配置中一级菜单下总共包含了几个children,那么 this.$route.matched 的长度 = children数量 + 1。 利用这个特点,就能很方便快速的判断出点击的一级/二级菜单下是否有三级菜单。
思路:
1. 判断点击的路由是否包含3级菜单。
由于我配置路由时,没有二级菜单的一级菜单 与 有二级菜单的一级菜单 是根据 leaf 值来判断的,两者都写了 children:[],所以 this.$route.matched.length 都= 2。但是不管是前者还是后者,被点击时,面包屑中显示的都是被点击的那个路由信息(即1级菜单面包屑显示1级菜单名称,2级菜单面包屑显示2级菜单名称)。
2. 如果没有 3级菜单( 即 this.$route.matched.length = 2),就把当前点击的路由信息保存下来。
3. 如果有 3级菜单,那么点击二级菜单时,就要把该二级菜单下的所有 3级菜单的路由信息保存下来。
4. 将保存下来的路由信息在面包屑中显示出来,这里用到 router-link,3级菜单被选中激活时,router-link 会自动生成一个 .router-link-active 的 class 名。
<template><div id="breadcrumb"><!-- 面包屑 --><div class="bg pd20 clear mb20"><router-link v-for="three in threeRouters" :key="three.name" :to="three.path">{{three.meta.title}}</router-link></div><router-view v-if="threeMenuOff"></router-view></div>
</template><script>
export default {data(){return {allRouter: this.$router.options.routes, //获取所有菜单threeRouters: [],threeMenuOff: false}},watch: {"$route": {handler(val,oldval){// console.log("面包屑监听",val, oldval)this.getChildRoute(val)},deep: true,immediate: true}},methods:{getChildRoute(currRoute){console.log("breadcrumb",currRoute,this.allRouter)if(currRoute.matched.length == 2){this.threeRouters = [currRoute]; //直接将当前的路由信息传给组件this.threeMenuOff = false;}else if(currRoute.matched.length == 3){ //说明有3级路由this.allRouter.map(item=>{if(currRoute.matched[0].path == item.path){//二级路由。存所有三级路由的数据item.children.map(child =>{if(currRoute.matched[1].name == child.name){this.threeRouters = JSON.parse(JSON.stringify(child.children));}});}})this.threeMenuOff = true;} //判断结束currRoute.matched.length=3console.log("3ji菜单",this.threeRouters)} ,},
}
</script><style lang="scss" scoped>
#breadcrumb{margin-bottom: 20px;a{float: left;margin-right: 20px;}a.router-link-active{border-bottom:2px solid #f88;}
}
</style>
这里需要注意:三级路由的页面,需要添加 <router-view></roter-view>,否则三级页面显示空白。原因是在配置时,二级路由的 component 指向的是面包屑组件路径而非三级页面所在路径,通过 redirect 重定向到三级页面。
2.4 页面布局
这个部分因为有个需要注意的地方,所以也贴一下代码。
<template><el-container><el-header class="bg" style="height: 80px;"> <v-head></v-head> </el-header><el-container><el-aside width="260px" class="bg"> <my-aside></my-aside> </el-aside><el-main><breadcrumb v-if="!threeMenuOff"></breadcrumb><router-view></router-view></el-main></el-container></el-container>
</template><script>
import vHead from "./vHead"
import myAside from "./myAside"
import breadcrumb from "./breadcrumb"
export default {components: { vHead, myAside, breadcrumb },data(){return {threeMenuOff: false //3级菜单的不需要面包屑组件,因为在路由中components已经配置了}},watch: {"$route": {handler(val,oldval){console.log("myLayout",val)if(val.matched.length == 3 || val.name == "home"){this.threeMenuOff = true; //不需要面包屑组件,将threeMenuOff = true} else {this.threeMenuOff = false;}},deep: true,immediate: true}},
}
</script>
这里在原来的布局上添加了 <breadcrumb></bredcrumb> 组件,前面在路由配置中,配置了3级菜单的面包屑,这里添加 breadcrumb 组件是给1级菜单和2级菜单添加面包屑的。同时,通常 首页home 是不需要面包屑的,这里也可以做判断,控制面包屑的显示和隐藏。