feat: add function of async router and async menu; 🌟

新增:异步路由和菜单功能;
This commit is contained in:
iczer
2020-07-29 11:08:01 +08:00
parent a4764d880d
commit bfb0358217
36 changed files with 614 additions and 450 deletions

18
src/bootstrap.js vendored Normal file
View File

@@ -0,0 +1,18 @@
import {loadRoutes, loginGuard, authorityGuard} from '@/utils/routerUtil'
/**
* 启动引导方法
* 应用启动时需要执行的操作放在这里
* @param router 应用的路由实例
* @param store 应用的 vuex.store 实例
* @param i18n 应用的 vue-i18n 实例
*/
function bootstrap({router, store, i18n}) {
// 加载路由
loadRoutes({router, store, i18n})
// 添加路由守卫
loginGuard(router)
authorityGuard(router, store)
}
export default bootstrap

View File

@@ -7,7 +7,7 @@
<h1>{{config[type].title}}</h1>
<div class="desc">{{config[type].desc}}</div>
<div class="action">
<a-button type="primary" >返回首页</a-button>
<a-button type="primary" @click="backHome">返回首页</a-button>
</div>
</div>
</div>
@@ -18,11 +18,19 @@ import Config from './typeConfig'
export default {
name: 'ExceptionPage',
props: ['type'],
props: ['type', 'homeRoute'],
data () {
return {
config: Config
}
},
methods: {
backHome() {
if (this.homeRoute) {
this.$router.push(this.homeRoute)
}
this.$emit('backHome', this.type)
}
}
}
</script>

View File

