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; }