import 'nprogress/nprogress.css'; import { clearSession, getToken } from '@/common/utils/storage'; import openApi from '@/common/openApi'; import { useUserInfo } from '@/store/userInfo'; import { useRoutesList } from '@/store/routesList'; import { useKeepALiveNames } from '@/store/keepAliveNames'; import router from '.'; import { RouteRecordRaw } from 'vue-router'; import { LAYOUT_ROUTE_NAME } from './staticRouter'; /** * 获取目录下的 .vue、.tsx 全部文件 * @method import.meta.glob * @link 参考:https://cn.vitejs.dev/guide/features.html#json */ const viewsModules: Record = import.meta.glob(['../views/**/*.{vue,tsx}']); const dynamicViewsModules: Record = Object.assign({}, { ...viewsModules }); // 后端控制路由:执行路由数据初始化 export async function initBackendRoutes() { const token = getToken(); // 获取浏览器缓存 token 值 if (!token) { // 无 token 停止执行下一步 return false; } useUserInfo().setUserInfo({}); // 获取路由 let menuRoute = await getBackEndControlRoutes(); const cacheList: Array = []; // 处理路由(component) const routes = backEndRouterConverter(menuRoute, (router: any) => { // 可能为false时不存在isKeepAlive属性 if (!router.meta.isKeepAlive) { router.meta.isKeepAlive = false; } if (router.meta.isKeepAlive) { cacheList.push(router.name); } }); routes.forEach((item: any) => { if (item.meta.isFull) { // 菜单为全屏展示 (示例:数据大屏页面等) router.addRoute(item as RouteRecordRaw); } else { // 要将嵌套路由添加到现有的路由中,可以将路由的 name 作为第一个参数传递给 router.addRoute(),这将有效地添加路由,就像通过 children 添加的一样 router.addRoute(LAYOUT_ROUTE_NAME, item as RouteRecordRaw); } }); useKeepALiveNames().setCacheKeepAlive(cacheList); useRoutesList().setRoutesList(routes); } // 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由 export async function getBackEndControlRoutes() { try { const menuAndPermission = await openApi.getPermissions(); // 赋值权限码,用于控制按钮等 useUserInfo().userInfo.permissions = menuAndPermission.permissions; return menuAndPermission.menus; } catch (e: any) { console.error('获取菜单权限信息失败', e); clearSession(); throw e; } } type RouterConvCallbackFunc = (router: any) => void; /** * 后端控制路由,后端返回路由 转换为vue route * * @description routes参数配置简介 * @param code(path) ==> route.path -> 路由菜单访问路径 * @param name ==> title,路由标题 相当于route.meta.title * * @param meta ==> 路由菜单元信息 * @param meta.routeName ==> route.name -> 路由 name (对应页面组件 name, 可用作 KeepAlive 缓存标识 && 按钮权限筛选) * @param meta.redirect ==> route.redirect -> 路由重定向地址 * @param meta.component ==> 文件路径 * @param meta.icon ==> 菜单和面包屑对应的图标 * @param meta.isHide ==> 是否在菜单中隐藏 (通常列表详情页需要隐藏) * @param meta.isFull ==> 菜单是否全屏 (示例:数据大屏页面) * @param meta.isAffix ==> 菜单是否固定在标签页中 (首页通常是固定项) * @param meta.isKeepAlive ==> 当前路由是否缓存 * @param meta.linkType ==> 外链类型, 内嵌: 以iframe展示、外链: 新标签打开 * @param meta.link ==> 外链地址 * */ export function backEndRouterConverter(routes: any, callbackFunc: RouterConvCallbackFunc = null as any, parentPath: string = '/') { if (!routes) { return []; } const routeItems = []; for (let item of routes) { if (!item.meta) { return item; } // 将json字符串的meta转为对象 item.meta = JSON.parse(item.meta); // 将meta.comoponet 解析为route.component if (item.meta.component) { item.component = dynamicImport(dynamicViewsModules, item.meta.component); delete item.meta['component']; } let path = item.code; // 如果不是以 / 开头,则路径需要拼接父路径 if (!path.startsWith('/')) { path = parentPath + '/' + path; } item.path = path; delete item['code']; // route.meta.title == resource.name item.meta.title = item.name; delete item['name']; // route.name == resource.meta.routeName item.name = item.meta.routeName; delete item.meta['routeName']; // route.redirect == resource.meta.redirect if (item.meta.redirect) { item.redirect = item.meta.redirect; delete item.meta['redirect']; } // 存在回调,则执行回调 callbackFunc && callbackFunc(item); item.children && backEndRouterConverter(item.children, callbackFunc, item.path); routeItems.push(item); } return routeItems; } /** * 后端路由 component 转换函数 * @param dynamicViewsModules 获取目录下的 .vue、.tsx 全部文件 * @param component 当前要处理项 component * @returns 返回处理成函数后的 component */ export function dynamicImport(dynamicViewsModules: Record, component: string) { const keys = Object.keys(dynamicViewsModules); const matchKeys = keys.filter((key) => { const k = key.replace(/..\/views|../, ''); return k.startsWith(`${component}`) || k.startsWith(`/${component}`); }); if (matchKeys?.length === 1) { return dynamicViewsModules[matchKeys[0]]; } if (matchKeys?.length > 1) { console.error('匹配到多个相似组件路径, 可添加后缀.vue或.tsx进行区分或者重命名组件名, 请调整...', matchKeys); return null; } console.warn(`未匹配到[${component}]组件名对应的组件文件`); return null; }