import type { RouteRecordRaw } from 'vue-router' import router from '@af-mobile-client-vue3/router' import routes from '@af-mobile-client-vue3/router/routes' import useUserStore from '@af-mobile-client-vue3/stores/modules/user.js' /** * 根据 路由配置 和 路由组件注册 解析路由 * @param routesConfig 路由配置 * @param routerMap 本地路由组件注册配置 */ function parseRoutes(routesConfig, routerMap) { const routes = [] routesConfig.forEach((item) => { // 获取注册在 routerMap 中的 router,初始化 routeCfg let router: any let routeCfg: any if (typeof item === 'string') { router = routerMap[item] routeCfg = { path: (router && router.path) || item, router: item } } else if (typeof item === 'object') { // 当没有设置路由对象名或者设置的是blank路由对象时, 给空界面, path为名称 if (!item.router || item.router === 'blank') { router = routerMap.blank item.path = encodeURI(item.name) } else { router = routerMap[item.router] } // 查看是否是单页面 if (item.meta && item.meta.singlePage) { router = routerMap.singlePage item.path = encodeURI(item.name) } // 当没在动态路由对象中找到时, 不添加到路由 // if (!router) return routeCfg = item } if (!router) { console.warn(`can't find register for router ${routeCfg.router}, please register it in advance.`) router = typeof item === 'string' ? { path: item, name: item } : item } // 从 router 和 routeCfg 解析路由 const meta = { authority: router.authority, icon: router.icon, page: router.page, link: router.link, params: router.params, query: router.query, ...router.meta, } const cfgMeta = { authority: routeCfg.authority, icon: routeCfg.icon, page: routeCfg.page, link: routeCfg.link, params: routeCfg.params, query: routeCfg.query, ...routeCfg.meta, } Object.keys(cfgMeta).forEach((key) => { if (cfgMeta[key] === undefined || cfgMeta[key] === null || cfgMeta[key] === '') delete cfgMeta[key] }) Object.assign(meta, cfgMeta) const route = { path: routeCfg.path || router.path || routeCfg.router, name: routeCfg.name || router.name, component: router.component || router, redirect: routeCfg.redirect || router.redirect, meta: { ...meta, authority: meta.authority || '*' }, beforeEnter: undefined, children: undefined, } if (router.beforeEnter) route.beforeEnter = router.beforeEnter if (routeCfg.invisible || router.invisible) route.meta.invisible = true if (routeCfg.children && routeCfg.children.length > 0) route.children = parseRoutes(routeCfg.children, routerMap) // 当没有子并且自己时blank(空界面)时, 不添加到路由 if (route.component.name === 'blank' && route.children && route.children.length <= 0) return routes.push(route) }) return routes } /** * 加载路由 * @param routesConfig {[{router: string, children: [{router: string, children: string[]}, {router: string, children: string[]}, {router: string, authority: string, name: string, icon: string}, {path: string, router: string, name: string, icon: string, link: string}, {path: string, router: string, name: string, icon: string, link: string}]}]} 路由配置 */ function loadRoutes(routesConfig?: any) { // 如果 routesConfig 有值,则更新到本地,否则从本地获取 if (routesConfig) useUserStore().setRoutesConfig(routesConfig) else routesConfig = useUserStore().getRoutesConfig() if (routesConfig && routesConfig.length > 0) { const newRoutes = parseRoutes(routesConfig, routes) // 设置路由首页 newRoutes[0].redirect = '/' let finalRoutes = mergeRoutes(routes, newRoutes) formatRoutes(finalRoutes) const duplicates = checkDuplicateRouteNames(finalRoutes) if (duplicates.length > 0) { // 可以选择在这里处理重复的路由名称 finalRoutes = deduplicateRouteNames(finalRoutes) } finalRoutes.forEach((row) => { router.addRoute(row) }) } } function checkDuplicateRouteNames(routes) { const names = new Set() const duplicates = [] function check(route) { if (route.name) { if (names.has(`${route.name}_${route.path}`)) { duplicates.push(`${route.name}_${route.path}`) } else { names.add(`${route.name}_${route.path}`) } } if (route.children) { route.children.forEach(check) } } routes.forEach(check) return duplicates } // 处理重复路由名称的函数 function deduplicateRouteNames(routes) { const namePathMap = new Map() // 存储 name_path 组合 const nameCountMap = new Map() // 存储仅 name 的计数 function deduplicate(route) { if (!route) return null if (route.name) { const namePathKey = `${route.name}_${route.path}` const nameKey = route.name // 检查 name 和 path 是否都重复 if (namePathMap.has(namePathKey)) { // 完全重复的路由,直接移除 // console.log(`移除重复路由: name=${route.name}, path=${route.path}`) return null } else { // 记录这个 name 和 path 的组合 namePathMap.set(namePathKey, route) // 检查是否只有 name 重复 if (nameCountMap.has(nameKey)) { const count = nameCountMap.get(nameKey) + 1 nameCountMap.set(nameKey, count) // 修改路由名称,添加计数后缀 route.name = `${nameKey}_${count}` // console.log(`重命名重复路由: ${nameKey} -> ${route.name}`) } else { nameCountMap.set(nameKey, 1) } } } // 递归处理子路由 if (route.children && route.children.length) { route.children = route.children .map(child => deduplicate({ ...child })) .filter(child => child !== null) // 移除返回 null 的子路由 } return route } // 处理所有路由并过滤掉返回 null 的路由 return routes .map(route => deduplicate({ ...route })) .filter(route => route !== null) } /** * 合并路由 * @param target * @param source */ function mergeRoutes(target: Array, source: Array) { const routesMap: RouteRecordRaw = {} as RouteRecordRaw target.forEach(item => routesMap[item.path] = item) source.forEach(item => routesMap[item.path] = item) return Object.values(routesMap) } /** * 格式化路由 * @param routes 路由配置 */ function formatRoutes(routes) { routes.forEach((route) => { const { path } = route if (!path.startsWith('/') && path !== '*') route.path = `/${path}` }) formatAuthority(routes) } /** * 格式化路由的权限配置 * @param routes 路由 * @param pAuthorities 父级路由权限配置集合 */ function formatAuthority(routes, pAuthorities = []) { routes.forEach((route) => { const meta = route.meta const defaultAuthority = pAuthorities[pAuthorities.length - 1] || { permission: '*' } if (meta) { let authority: any = {} if (!meta.authority) { authority = defaultAuthority } else if (typeof meta.authority === 'string' || Array.isArray(meta.authority)) { authority.permission = meta.authority } else if (typeof meta.authority === 'object') { authority = meta.authority const { role } = authority if (typeof role === 'string') authority.role = [role] if (!authority.permission && !authority.role) authority = defaultAuthority } meta.authority = authority } else { const authority = defaultAuthority route.meta = { authority } } route.meta.pAuthorities = pAuthorities if (route.children) formatAuthority(route.children, [...pAuthorities, route.meta.authority]) }) } /** * 加载导航守卫 * @param guards */ function loadGuards(guards, _router) { const { beforeEach, afterEach } = guards beforeEach.forEach((guard) => { if (guard && typeof guard === 'function') _router.beforeEach((to, from, next) => guard(to, from, next)) }) afterEach.forEach((guard) => { if (guard && typeof guard === 'function') _router.afterEach((to, from) => guard(to, from)) }) } /** * 资源服务路由转新路由 * @param func */ function funcToRouter(func) { return [{ router: 'root', children: positionRouter(parsefunc(func)), }] } function parsefunc(func) { const tree = [] for (const row of func) { const route = { router: row.link, meta: { singlePage: row.navigate, }, position: row.position - 1, icon: row.icon, name: row.name, params: undefined, children: undefined, } if (row?.link?.includes('$')) { route.router = row.link.split('$')[1] route.params = row.link.split('$')[0] } if (row.children && row.children.length > 0) route.children = parsefunc(row.children) tree.push(route) } // 如果是 microapp 作为子应用路由应该是平铺的 树状结构由父项目处理 if (window.__MICRO_APP_ENVIRONMENT__) return flattenTree(tree) return tree } function flattenTree(tree) { let flat = [] for (const node of tree) { const { children, ...rest } = node flat.push(rest) if (children && children.length > 0) flat = flat.concat(flattenTree(children)) } return flat } /** * 资源服务路由排序 */ function positionRouter(r) { let router = r.sort((a, b) => a.position - b.position) router = arrRemoveEmpty(router) return router } /** * 数组去空值 */ function arrRemoveEmpty(arr) { for (let i = 0; i < arr.length; i++) { if (arr[i] === '' || typeof (arr[i]) == 'undefined') { arr.splice(i, 1) i-- } } return arr } export { formatAuthority, formatRoutes, funcToRouter, loadGuards, loadRoutes, parseRoutes }