refactor: config of async router and guards of router; 🌟

重构:异步路由配置和路由守卫配置;
This commit is contained in:
iczer 2020-08-28 12:25:28 +08:00
parent 75f53df1e4
commit 192d4f243d
9 changed files with 289 additions and 148 deletions

8
src/bootstrap.js vendored
View File

@ -1,5 +1,6 @@
import {loadRoutes, loginGuard, authorityGuard} from '@/utils/routerUtil'
import {loadRoutes, loadGuards} from '@/utils/routerUtil'
import {loadInterceptors} from '@/utils/request'
import guards from '@/router/guards'
import interceptors from '@/utils/axios-interceptors'
/**
@ -14,9 +15,8 @@ function bootstrap({router, store, i18n, message}) {
loadInterceptors(interceptors, {router, store, i18n, message})
// 加载路由
loadRoutes({router, store, i18n})
// 添加路由守卫
loginGuard(router)
authorityGuard(router, store)
// 加载路由守卫
loadGuards(guards, {router, store, i18n, message})
}
export default bootstrap

View File

@ -0,0 +1,154 @@
// 视图组件
const view = {
tabs: () => import('@/layouts/tabs'),
blank: () => import('@/layouts/BlankView'),
page: () => import('@/layouts/PageView')
}
// 路由组件注册
const routerMap = {
login: {
authority: '*',
path: '/login',
component: () => import('@/pages/login')
},
root: {
path: '/',
name: '首页',
redirect: '/login',
component: view.tabs
},
dashboard: {
name: 'Dashboard',
component: view.blank
},
workplace: {
name: '工作台',
component: () => import('@/pages/dashboard/workplace')
},
analysis: {
name: '分析页',
component: () => import('@/pages/dashboard/analysis')
},
form: {
name: '表单页',
icon: 'form',
component: view.page
},
basicForm: {
path: 'basic',
name: '基础表单',
component: () => import('@/pages/form/basic')
},
stepForm: {
path: 'step',
name: '分步表单',
component: () => import('@/pages/form/step')
},
advanceForm: {
path: 'advance',
name: '高级表单',
component: () => import('@/pages/form/advance')
},
list: {
name: '列表页',
icon: 'table',
component: view.page
},
queryList: {
path: 'query',
name: '查询表格',
component: () => import('@/pages/list/QueryList')
},
primaryList: {
path: 'primary',
name: '标准列表',
component: () => import('@/pages/list/StandardList')
},
cardList: {
path: 'card',
name: '卡片列表',
component: () => import('@/pages/list/CardList')
},
searchList: {
path: 'search',
name: '搜索列表',
component: () => import('@/pages/list/search/SearchLayout')
},
article: {
name: '文章',
component: () => import('@/pages/list/search/ArticleList')
},
application: {
name: '应用',
component: () => import('@/pages/list/search/ApplicationList')
},
project: {
name: '项目',
component: () => import('@/pages/list/search/ProjectList')
},
details: {
name: '详情页',
icon: 'profile',
component: view.blank
},
basicDetails: {
path: 'basic',
name: '基础详情页',
component: () => import('@/pages/detail/BasicDetail')
},
advanceDetails: {
path: 'advance',
name: '高级详情页',
component: () => import('@/pages/detail/AdvancedDetail')
},
result: {
name: '结果页',
icon: 'check-circle-o',
component: view.page
},
success: {
name: '成功',
component: () => import('@/pages/result/Success')
},
error: {
name: '失败',
component: () => import('@/pages/result/Error')
},
exception: {
name: '异常页',
icon: 'warning',
component: view.blank
},
exp403: {
authority: '*',
name: 'exp403',
path: '403',
component: () => import('@/pages/exception/403')
},
exp404: {
name: 'exp404',
path: '404',
component: () => import('@/pages/exception/404')
},
exp500: {
name: 'exp500',
path: '500',
component: () => import('@/pages/exception/500')
},
components: {
name: '小组件',
icon: 'appstore-o',
component: view.page
},
taskCard: {
name: '任务卡片',
component: () => import('@/pages/components/TaskCard')
},
palette: {
name: '颜色复选框',
component: () => import('@/pages/components/Palette')
}
}
export default routerMap

44
src/router/guards.js Normal file
View File

@ -0,0 +1,44 @@
import {hasPermission, hasRole} from '@/utils/authority-utils'
import {loginIgnore} from '@/router/index'
import {checkAuthorization} from '@/utils/request'
/**
* 登录守卫
* @param to
* @param form
* @param next
* @param options
*/
const loginGuard = (to, from, next, options) => {
const {message} = options
if (!loginIgnore.includes(to) && !checkAuthorization()) {
message.warning('登录已失效,请重新登录')
next({path: '/login'})
} else {
next()
}
}
/**
* 权限守卫
* @param to
* @param form
* @param next
* @param options
*/
const authorityGuard = (to, from, next, options) => {
const {store, message} = options
const permissions = store.getters['account/permissions']
const roles = store.getters['account/roles']
if (!hasPermission(to, permissions) && !hasRole(to, roles)) {
message.warning(`对不起,您无权访问页面: ${to.fullPath},请联系管理员`)
next({path: '/403'})
} else {
next()
}
}
export default {
beforeEach: [loginGuard, authorityGuard],
afterEach: []
}

