zy-vue-library/dynamicRouter/index.js

198 lines
6.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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