设计思路:
系统应用级别,目前简单的路由方式,无法满足平台级别的应用。
对路由管理的一种集中式管理和驱动。
设计动机:
-
注册动态路由
-
动态路由数据的存取
-
进入页面前/中/后的逻辑注入
-
多端路由访问和拦截
API
名称 | 含义 |
---|---|
loader | 路由加载主入口 |
registerDynamicRoutes | 注册动态路由 |
redirectEntry | 路由拦截异常跳转 |
redirectNextApp | 路由跳转 |
代码实践:
/*
* @Description: 路由管理器
* @Author: ffzheng
*/
import { cache } from '@basic-utils'
import { Config, BaseStore, Service} from '@basic-library'
import { EduMessage } from '@components'
import LoginLoader from "./Login";
/**
* registerDynamicRoutes 注册动态路由
* @param {*} router 路由
* 场景: 刷新页面
*/
function registerDynamicRoutes(router){
Window._EDU_SYSTEM_ROUTERS = router.getRoutes()
const _AUTH_STORE = cache.getCache('_AUTH_STORE_','session') || []
_AUTH_STORE.forEach( menus => {
const routerName = menus.path?.substr(1)
if(routerName && !router.hasRoute(routerName)){
router.addRoute('home',{
path: menus.path,
name: routerName,
meta: {
id: menus.pageId,
title: menus.pageName,
isAsync: true,
icon: menus.icon || 'icon-wenjian'
},
component: ()=> import(`@/${menus.component}`)
})
}
})
}
/**
* loaderAuthData 从内存中加载路由
* @param {*} router 路由
* 场景: 刷新页面
*/
function loaderAuthData(){
const _AUTH_STORE = cache.getCache('_AUTH_STORE_','session')
if(_AUTH_STORE){
BaseStore.auth.initAuthData(_AUTH_STORE)
}else{
console.warn('获取功能权限失效!')
}
}
const pathFill = (url) => {
let path = url
if(path.indexOf('/') !== 0) path = `/${path}`
if(path.lastIndexOf('/') !== path.length -1) path = `${path}/`
if(path.indexOf('/') == -1) path = `/${path}/`
return path
}
/**应用路径拼装--斜杠路径补全
* @param {*} url
* @returns
*/
const getMicroUrl = (url) => {
const protocol = window.location.protocol
const host = window.location.host
const path = pathFill(url)
return `${protocol}//${host}${path}`
}
const redirectEntry = () => {
const url = window._IS_RUN_MICRO_MODE ? getMicroUrl('main') : '/login'
window.location.href = url
}
const redirectNextApp = (next) => {
window._IS_RUN_MICRO_MODE ? (window.location.href = getMicroUrl('main')) : next()
}
/**
* registerPageMount 全局页面挂载监听
* @param {*} router 路由
* 场景:
* 1、页面内获取接口请求标记-内置全局实例
* 2、页面内错误捕获
*/
function registerPageMount(router){
// 进入页面
router.beforeResolve(async(to, from, next) => {
document.title = Config.BSConfig.systemName || '';
try {
if(Config.BSConfig?.IS_AUTH_INTERFACE){
const cItem= BaseStore.auth.getInfoByRoute(to.path)
if(cItem){
const result = await Service.useHttp('interfaceService',`pageId=${cItem.pageId}`)
cache.setCache('_EDU_CUR_PAGE_INFO',cItem,'session')
if(result?.success){
cache.setCache('_EDU_CUR_INTERFACE_INFO',result?.returnObj,'session')
Service.initData(result?.returnObj)
}else{
console.warn(`当前页面${cItem.pageId}接口未授权!`)
}
}
}
next()
} catch (error) {
console.warn(error)
next()
}
});
}
const getExtEle = (list, field) => {
return list.reduce((prev, next) => prev.concat(pathFill(next[field])), [])
}
const logoutAction = () => {
LoginLoader.logout().then(() => LoginLoader.redirect())
}
const entryClietInspection = () => {
if(!window._IS_RUN_MICRO_MODE) return
const BaseUrl = process.env.BASE_URL
const subSystemList = LoginLoader.getSubApps()
const extPaths = getExtEle(subSystemList,"path")?.join('|')
if(extPaths.indexOf(BaseUrl) == -1){
// 不存在当前系统中,进行提醒,非法访问
EduMessage({
type: 'error',
message: '非法访问!'
})
logoutAction()
}
}
/**
* 业务路由加载器
* @param {*} router 路由
*/
function loader(router){
entryClietInspection()
loaderAuthData()
registerDynamicRoutes(router)
registerPageMount(router)
}
export default{
loader,
registerDynamicRoutes,
redirectEntry,
redirectNextApp
}
核心解读:
entryClietInspection 子系统拦截
从登录管理器LoginLoader.getSubApps
获取当前用户的子系统,根据访问的目标路径,进行匹配命中,如果异常就进行拦截提醒,停止进入。
registerDynamicRoutes 注册动态路由
从本地缓存cache.getCache('_AUTH_STORE_','session')
获取菜单信息,根据约定的菜单规则
- 路由判断
- addRoute 路由注册
registerPageMount 全局页面挂载监听
进入页面后,目前的设计逻辑是,根据进入页面的ID,从后端接口发送请求,获取到当前页面所拥有的接口权限。
主要目的:
控制页面接口权限
loaderAuthData 加载权限数据
主要场景是,刷新页面后,获取当菜单路由信息
然后进行BaseStore.auth.initAuthData(_AUTH_STORE)
进行注册
这个感觉好像在应用管理器 AppLoader里面有这个逻辑。 重复了。我看看去。
设计完毕,期望你的指点....
针对Loader管理器的一系列,请看下方:
业务侧-Loader设计
业务侧-系统应用加载器-AppLoader
业务侧-登录管理器-LoginLoader
业务侧-异常拦截管理器-ErrorLoader