后台管理系统有这个需求。不同角色账号进来后,只能看到自己权限内的页面。这种权限限制需要前后端协同工作。结束。当用户有未授权访问时,后端需要返回错误信息。也可以让后端直接返回路由列表。只是这样不够灵活,每增加一个新页面都需要配置路由和权限,不符合前后端分离的原则。前期准备和产品经理和后端同学讨论,拿到角色类型列表,约定角色可以进入哪些页面。核心逻辑创建路由列表和菜单列表(左/上)。两者的格式类似,菜单只是多了一些图标。该字段将路由列表分为两部分:登录后可查看的(权限列表)和无需登录即可查看的(访客列表)。为权限列表中的每条路由添加角色数组字段,只有其中的角色才能访问这条路由。在路由配置文件中跳转到新页面前添加导航钩子,比较用户登录后返回的角色信息,并与权限列表进行比较,计算出自己可以访问的路由列表,保存在vuex中。router.addRoutes()方法将两个列表拼接在一起(Vue框架)并比较计算可见菜单列表,赋值保存到vuex中显示页面访问者列表代码:routes.jsimportlayoutHeaderAsidefrom'@/layout/header-aside'//因为懒加载页面太多会导致webpack热更新太慢,所以开发环境不使用懒加载,只有生产环境使用懒加载const_import=process.env.NODE_ENV==='发展'?file=>require('@/views/'+file).default:file=>()=>import('@/views/'+file)/***在主框架中显示*/constframeIn=[{path:'/',redirect:{name:'index'},component:layoutHeaderAside,children:[//首页{path:'/index',name:'index',component:_import('system/index')},//刷新页面必须保留{path:'/refresh',name:'refresh',hidden:true,component:_import('system/function/refresh')},//页面重定向必须保留{path:'/redirect/:route*',name:'redirect',hidden:true,component:_import('system/function/redirect')}]}]/***在主框架外显示*/constframeOut=[//login{path:'/login',name:'login',component:_import('system/login')}]/***错误页面*/consterrorPage=[{path:'*',name:'404',component:_import('system/error/404')}]//导出需要显示菜单的exportconstframeInRoutes=frameIn//重组导出后导出默认[...frameIn,...frameOut,...errorPage]权限列表:auth-routes.jsimportlayoutHeaderAsidefrom'@/layout/header-aside'const_import=process.env.NODE_ENV==='发展'?file=>require('@/views/'+file).default:file=>()=>import('@/views/'+file)constlists=[{path:'/',redirect:{名称:'index'},component:layoutHeaderAside,children:[//组织管理{path:'/organization-management',name:'organizationManagement',meta:{title:'组织管理',auth:true,roles:[0]//授权进入的角色},component:_import('pages/organization-management')},//人员管理{path:'/personnel-management',name:'personnelManagement',meta:{title:'PersonnelManagement',auth:true,roles:[0]},component:_import('pages/personnel-management')},//RoleManagement{path:'/roles-management',name:'rolesManagement',meta:{title:'RoleManagement',auth:true,roles:[0]},component:_import('pages/roles-management')},//角色授权{path:'/roles-impower',name:'rolesImpower',meta:{title:'Authorization',auth:true,roles:[0]},component:_import('pages/roles-management/components/roles-impower')},{path:'权限管理ment',name:'authority-management',meta:{title:'权限管理',auth:true,roles:[0]},component:_import('pages/authority-management')},{path:'agency-register-approval',name:'agency-register-approval',meta:{title:'机构注册审核',auth:true,roles:[1]},component:_import('pages/agency-register-approval')},{path:'program-info-management',name:'program-info-management',meta:{title:'项目信息管理',auth:true,roles:[1]},component:_import('pages/program-info-management')},{path:'product-category-management',name:'product-category-management',meta:{title:'产品类目管理',auth:true,角色:[1]},组件:_import('pages/product-category-management')}]}]ex端口默认列表menulist:aside.js//menusidebarexportdefault[{path:'/index',title:'Home',icon:'home',roles:[0,1]},{path:'/组织管理',标题:'组织管理',图标:'机构',角色:[0]},{标题:'用户管理',图标:'用户',角色:[0],孩子:[{路径:'/personnel-management',title:'人事管理',icon:'',roles:[0]},{path:'/roles-management',title:'角色管理',icon:'',roles:[0]}]},{title:'权限管理',icon:'shield',roles:[0],children:[{path:'/authority-management',title:'权限管理',icon:'',roles:[0]}]},{title:'RegistrationApproval',icon:'legal',roles:[1],children:[{path:'/agency-register-approval',title:'机构注册批准',icon:'',roles:[1]}]},{title:'ProjectManagement',icon:'window-restore',roles:[1],children:[{path:'/program-info-management',title:'项目信息管理',icon:'',roles:[1]}]},{title:'分类管理',icon:'数据库',roles:[1],children:[{path:'/product-category-management',title:'产品分类管理',icon:'',roles:[1]}]}]路由配置文件importroutesfrom'./routes'//侧边栏菜单数据导入menuAsidefrom'@/menu/aside'/***路由拦截*权限校验*/router.beforeEach(async(to,from,next)=>{//进度条NProgress.start()//关闭搜索面板store.commit('d2admin/search/set',false)asyncfunctiongetRouteAndMenu(){awaitstore.dispatch('d2admin/user/GenerateRoutes')//获取可用的Accessroute,保存在vuex中router.addRoutes(store.state.d2admin.user.accessedRouters)//与原来的固定路由合并//获取侧边栏菜单,保存在vuex中awaitstore.dispatch('d2admin/menu/GenerateMenu',{role:store.state.d2admin.user.info.role,menuAside})//设置侧边栏菜单store.commit('d2admin/menu/asideSet',store.state.d2admin.菜单。一边)下一个()}if(from.name===null&&to.name==='404'){//避免刷新404页面getRouteAndMenu()}//在需要验证的路由中,进一步判断角色路由if(to.matched.some(r=>r.meta.auth)){//cookie中是否有token作为验证是否登录的条件consttoken=util.cookies.get('token')if(token&&token!=='undefined'){constaccessedRouters=store.state.d2admin.user.accessedRoutersif(accessedRouters.length<=0){//如果本地没有保存可访问路由,需要计算getRouteAndMenu()}else{next()}}else{//未登录时跳转到登录界面//携带登录成功后需要跳转的页面全路径next({name:'login',query:{redirect:to.fullPath}})NProgress.done()}}else{//不需要直接通过next()进行身份验证}})logininstoremodule:account.jsactions:{/***@描述登录*@param{Object}context*@param{Object}payloadusername{String}useraccount*@param{Object}payloadpassword{String}password*@param{Object}payloadroute{Object}登录成功后指向的路由对象vue-router支持的任意格式*///登录后保存用户角色信息login({dispatch},{username='',password=''}={}){returnnewPromise((resolve,reject)=>{//开始请求登录接口AccountLogin({username,password}).then(asyncres=>{//设置cookie时,必须保存uuid和token两个cookie//整个系统依赖这两个数据进行校验和存储//uuid是用户的唯一标识,在用户注册时确定,不可更改,不可重复。//token代表用户当前登录状态.建议在网络请求中携带token//如果需要的话token需要定时更新,默认保存一天util.cookies.set('uuid',res.userId)util.cookies.set('token',res.token)//设置vuex用户信息,保存用户名和用户角色awaitdispatch('d2admin/user/set',{name:res.userName,role:res.userType},{root:true})//用户登录后,从本地存储加载一系列设置awaitdispatch('load')//endresolve()}).catch(err=>{console.log('err:',err)reject(err)})})}}userinstore模块:user.js//originalmenulistimportauthRoutesfrom'@/router/auth-routes'//判断当前路由字段的roles数组是否包含用户角色functionhasPermission(roles,route){if(route.meta&&route.meta.roles){returnroute.meta.roles.some(role=>role===roles)}else{returntrue}}exportdefault{namespaced:true,state:{//用户信息//角色,0为管理员,1为非管理员info:{},accessedRouters:[]},mutations:{//保存当前用户可访问的路由setRouters(state,routers){state.accessedRouters=routers}},actions:{//根据权限生成可访问的路由//保存并返回GenerateRoutes({state,commit}){returnnewPromise(resolve=>{const{role}=state.info;constaccessedRouters=authRoutes.filter(v=>{if(hasPermission(role,v)){if(v.children&&v.children.length>0){v.children=v.children.filter(child=>{if(hasPermission(role,child)){returnchild}returnfalse;});返回v}其他{返回v}}返回假;});commit('setRouters',accessedRouters);解决(访问路由器);})}}}store:menu.js中的菜单模块与路由模块相同。注销时,清除内存中的数据。用户模块:注销时store中的account.js,清除当前内存中保存的计算路由数据和菜单数据asyncfunctionlogout(){//删除cookiesutil.cookies.remove('token')util.cookies.remove('uuid')//清除vuex用户信息awaitdispatch('d2admin/user/set',{},{root:true})//清除vuex的路由和菜单信息commit('d2admin/user/setRouters',[],{root:true})commit('d2admin/menu/setMenu',[],{root:true})//跳转到路由router.push({name:'login'})}注意这里只保存路由和菜单在内存中,所以页面刷新后,如果当前路径是权限列表中的路径,它将导致404页面,因为路由列表已重置为访问者列表。这也是为什么我在路由配置文件里也做了404判断,比较麻烦。您可以将路线和菜单保存在本地,刷新后在本地恢复。角色使用admin、user等有意义的名字,比数字更直观。注销时,清除当前计算的数据,避免被下一个用户看到