@@ -16,6 +16,7 @@ module.exports = {
hideSetting: false, //隐藏设置抽屉true:隐藏false:不隐藏
systemName: 'Vue Antd Admin', //系统名称
copyright: '2018 ICZER 工作室出品', //copyright
asyncRoutes: false, //异步加载路由true:开启false:不开启
animate: { //动画设置
disabled: false, //禁用动画true:禁用false:启用
name: 'bounce', //动画效果,支持的动画效果可参考 ./animate.config.js

View File

@@ -35,8 +35,6 @@ import {mapState, mapMutations} from 'vuex'
const minHeight = window.innerHeight - 64 - 24 - 122
let menuData = []
export default {
name: 'AdminLayout',
components: {Setting, SideMenu, Drawer, PageFooter, AdminHeader},
@@ -44,12 +42,12 @@ export default {
return {
minHeight: minHeight,
collapsed: false,
menuData: menuData,
showSetting: false
}
},
computed: {
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar', 'hideSetting', 'pageMinHeight']),
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar',
'hideSetting', 'menuData']),
sideMenuWidth() {
return this.collapsed ? '80px' : '256px'
},
@@ -74,9 +72,6 @@ export default {
},
beforeDestroy() {
this.correctPageMinHeight(-minHeight + 1)
},
beforeCreate () {
menuData = this.$router.options.routes.find((item) => item.path === '/').children
}
}
</script>

View File

@@ -25,13 +25,13 @@
</template>
<script>
import {mapState} from 'vuex'
import {mapGetters} from 'vuex'
import {logout} from '@/services'
export default {
name: 'HeaderAvatar',
computed: {
...mapState('account', ['user']),
...mapGetters('account', ['user']),
},
methods: {
logout() {

View File

@@ -0,0 +1,2 @@
import TabsView from './TabsView'
export default TabsView

View File

@@ -1,6 +1,6 @@
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import {initRouter} from './router'
import './theme/index.less'
import Antd from 'ant-design-vue'
import Viser from 'viser-vue'
@@ -9,14 +9,17 @@ import store from './store'
import 'animate.css/source/animate.css'
import Plugins from '@/plugins'
import {initI18n} from '@/utils/i18n'
import bootstrap from '@/bootstrap'
const router = initRouter(store.state.setting.asyncRoutes)
const i18n = initI18n('CN', 'US')
bootstrap({router, store, i18n})
Vue.config.productionTip = false
Vue.use(Viser)
Vue.use(Antd)
Vue.use(Plugins)
const i18n = initI18n(router, 'CN', 'US')
new Vue({
router,
store,

View File

@@ -3,6 +3,7 @@ import '@/mock/user/current'
import '@/mock/project'
import '@/mock/user/login'
import '@/mock/workplace'
import '@/mock/user/routes'
// 设置全局延时
Mock.setup({

View File

@@ -22,6 +22,8 @@ Mock.mock('/login', 'post', ({body}) => {
result.data.user = user
result.data.token = 'Authorization:' + Math.random()
result.data.expireAt = new Date(new Date().getTime() + 30 * 60 * 1000)
result.data.permissions = [{id: 'analysis', extra: ['add', 'edit', 'delete']}]
result.data.roles = [{id: 'admin', extra: ['add', 'edit', 'delete']}]
}
return result
})

26
src/mock/user/routes.js Normal file
View File

@@ -0,0 +1,26 @@
import Mock from 'mockjs'
Mock.mock('/routes', 'get', () => {
let result = {}
result.code = 0
result.data = [{
router: 'root',
children: [
{
router: 'dashboard',
children: ['workplace', 'analysis'],
},
{
router: 'form',
children: ['basicForm', 'stepForm', 'advanceForm']
},
{
router: 'basicForm',
name: '验权表单',
icon: 'file-excel',
authority: 'form'
}
]
}]
return result
})

View File

@@ -0,0 +1,2 @@
import Analysis from './Analysis'
export default Analysis

View File

@@ -0,0 +1,2 @@
import WorkPlace from './WorkPlace'
export default WorkPlace

View File

@@ -1,5 +1,5 @@
<template>
<exception-page :style="`min-height: ${pageMinHeight}px`" type="403" />
<exception-page home-route="/dashboard/workplace" :style="`min-height: ${minHeight}`" type="403" />
</template>
<script>
@@ -9,7 +9,10 @@ export default {
name: 'Exp403',
components: {ExceptionPage},
computed: {
...mapState('setting', ['pageMinHeight'])
...mapState('setting', ['pageMinHeight']),
minHeight() {
return this.pageMinHeight ? this.pageMinHeight + 'px' : '100vh'
}
}
}
</script>

View File

@@ -1,5 +1,5 @@
<template>
<exception-page :style="`min-height: ${minHeight}`" type="404" />
<exception-page home-route="/dashboard/workplace" :style="`min-height: ${minHeight}`" type="404" />
</template>
<script>

View File

@@ -1,5 +1,5 @@
<template>
<exception-page :style="`min-height: ${pageMinHeight}px`" type="500" />
<exception-page home-route="/dashboard/workplace" :style="`min-height: ${minHeight}`" type="500" />
</template>
<script>
@@ -9,7 +9,10 @@ export default {
name: 'Exp500',
components: {ExceptionPage},
computed: {
...mapState('setting', ['pageMinHeight'])
...mapState('setting', ['pageMinHeight']),
minHeight() {
return this.pageMinHeight ? this.pageMinHeight + 'px' : '100vh'
}
}
}
</script>

View File

@@ -0,0 +1,2 @@
import AdvancedForm from '@/pages/form/advance/AdvancedForm'
export default AdvancedForm

View File

@@ -0,0 +1,2 @@
import BasicForm from './BasicForm'
export default BasicForm

View File

@@ -0,0 +1,2 @@
import StepForm from './StepForm'
export default StepForm

View File

@@ -75,8 +75,10 @@
<script>
import CommonLayout from '@/layouts/CommonLayout'
import {login} from '@/services'
import {login, getRoutesConfig} from '@/services'
import {setAuthorization} from '@/utils/request'
import {loadRoutes} from '@/utils/routerUtil'
import {mapMutations} from 'vuex'
export default {
name: 'Login',
@@ -94,6 +96,7 @@ export default {
}
},
methods: {
...mapMutations('account', ['setUser', 'setPermissions', 'setRoles']),
onSubmit (e) {
e.preventDefault()
this.form.validateFields((err) => {
@@ -107,15 +110,24 @@ export default {
},
afterLogin(res) {
this.logging = false
const result = res.data
if (result.code >= 0) {
const user = result.data.user
setAuthorization({token: result.data.token, expireAt: new Date(result.data.expireAt)})
this.$router.push('/dashboard/workplace')
this.$store.commit('account/setUser', user)
this.$message.success(result.message, 3)
const loginRes = res.data
if (loginRes.code >= 0) {
const user = loginRes.data.user
const permissions = loginRes.data.permissions
const roles = loginRes.data.roles
this.setUser(user)
this.setPermissions(permissions)
this.setRoles(roles)
setAuthorization({token: loginRes.data.token, expireAt: new Date(loginRes.data.expireAt)})
// 获取路由配置
getRoutesConfig().then(result => {
const routesConfig = result.data.data
loadRoutes({router: this.$router, store: this.$store, i18n: this.$i18n}, routesConfig)
this.$router.push('/dashboard/workplace')
this.$message.success(loginRes.message, 3)
})
} else {
this.error = result.message
this.error = loginRes.message
}
}
}

2
src/pages/login/index.js Normal file
View File

@@ -0,0 +1,2 @@
import Login from './Login'
export default Login

View File

@@ -0,0 +1,24 @@
import routerMap from './router.map'
import {parseRoutes} from '@/utils/routerUtil'
// 异步路由配置
const routesConfig = [
'login',
'root',
{
router: 'exp404',
path: '*',
name: '404'
},
{
router: 'exp403',
path: '/403',
name: '403'
}
]
const options = {
routes: parseRoutes(routesConfig, routerMap)
}
export default options

View File

@@ -1,20 +1,25 @@
import Login from '@/pages/login/Login'
import TabsView from '@/layouts/tabs/TabsView'
import BlankView from '@/layouts/BlankView'
import PageView from '@/layouts/PageView'
// 路由配置
const options = {
routes: [
{
path: '/login',
name: '登录页',
component: Login
component: () => import('@/pages/login')
},
{
path: '*',
name: '404',
component: () => import('@/pages/exception/404'),
},
{
path: '/403',
name: '403',
component: () => import('@/pages/exception/403'),
},
{
path: '/',
name: '首页',
@@ -32,12 +37,12 @@ const options = {
{
path: 'workplace',
name: '工作台',
component: () => import('@/pages/dashboard/workplace/WorkPlace'),
component: () => import('@/pages/dashboard/workplace'),
},
{
path: 'analysis',
name: '分析页',
component: () => import('@/pages/dashboard/analysis/Analysis'),
component: () => import('@/pages/dashboard/analysis'),
}
]
},
@@ -52,17 +57,17 @@ const options = {
{
path: 'basic',
name: '基础表单',
component: () => import('@/pages/form/basic/BasicForm'),
component: () => import('@/pages/form/basic'),
},
{
path: 'step',
name: '分步表单',
component: () => import('@/pages/form/step/StepForm'),
component: () => import('@/pages/form/step'),
},
{
path: 'advance',
name: '高级表单',
component: () => import('@/pages/form/advance/AdvancedForm'),
component: () => import('@/pages/form/advance'),
}
]
},
@@ -197,24 +202,21 @@ const options = {
component: () => import('@/pages/components/Palette')
}
]
},
{
name: '验权表单',
path: 'auth/form',
meta: {
icon: 'file-excel',
authority: {
permission: 'form'
}
},
component: () => import('@/pages/form/basic')
}
]
}
},
]
}
// 不需要登录拦截的路由配置
const loginIgnore = {
names: ['404'], //根据路由名称匹配
paths: ['/login'], //根据路由fullPath匹配
/**
* 判断路由是否包含在该配置中
* @param route vue-router 的 route 对象
* @returns {boolean}
*/
includes(route) {
return this.names.includes(route.name) || this.paths.includes(route.path)
}
}
export {options, loginIgnore}
export default options

View File

@@ -1,19 +1,29 @@
import Vue from 'vue'
import Router from 'vue-router'
import {checkAuthorization} from '@/utils/request'
import {options, loginIgnore} from './config'
Vue.use(Router)
const router = new Router(options)
// 登录拦截
router.beforeEach((to, from, next) => {
if (!loginIgnore.includes(to) && !checkAuthorization()) {
next({path: '/login'})
} else {
next()
// 不需要登录拦截的路由配置
const loginIgnore = {
names: ['404'], //根据路由名称匹配
paths: ['/login'], //根据路由fullPath匹配
/**
* 判断路由是否包含在该配置中
* @param route vue-router 的 route 对象
* @returns {boolean}
*/
includes(route) {
return this.names.includes(route.name) || this.paths.includes(route.path)
}
})
}
export default router
/**
* 初始化路由实例
* @param isAsync 是否异步路由模式
* @returns {VueRouter}
*/
function initRouter(isAsync) {
const options = isAsync ? require('./config.async').default : require('./config').default
return new Router(options)
}
export {loginIgnore, initRouter}

154
src/router/router.map.js Normal file
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

View File

@@ -1,5 +1,6 @@
// const BASE_URL = 'http://localhost:8080' your service base url
const BASE_URL = '' // mock base url
module.exports = {
LOGIN: `${BASE_URL}/login`
LOGIN: `${BASE_URL}/login`,
ROUTES: `${BASE_URL}/routes`
}

View File

@@ -1,6 +1,7 @@
import {login, logout} from './user'
import {login, logout, getRoutesConfig} from './user'
export {
login,
logout
logout,
getRoutesConfig
}

View File

@@ -1,4 +1,4 @@
import {LOGIN} from '@/services/api'
import {LOGIN, ROUTES} from '@/services/api'
import {request, METHOD, removeAuthorization} from '@/utils/request'
/**
@@ -14,11 +14,18 @@ async function login(name, password) {
})
}
async function getRoutesConfig() {
return request(ROUTES, METHOD.GET)
}
/**
* 退出登录
*/
function logout() {
localStorage.removeItem(process.env.VUE_APP_ROUTES_KEY)
localStorage.removeItem(process.env.VUE_APP_PERMISSIONS_KEY)
localStorage.removeItem(process.env.VUE_APP_ROLES_KEY)
removeAuthorization()
}
export {login, logout}
export {login, logout, getRoutesConfig}

View File

@@ -1,16 +1,8 @@
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
import PouchDB from 'pouchdb'
const db = new PouchDB('adminDb')
Vue.use(Vuex)
const store = new Vuex.Store({modules})
// 读取用户信息
db.get('currUser')
.then(doc => store.commit('account/setUser', doc.user))
.catch(() => {})
export default store

View File

@@ -1,36 +1,73 @@
import PouchDB from 'pouchdb'
let db = new PouchDB('adminDb')
export default {
namespaced: true,
state: {
user: {
name: '',
avatar: '',
position: '',
address: ''
user: undefined,
permissions: [],
roles: [],
routesConfig: []
},
getters: {
user: state => {
if (!state.user) {
try {
const user = localStorage.getItem(process.env.VUE_APP_USER_KEY)
state.user = JSON.parse(user)
} catch (e) {
console.error(e)
}
}
return state.user
},
permissions: state => {
if (!state.permissions || state.permissions.length === 0) {
try {
const permissions = localStorage.getItem(process.env.VUE_APP_PERMISSIONS_KEY)
state.permissions = JSON.parse(permissions)
} catch (e) {
console.error(e.message)
}
}
return state.permissions
},
roles: state => {
if (!state.roles || state.roles.length === 0) {
try {
const roles = localStorage.getItem(process.env.VUE_APP_ROLES_KEY)
state.roles = JSON.parse(roles)
} catch (e) {
console.error(e.message)
}
}
return state.roles
},
routesConfig: state => {
if (!state.routesConfig || state.routesConfig.length === 0) {
try {
const routesConfig = localStorage.getItem(process.env.VUE_APP_ROUTES_KEY)
state.routesConfig = eval(routesConfig) ? JSON.parse(routesConfig) : state.routesConfig
} catch (e) {
console.error(e.message)
}
}
return state.routesConfig
}
},
mutations: {
setUser (state, user) {
state.user = user
db.get('currUser').then(doc => {
db.put({
_id: 'currUser',
_rev: doc._rev,
user: user
})
}).catch(e => {
if (e.status === 404) {
db.put({
_id: 'currUser',
user: user
})
} else {
throw e
}
})
localStorage.setItem(process.env.VUE_APP_USER_KEY, JSON.stringify(user))
},
setPermissions(state, permissions) {
state.permissions = permissions
localStorage.setItem(process.env.VUE_APP_PERMISSIONS_KEY, JSON.stringify(permissions))
},
setRoles(state, roles) {
state.roles = roles
localStorage.setItem(process.env.VUE_APP_ROLES_KEY, JSON.stringify(roles))
},
setRoutesConfig(state, routesConfig) {
state.routesConfig = routesConfig
localStorage.setItem(process.env.VUE_APP_ROUTES_KEY, JSON.stringify(routesConfig))
}
}
}

View File

@@ -8,6 +8,7 @@ export default {
palettes: ADMIN.palettes,
dustbins: [],
pageMinHeight: 0,
menuData: [],
...config,
},
mutations: {
@@ -46,6 +47,12 @@ export default {
},
correctPageMinHeight(state, minHeight) {
state.pageMinHeight += minHeight
},
setMenuData(state, menuData) {
state.menuData = menuData
},
setAsyncRoutes(state, asyncRoutes) {
state.asyncRoutes = asyncRoutes
}
}
}

View File

@@ -5,28 +5,19 @@ import './Objects'
/**
* 创建 i18n 配置
* @param router 路由
* @param locale 本地化语言
* @param fallback 回退语言
* @returns {VueI18n}
*/
function initI18n(router, locale, fallback) {
function initI18n(locale, fallback) {
Vue.use(VueI18n)
const options = router.options.routes.find(item => item.path === '/').children
formatOptions(options, '')
const CN = generateI18n(new Object(), options, 'name')
const US = generateI18n(new Object(), options, 'path')
const i18n = new VueI18n({
let i18nOptions = {
locale,
fallbackLocale: fallback,
silentFallbackWarn: true,
messages: {CN, US}
})
const messages = routesI18n.messages
Object.keys(messages).forEach(key => {
i18n.mergeLocaleMessage(key, messages[key])
})
return i18n
messages: routesI18n.messages
}
return new VueI18n(i18nOptions)
}
/**
@@ -62,6 +53,15 @@ function formatOptions(options, parentPath) {
})
}
function mergeI18nFromRoutes(i18n, routes) {
formatOptions(routes, '')
const CN = generateI18n(new Object(), routes, 'name')
const US = generateI18n(new Object(), routes, 'path')
i18n.mergeLocaleMessage('CN', CN)
i18n.mergeLocaleMessage('US', US)
}
export {
initI18n
}
initI18n,
mergeI18nFromRoutes
}

157
src/utils/routerUtil.js Normal file
View File

@@ -0,0 +1,157 @@
import routerMap from '@/router/router.map'
import {mergeI18nFromRoutes} from '@/utils/i18n'
import Router from 'vue-router'
import {loginIgnore} from '@/router'
import {checkAuthorization} from '@/utils/request'
/**
* 根据 路由配置 和 路由组件注册 解析路由
* @param routesConfig 路由配置
* @param routerMap 本地路由组件注册配置
*/
function parseRoutes(routesConfig, routerMap) {
let routes = []
routesConfig.forEach(item => {
// 获取注册在 routerMap 中的 router初始化 routeCfg
let router = undefined, routeCfg = {}
if (typeof item === 'string') {
router = routerMap[item]
routeCfg = {path: router.path || item, router: item}
} else if (typeof item === 'object') {
router = routerMap[item.router]
routeCfg = item
}
// 从 router 和 routeCfg 解析路由
if (!router) {
console.warn(`can't find register for router ${routeCfg.router}, please register it in advance.`)
} else {
const route = {
path: routeCfg.path || router.path || routeCfg.router,
name: routeCfg.name || router.name,
component: router.component,
redirect: routeCfg.redirect || router.redirect,
meta: {
authority: routeCfg.authority || router.authority || '*',
icon: routeCfg.icon || router.icon,
page: routeCfg.page || router.page
}
}
if (routeCfg.children && routeCfg.children.length > 0) {
route.children = parseRoutes(routeCfg.children, routerMap)
}
routes.push(route)
}
})
return routes
}
/**
* 加载路由
* @param router 应用路由实例
* @param store 应用的 vuex.store 实例
* @param i18n 应用的 vue-i18n 实例
* @param routesConfig 路由配置
*/
function loadRoutes({router, store, i18n}, routesConfig) {
// 如果 routesConfig 有值,则更新到本地,否则从本地获取
if (routesConfig) {
store.commit('account/setRoutesConfig', routesConfig)
} else {
routesConfig = store.getters['account/routesConfig']
}
// 如果开启了异步路由,则加载异步路由配置
const asyncRoutes = store.state.setting.asyncRoutes
if (asyncRoutes) {
if (routesConfig && routesConfig.length > 0) {
const routes = parseRoutes(routesConfig, routerMap)
const finalRoutes = mergeRoutes(router.options.routes, routes)
router.options = {...router.options, routes: finalRoutes}
router.matcher = new Router({...router.options, routes:[]}).matcher
router.addRoutes(finalRoutes)
}
}
// 初始化Admin后台菜单数据
const rootRoute = router.options.routes.find(item => item.path === '/')
const menuRoutes = rootRoute && rootRoute.children
if (menuRoutes) {
mergeI18nFromRoutes(i18n, menuRoutes)
store.commit('setting/setMenuData', menuRoutes)
}
}
/**
* 合并路由
* @param target {Route[]}
* @param source {Route[]}
* @returns {Route[]}
*/
function mergeRoutes(target, source) {
const routesMap = {}
target.forEach(item => routesMap[item.path] = item)
source.forEach(item => routesMap[item.path] = item)
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 === '*' || (required && roles.findIndex(item => item === required || item.id === required) !== -1)
}
export {parseRoutes, loadRoutes, loginGuard, authorityGuard}