zy-vue-library/dynamicRouter/index.js

198 lines
6.7 KiB
JavaScript
Raw Permalink Normal View History

2025-10-22 11:19:51 +08:00
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;
}