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

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}