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