198 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
		
			
		
	
	
			198 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
|  | import { cloneDeep } from "lodash-es"; | |||
|  | import { conversionRouterMeta, conversionNavMeta } from "../conversionRouterMeta/index.js"; | |||
|  | import { resetQueryCriteria } from "../hooks/useQueryCriteria/index.js"; | |||
|  | import { postRequest } from "../axios/index.js"; | |||
|  | import Children from '../components/children/index.vue' | |||
|  | 
 | |||
|  | /** | |||
|  |  * 动态路由配置对象 | |||
|  |  */ | |||
|  | let dynamicRouterConfig = { | |||
|  |   router: null, | |||
|  |   stores: { | |||
|  |     routerStore: null, | |||
|  |     menuStore: null, | |||
|  |     userStore: null, | |||
|  |     navStore: null | |||
|  |   }, | |||
|  |   modules: null, | |||
|  |   childrenComponent: Children, | |||
|  |   getRouterUrl: '/sys/menu/nav', | |||
|  |   loginPath: '/login', | |||
|  |   notFoundPath: '/404', | |||
|  |   parentRouteName: 'app' | |||
|  | }; | |||
|  | 
 | |||
|  | // 用来获取后台拿到的路由
 | |||
|  | let storageRouter = null; | |||
|  | 
 | |||
|  | // 后台返回的默认选中的导航
 | |||
|  | let defaultSelectionNav = ""; | |||
|  | 
 | |||
|  | /** | |||
|  |  * 配置动态路由 | |||
|  |  * @param {Object} config - 配置对象 | |||
|  |  * @param {Object} config.router - Vue Router 实例 | |||
|  |  * @param {Object} config.stores - Pinia Store 实例集合 | |||
|  |  * @param {Object} config.stores.routerStore - 路由状态管理 | |||
|  |  * @param {Object} config.stores.menuStore - 菜单状态管理 | |||
|  |  * @param {Object} config.stores.userStore - 用户状态管理 | |||
|  |  * @param {Object} config.stores.navStore - 导航状态管理 | |||
|  |  * @param {Object} config.modules - 组件模块映射 | |||
|  |  * @param {Object} [config.childrenComponent] - 路由为children时渲染的组件 | |||
|  |  * @param {string} [config.getRouterUrl='/sys/menu/nav'] - 获取菜单数据的API端点,要求返回 { menuList, permissions, navList, defaultSelection } | |||
|  |  * @param {string} [config.loginPath='/login'] - 登录页面路径 | |||
|  |  * @param {string} [config.notFoundPath='/404'] - 404页面路径 | |||
|  |  * @param {string} [config.parentRouteName='app'] - 父路由名称 | |||
|  |  */ | |||
|  | export function configureDynamicRouter(config) { | |||
|  |   if (!config.router) throw new Error('router 参数必传'); | |||
|  |   if (!config.stores || !config.stores.routerStore || !config.stores.menuStore || | |||
|  |       !config.stores.userStore || !config.stores.navStore) | |||
|  |     throw new Error('stores (routerStore, menuStore, userStore, navStore) 必传'); | |||
|  |   if (!config.modules) throw new Error('modules 参数必传'); | |||
|  | 
 | |||
|  |   dynamicRouterConfig = { | |||
|  |     router: config.router, | |||
|  |     stores: config.stores, | |||
|  |     modules: config.modules, | |||
|  |     childrenComponent: config.childrenComponent || Children, | |||
|  |     getRouterUrl: config.getRouterUrl || '/sys/menu/nav', | |||
|  |     loginPath: config.loginPath || '/login', | |||
|  |     notFoundPath: config.notFoundPath || '/404', | |||
|  |     parentRouteName: config.parentRouteName || 'app' | |||
|  |   }; | |||
|  | 
 | |||
|  |   setupRouterGuard(); | |||
|  | } | |||
|  | 
 | |||
|  | /** | |||
|  |  * 设置路由守卫 | |||
|  |  */ | |||
|  | function setupRouterGuard() { | |||
|  |   const { router, stores } = dynamicRouterConfig; | |||
|  | 
 | |||
|  |   router.beforeEach(async (to, from, next) => { | |||
|  |     const { routerStore, userStore, navStore } = stores; | |||
|  |     // 需要登陆
 | |||
|  |     if (to.meta.isLogin !== false) { | |||
|  |       if (!userStore.getUserInfo.userId) { | |||
|  |         next(dynamicRouterConfig.loginPath); | |||
|  |         return; | |||
|  |       } | |||
|  |       if (!storageRouter) { | |||
|  |         // 变量里没有储存路由
 | |||
|  |         if (routerStore.getRouters.length === 0) { | |||
|  |           // pinia里没有储存路由,去后台获取路由
 | |||
|  |           const { menuList, permissions, navList, defaultSelection } = await postRequest(dynamicRouterConfig.getRouterUrl); | |||
|  |           // 后台返回的默认选中导航
 | |||
|  |           defaultSelectionNav = defaultSelection; | |||
|  |           // 后台返回的权限
 | |||
|  |           userStore.setPermissions(permissions); | |||
|  |           // 后台返回的路由
 | |||
|  |           storageRouter = conversionRouterMeta(menuList); | |||
|  |           // 后台返回的导航
 | |||
|  |           navStore.setNavList(conversionNavMeta(navList)); | |||
|  |           // 存储路由
 | |||
|  |           routerStore.setRouters(storageRouter); | |||
|  |           // 执行路由跳转方法
 | |||
|  |           routerGo(to, next); | |||
|  |         } else { | |||
|  |           // pinia里储存了路由
 | |||
|  |           // 拿到路由
 | |||
|  |           storageRouter = routerStore.getRouters; | |||
|  |           // 执行路由跳转方法
 | |||
|  |           routerGo(to, next); | |||
|  |         } | |||
|  |       } else { | |||
|  |         next(); | |||
|  |       } | |||
|  |     } else { | |||
|  |       // 不需要登陆,清空储存路由
 | |||
|  |       resetDynamicRouter(); | |||
|  |       next(); | |||
|  |     } | |||
|  |   }); | |||
|  | } | |||
|  | 
 | |||
