多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## vue鉴权的实现 ### 路由的动态注入 #### 递归遍历路由 **静态路由** ``` // 静态路由 export const StaticRouterMap = [ { path: '/login', component: login, meta: { title: '管理员登录' }, hidden: true }, { path: '/user', component: userLogin, redirect: '/user/userlogin', name: 'user', hidden: true, children: [ { path: 'userLogin', component: () => import('@/views/userLogin/components/login'), meta: { title: '商户登录' } }, { path: 'userRegistry', component: () => import('@/views/userLogin/components/registry'), meta: { title: '商户注册' } } ] }, { path: '/', component: Layout, redirect: '/dashboard', name: 'dashboard', children: [ { path: 'dashboard', component: () => import('@/views/dashboard/index'), meta: { title: '根目录', icon: 'dashboard', affix: true } } ] }, { path: '/404', component: () => import('@/views/404'), hidden: true } ] export default new Router({ mode: 'history', scrollBehavior: () => ({ y: 0 }), routes: StaticRouterMap }) ``` #### 权限数据 ``` [{ "id": 1, "name": "Nested", "code": null, "description": null, "url": "/nested", "generatemenu": 0, "sort": 0, "parentId": null, "permName": null, "redirect": "/nested/menu1", "title": "Nested", "icon": "nested", "children": [{ "id": 2, "name": "Menu1", "code": null, "description": null, "url": "menu1", "generatemenu": 0, "sort": 0, "parentId": 1, "permName": null, "redirect": "", "title": "Menu1", "icon": "menu1", "children": [{ "id": 4, "name": "Menu1-1", "code": null, "description": null, "url": "menu1-1", "generatemenu": 0, "sort": 0, "parentId": 2, "permName": null, "redirect": "", "title": "Menu1-1", "icon": "", "children": null }, { "id": 5, "name": "Menu1-2", "code": null, "description": null, "url": "menu1-2", "generatemenu": 0, "sort": 0, "parentId": 2, "permName": null, "redirect": "", "title": "Menu1-2", "icon": "", "children": null }] }, { "id": 3, "name": "Menu2", "code": null, "description": null, "url": "menu2", "generatemenu": 0, "sort": 0, "parentId": 1, "permName": null, "redirect": "", "title": "Menu2", "icon": "menu2", "children": null }] }] ``` #### 组件 异步加载 ``` export default file => { return map[file] || null } const map = { Nested: () => import('@/views/layout/Layout'), Menu1: () => import('@/views/nested/menu1/index'), 'Menu1-1': () => import('@/views/nested/menu1/menu1-1'), 'Menu1-2': () => import('@/views/nested/menu1/menu1-2') } ``` #### 递归遍历路由 ``` import _import from '../router/_import' // 获取组件的方法 /** * 生成路由 * @param {Array} routerlist 格式化路由 * @returns */ export function addRouter(routerlist) { const router = [] routerlist.forEach(e => { let e_new = { path: e.url, name: e.name, component: _import(e.name) } if (e.children) { e_new = Object.assign({}, e_new, { children: addRouter(e.children) }) } if (e.redirect) { e_new = Object.assign({}, e_new, { redirect: e.redirect }) } if (e.generatemenu == 0) { e_new = Object.assign({}, e_new, { hidden: true }) } if (e.icon !== '' && e.title !== '') { e_new = Object.assign({}, e_new, { meta: { title: e.title, icon: e.icon } }) } else if (e.title !== '' && e.icon === '') { e_new = Object.assign({}, e_new, { meta: { title: e.title }}) } router.push(e_new) }) return router } ``` #### 处理后的路由 ``` [{ "name": "Nested", "redirect": "/nested/menu1", "children": [{ "name": "Menu1", "children": [{ "name": "Menu1-1", "children": null, "path": "menu1-1", "meta": { "title": "Menu1-1" } }, { "name": "Menu1-2", "children": null, "path": "menu1-2", "meta": { "title": "Menu1-2" } }], "path": "menu1", "meta": { "title": "Menu1", "icon": "menu1" } }, { "name": "Menu2", "children": null, "path": "menu2", "component": null, "meta": { "title": "Menu2", "icon": "menu2" } }], "path": "/nested", "meta": { "title": "Nested", "icon": "nested" } }] ``` #### ### (核心)合并路由 ``` import router from './router' import store from './store' import { getToken, removeToken } from './utils/auth' import NProgress from 'nprogress' // Progress 进度条 import 'nprogress/nprogress.css' // Progress 进度条样式 import { Message } from 'element-ui' import { getRouter } from './api/login' import { addRouter } from './utils/addRouter' const whiteList = ['/login'] var data = false // 本次demo用变量凑合一下,项目里面应该放到vuex内 router.beforeEach((to, from, next) => { NProgress.start() if (getToken()) { // 判断cookice是否存在 不存在即为未登录 if (to.path !== '/login') { if (data) { // 获取了动态路由 data一定true,就无需再次请求 直接放行 next() } else { // data为false,一定没有获取动态路由,就跳转到获取动态路由的方法 gotoRouter(to, next) } } else { Message({ message: '您已经登录', type: 'info' }) next('/') } } else { data = false if (whiteList.indexOf(to.path) !== -1) { // 免登陆白名单 直接进入 next() } else { if (to.path !== '/login') { // 重定向到登录页面 不能这么写 因为假如之前的角色是 管理员页面 后又登陆了非管理员 重定向的页面就可能不存在,就会导致404 // next(`/login?redirect=${to.path}`) next('/login') } else { next() } } } }) router.afterEach(() => { NProgress.done() // 结束Progress }) function gotoRouter(to, next) { getRouter(store.getters.token) // 获取动态路由的方法 .then(res => { console.log('解析后端动态路由', res.data.data) const asyncRouter = addRouter(res.data.data) // 进行递归解析 // 一定不能写在静态路由里面,否则会出现,访问动态路由404的情况.所以在这列添加 asyncRouter.push({ path: '*', redirect: '/404', hidden: true }) return asyncRouter }) .then(asyncRouter => { router.addRoutes(asyncRouter) // vue-router提供的addRouter方法进行路由拼接 data = true // 记录路由获取状态 store.dispatch('setRouterList', asyncRouter) // 存储到vuex store.dispatch('GetInfo') next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 }) .catch(e => { console.log(e) removeToken() }) } ``` #### **Vuex内部的逻辑** ``` import { StaticRouterMap } from '../../router/index' state: { //..... RouterList: [] // 动态路由 }, mutations: { set_router: (state, RouterList) => { state.RouterList = RouterList } }, action: { // 动态设置路由 此为设置设置途径 setRouterList({ commit }, routerList) { commit('set_router', StaticRouterMap.concat(routerList)) // 进行路由拼接并存储 }, } ``` #### 挂载路由 ``` computed: { // .... routes() { return this.$store.getters.routerList }, // .... } ```