View File

@ -24,7 +24,7 @@ const loginIgnore = {
* @returns {VueRouter}
*/
function initRouter(isAsync) {
const options = isAsync ? require('./config.async').default : require('./config').default
const options = isAsync ? require('./async/config.async').default : require('./config').default
formatAuthority(options.routes)
return new Router(options)
}

View File

@ -1,59 +0,0 @@
// 视图组件
const view = {
tabs: () => import('@/layouts/tabs'),
blank: () => import('@/layouts/BlankView'),
page: () => import('@/layouts/PageView')
}
// 路由组件注册
const routerMap = {
login: {
authority: '*',
path: '/login',
component: () => import('@/pages/login')
},
demo: {
name: '演示页',
renderMenu: false,
component: () => import('@/pages/demo')
},
exp403: {
authority: '*',
name: 'exp403',
path: '403',
component: () => import('@/pages/exception/403')
},
exp404: {
name: 'exp404',
path: '404',
component: () => import('@/pages/exception/404')
},
exp500: {
name: 'exp500',
path: '500',
component: () => import('@/pages/exception/500')
},
root: {
path: '/',
name: '首页',
redirect: '/login',
component: view.tabs
},
parent1: {
name: '父级路由1',
icon: 'dashboard',
component: view.blank
},
parent2: {
name: '父级路由2',
icon: 'form',
component: view.page
},
exception: {
name: '异常页',
icon: 'warning',
component: view.blank
}
}
export default routerMap

View File

@ -0,0 +1,50 @@
/**
* 判断是否有路由的权限
* @param route 路由
* @param permissions 用户权限集合
* @returns {boolean|*}
*/
function hasPermission(route, permissions) {
const authority = route.meta.authority || '*'
let required = '*'
if (typeof authority === 'string') {
required = authority
} else if (typeof authority === 'object') {
required = authority.permission
}
return required === '*' || (permissions && permissions.findIndex(item => item === required || item.id === required) !== -1)
}
/**
* 判断是否有路由需要的角色
* @param route 路由
* @param roles 用户角色集合
*/
function hasRole(route, roles) {
const authority = route.meta.authority || '*'
let required = undefined
if (typeof authority === 'object') {
required = authority.role
}
return authority === '*' || hasAnyRole(required, roles)
}
/**
* 判断是否有需要的任意一个角色
* @param required {String | Array[String]} 需要的角色可以是单个角色或者一个角色数组
* @param roles 拥有的角色
* @returns {boolean}
*/
function hasAnyRole(required, roles) {
if (!required) {
return false
} else if(Array.isArray(required)) {
return roles.findIndex(role => {
return required.findIndex(item => item === role || item === role.id) !== -1
}) !== -1
} else {
return roles.findIndex(role => role === required || role.id === required) !== -1
}
}
export {hasPermission, hasRole}

View File

@ -1,8 +1,6 @@
import routerMap from '@/router/router.map'
import routerMap from '@/router/async/router.map'
import {mergeI18nFromRoutes} from '@/utils/i18n'
import Router from 'vue-router'
import {loginIgnore} from '@/router'
import {checkAuthorization} from '@/utils/request'
/**
* 根据 路由配置 路由组件注册 解析路由
@ -97,86 +95,6 @@ function mergeRoutes(target, source) {
return Object.values(routesMap)
}
/**
* 登录守卫
* @param router 应用路由实例
*/
function loginGuard(router) {
router.beforeEach((to, from, next) => {
if (!loginIgnore.includes(to) && !checkAuthorization()) {
next({path: '/login'})
} else {
next()
}
})
}
/**
* 权限守卫
* @param router 应用路由实例
* @param store 应用的 vuex.store 实例
*/
function authorityGuard(router, store) {
router.beforeEach((to, form, next) => {
const permissions = store.getters['account/permissions']
const roles = store.getters['account/roles']
if (!hasPermission(to, permissions) && !hasRole(to, roles)) {
next({path: '/403'})
} else {
next()
}
})
}
/**
* 判断是否有路由的权限
* @param route 路由
* @param permissions 用户权限集合
* @returns {boolean|*}
*/
function hasPermission(route, permissions) {
const authority = route.meta.authority || '*'
let required = '*'
if (typeof authority === 'string') {
required = authority
} else if (typeof authority === 'object') {
required = authority.permission
}
return required === '*' || (permissions && permissions.findIndex(item => item === required || item.id === required) !== -1)
}
/**
* 判断是否有路由需要的角色
* @param route 路由
* @param roles 用户角色集合
*/
function hasRole(route, roles) {
const authority = route.meta.authority || '*'
let required = undefined
if (typeof authority === 'object') {
required = authority.role
}
return authority === '*' || hasAnyRole(required, roles)
}
/**
* 判断是否有需要的任意一个角色
* @param required {String | Array[String]} 需要的角色可以是单个角色或者一个角色数组
* @param roles 拥有的角色
* @returns {boolean}
*/
function hasAnyRole(required, roles) {
if (!required) {
return false
} else if(Array.isArray(required)) {
return roles.findIndex(role => {
return required.findIndex(item => item === role || item === role.id) !== -1
}) !== -1
} else {
return roles.findIndex(role => role === required || role.id === required) !== -1
}
}
/**
* 格式化路由的权限配置
* @param routes
@ -222,4 +140,24 @@ function getI18nKey(path) {
return keys.join('.')
}
export {parseRoutes, loadRoutes, loginGuard, authorityGuard, formatAuthority, getI18nKey}
/**
* 加载导航守卫
* @param guards
* @param options
*/
function loadGuards(guards, options) {
const {beforeEach, afterEach} = guards
const {router} = options
beforeEach.forEach(guard => {
if (guard && typeof guard === 'function') {
router.beforeEach((to, from, next) => guard(to, from, next, options))
}
})
afterEach.forEach(guard => {
if (guard && typeof guard === 'function') {
router.afterEach((to, from) => guard(to, from, options))
}
})
}
export {parseRoutes, loadRoutes, formatAuthority, getI18nKey, loadGuards}

View File

@ -1,3 +1,5 @@
import enquireJs from 'enquire.js'
export function isDef (v){
return v !== undefined && v !== null
}
@ -18,4 +20,16 @@ export function isRegExp (v) {
return _toString.call(v) === '[object RegExp]'
}
export function enquireScreen(call) {
const handler = {
match: function () {
call && call(true)
},
unmatch: function () {
call && call(false)
}
}
enquireJs.register('only screen and (max-width: 767.99px)', handler)
}
const _toString = Object.prototype.toString