背景
在管理后台系统中,vue-router 往往要承担更多的事情。
因为在管理后台,一个非常重要也非常常见的功能,就是用户权限的区分。一般用户,管理用户,超级管理员,审计用户等等。
针对不同的用户,既要做接口的检验,也要对页面的权限进行划分。
比如:
超级管理员拥有所有的权限,能查看所有的页面,能进行所有的操作
管理用户,则不能添加,删除,变更系统里的用户
审计用户,只能查看需要审计的内容,如系统操作日志等
以上种种,其实都是跟页面的展示,路由的访问,接口的校验强相关的。当前用户如果是审计用户,那么只能展示某些页面,如果是管理用户,那要隐藏某些页面。
另外,管理项目都有一套侧边栏作为不同功能页面的入口。
假设我们的路由和侧边栏是分开的逻辑,那么就有以下的问题:
每次更新一次路由,就要更新一次侧边栏,
路由中做了权限,侧边栏还要单独加一个权限管理的逻辑
这样会显得我们前端开发很呆板。
所以总结来说,vue-router 要实现这样的功能
挂载组件到页面
根据不同的用户角色,如:超级管理员,审计用户,普通用户等来做路由权限管理
根据路由生成复杂的侧边栏
那么,如何使用一套定义好的 router 来做到以上三个需求呢?
分析
首先,我们来细致的分析一下这三个需求有哪些具体需要
挂载组件到页面中
想必不用多说了,vue-router 本来就是做这个的
区分不同用户角色
用户角色一般是保存在数据库中,用户登录之后由后台的接口返回的,比如这个字段是user_role
,那么这里怎么实现动态的路由区分呢?
首先,我们可以把这个字段存储在 vuex 中,这样可以做到全局访问,全局自动更新
其次,在 vue-router 的每一个路由对象中插入一个字段,比如叫permission
,来记录当前这个路由能够被谁访问,如permission: [0,1,2]
根据user_role
和permission
来匹配筛选所有的路由,筛选后的结果,就是当前用户能够访问的路由
使用 vue-router 的方法将得到的路由添加到项目的路由对象里,地址
生成复杂的侧边栏
侧边栏是管理后台都有的功能,这里参考 vue-element-admin 的实现。 本质上 router 是一个javascript 的数组对象,遍历这个对象,利用 elementui 的 el-menu 组件来生成侧边栏就可以了。
如果 router 变化了 那么侧边栏也会随之变化。
实现
我们先定义一个整体的 router 对象,这个对象是整个项目里所有的页面路由,也是超级管理员默认的访问权限,这个对象的每一个route 对象,都可以含有如下的属性或者方法:
hidden: 是否显示在侧边栏
name: 侧边栏对应的文字内容,当name不存在,则children中的路由为一级路由,否则为二级路由,侧边栏最多显示到二级路由
iconName:侧边栏的 icon 图标
permission:可以访问辞路由的用户列表
//routerData.jsconstrouter1=[{path:'/',redirect:'/homepage',hidden:true,meta:{permission:[1,2,3]}},{path:'/homepage',meta:{permission:[1,2,3]},redirect:'/homepage/dashboard',component:layout,children:[{path:'dashboard',name:'安全状态监控',meta:{iconName:'icon-erji-loudongsaomiaoguanli',permission:[1,2,3]},component:()=>import('../views/dashboard/index.vue'),},]},{path:'/assetManagement',name:'资产管理',meta:{iconName:'icon-erji-loudongsaomiaoguanli',permission:[1,2,3]},redirect:'/assetManagement/assetList',component:layout,children:[{path:'assetList',name:'资产列表',meta:{permission:[1,2,3]},component:()=>import('../views/assetManagement/assetList.vue'),},{path:'assetDetail',name:'资产详情',meta:{permission:[1,2,3]},component:()=>import('../views/assetManagement/assetList.vue'),},]},{path:'*',redirect:'/',hidden:true,meta:{permission:[1,2,3]},}]exportcommonRoute=[{path:'/login',component:()=>import('@/views/login.vue'),hidden:true,meta:{permission:[1,2,3]},}]exportdefaultrouter1
然后我们在 index.js 中实现 router 的挂载
//router/index.jsimportVuefrom'vue'importVueRouterfrom'vue-router'import{commonRoute}from'./routerData'importlayoutfrom'@/layout'Vue.use(VueRouter)exportconstpermissionRouter=function(router,roleType){returnrouter.filter(item=>{if(item.children)item.children=permissionRouter(item.children)returnitem.meta.permission.includes(roleType)})}exportdefaultnewVueRouter({commonRoute})
当用户登录后,我们先获取用户的角色,然后放到 vuex 中存储后,就可以根据用户角色,动态的计算出权限路由了
importstorefrom'@/store'importrouter1from'@/router/routerData'importrouter,{permissionRouter}from'@/router/index'constroleType=store.getters.user_roleconstpermissionRouterList=permissionRouter(router1,roleType)router.addRoute(permissionRouterList)
到这里整个 router 就写好了,router 接下来就可以准备动态的生成侧边栏,侧边栏逻辑复杂一点,所以我也是拆成了三个文件
这是文件的目录:
sidebar├──index.vue├──item.vue├──sidebarItem.vue
然后来实现动态侧边栏的逻辑
//index.vue<template><divclass="sidebar-container"><el-menu:unique-opened="true":collapse-transition="false"background-color="rgba(0,0,0,0)":default-active="activeMenu"><sidebar-itemv-for="(item,index)inpermissionRouterList":key="index":item="item"/></el-menu></div></template><script>importstorefrom'@/store'importrouter1from'@/router/routerData'import{permissionRouter}from'@/router/index'importSidebarItemfrom'./sidebarItem.vue'exportdefault{components:{SidebarItem},computed:{activeMenu(){returnthis.$route.path},permissionRouterList(){constroleType=store.getters.user_rolereturnpermissionRouter(router1,roleType)}}}</script>
//item.vue<template><el-menu-item:index="compilePath(child.path)"@click="clickEvent(child)"v-if="!child.hidden"><iv-if="child.meta"class="iconfont":class="child.meta.iconName"></i><spanslot="title">{{child.name}}</span></el-menu-item></template><script>importpathfrom'path'exportdefault{props:{child:{type:Object},basePath:{type:String}},methods:{compilePath(childpath){returnpath.resolve(this.basePath,childpath)},clickEvent(child){constpath=this.compilePath(child.path)this.$router.push(path)}}}</script>
//sidebarItem.vue<template><el-submenuv-if="(item.children&&hasName(item))&&!item.hidden":index="item.path"><templateslot="title"><iv-if="item.meta"class="iconfont":class="item.meta.iconName"></i><spanslot="title">{{item.name}}</span></template><menu-itemv-for="childinitem.children":key="compilePath(item.path,child.path)":child="child":basePath="item.path"/></el-submenu><menu-item:child="item.children[0]"v-else-if="item.children&&!item.hidden":basePath="item.path"/></template><script>importpathfrom'path'importMenuItemfrom'./item.vue'exportdefault{props:{item:{type:Object}},components:{MenuItem},methods:{compilePath(itempath,childpath){returnpath.resolve(itempath,childpath)},hasName(item){returnitem.name?true:false}}}</script>
这样,我们的整个需求就完成了,然后将 sidebar 组件正常引入,加入自己的样式,就可以了
最后的最后,代码未经测试,有任何问题请私信或评论,谢谢。
作者:晴天同学