|  | function routerGo(to, next) { | |||
|  |   const { router, stores, parentRouteName, notFoundPath } = dynamicRouterConfig; | |||
|  |   const { menuStore, userStore } = stores; | |||
|  |   // 储存权限,给permission指令使用
 | |||
|  |   window.permissions = userStore.getPermissions; | |||
|  |   // 过滤路由
 | |||
|  |   storageRouter = filterAsyncRouter(cloneDeep(storageRouter)); | |||
|  |   for (let i = 0; i < storageRouter.length; i++) { | |||
|  |     // 动态添加路由
 | |||
|  |     router.addRoute(parentRouteName, storageRouter[i]); | |||
|  |   } | |||
|  |   // 将404路由添加到最后
 | |||
|  |   router.addRoute({ path: "/:pathMatch(.*)*", redirect: notFoundPath }); | |||
|  |   for (let i = 0; i < router.options.routes.length; i++) { | |||
|  |     if (router.options.routes[i].path === "/") { | |||
|  |       // 将路由数据存到一个新的pinia里,做菜单渲染
 | |||
|  |       menuStore.setMenus(router.options.routes[i].children.concat(storageRouter)); | |||
|  |       // 如果没有选中任何导航,则默认选中一个
 | |||
|  |       if (!menuStore.getModel) menuStore.setModel(defaultSelectionNav); | |||
|  |       break; | |||
|  |     } | |||
|  |   } | |||
|  |   // 等待addRoute执行完毕跳转路由
 | |||
|  |   next({ ...to, replace: true }); | |||
|  | } | |||
|  | 
 | |||
|  | function filterAsyncRouter(asyncRouterMap) { | |||
|  |   // 遍历后台传来的路由字符串,转换为组件对象
 | |||
|  |   return asyncRouterMap.filter((route) => { | |||
|  |     // 后台将name存成了routeKey,将meta.title存成了name,这里使用routeKey作为name
 | |||
|  |     route.name = route.routeKey || ""; | |||
|  |     route.props = route.props === 1; | |||
|  |     if (route.component) { | |||
|  |       if (route.component === "children") { | |||
|  |         route.component = dynamicRouterConfig.childrenComponent; | |||
|  |       } else { | |||
|  |         const { modules } = dynamicRouterConfig; | |||
|  |         if (route.component.charAt(0) === "/") { | |||
|  |           // 如果路径的第一位是/,则直接使用modules里的组件
 | |||
|  |           route.component = modules[`./views${route.component}.vue`]; | |||
|  |         } else { | |||
|  |           //  如果不是,则拼接成路径
 | |||
|  |           route.component = modules[`./views/${route.component}.vue`]; | |||
|  |         } | |||
|  |       } | |||
|  |     } | |||
|  |     if (route.list && route.list.length) { | |||
|  |       // 如果存在子级递归
 | |||
|  |       route.children = filterAsyncRouter(route.list); | |||
|  |       // 因为后台返回的子级是list,所以需要删除
 | |||
|  |       delete route.list; | |||
|  |     } | |||
|  |     return true; | |||
|  |   }); | |||
|  | } | |||
|  | 
 | |||
|  | /** | |||
|  |  * 重置动态路由状态 | |||
|  |  * 用于登出或重新配置时清理状态 | |||
|  |  */ | |||
|  | export function resetDynamicRouter() { | |||
|  |   storageRouter = null; | |||
|  |   defaultSelectionNav = ""; | |||
|  |   const { stores } = dynamicRouterConfig; | |||
|  |   const { routerStore, menuStore, navStore, userStore } = stores; | |||
|  |   routerStore?.$reset(); | |||
|  |   menuStore?.$reset(); | |||
|  |   navStore?.$reset(); | |||
|  |   userStore?.$reset(); | |||
|  |   window.permissions = undefined; | |||
|  |   resetQueryCriteria(); | |||
|  | } | |||
|  | 
 | |||
|  | /** | |||
|  |  * 获取当前存储的路由数据 | |||
|  |  * @returns {Array|null} 路由数据数组或null | |||
|  |  */ | |||
|  | export function getStorageRouter() { | |||
|  |   return storageRouter; | |||
|  | } |