import router from '@/router/index' import { isNavigationFailure, NavigationFailureType ,useRouter} from 'vue-router' import type { RouteRecordRaw, RouteLocationRaw } from 'vue-router' import { ElNotification } from 'element-plus' import { useConfig } from '@/stores/config' import { useNavTabs } from '@/stores/navTabs' import { closeShade } from '@/utils/pageShade' import { adminBaseRoute } from '@/router/static' import { compact, isEmpty, reverse } from 'lodash-es' import { isAdminApp } from '@/utils/common' import { getRouteMenu, dictDataCache } from '@/api/auth' import { adminBaseRoutePath } from '@/router/static' const route = useRouter() /** * 导航失败有错误消息的路由push * @param to — 导航位置,同 router.push */ export const routePush = async (to: RouteLocationRaw) => { try { const failure = await router.push(to) if (isNavigationFailure(failure, NavigationFailureType.aborted)) { ElNotification({ message: 'utils.Navigation failed, navigation guard intercepted!', type: 'error' }) } else if (isNavigationFailure(failure, NavigationFailureType.duplicated)) { // ElNotification({ // message: '已在目标页', // type: 'warning' // }) } } catch (error) { ElNotification({ message: '导航失败,路由无效', type: 'error' }) console.error(error) } } /** * 获取第一个菜单 */ export const getFirstRoute = (routes: RouteRecordRaw[], menuType = 'tab'): false | RouteRecordRaw => { const routerPaths: string[] = [] const routers = router.getRoutes() routers.forEach(item => { if (item.path) routerPaths.push(item.path) }) let find: boolean | RouteRecordRaw = false for (const key in routes) { if ( routes[key].meta?.type == 'menu' && routes[key].meta?.menu_type == menuType && routerPaths.indexOf(routes[key].path) !== -1 ) { return routes[key] } else if (routes[key].children && routes[key].children?.length) { find = getFirstRoute(routes[key].children!) if (find) return find } } return find } /** * 打开侧边菜单 * @param menu 菜单数据 */ export const onClickMenu = (menu: RouteRecordRaw) => { switch (menu.meta?.menu_type) { case 'iframe': case 'tab': routePush({ path: menu.path }) break case 'link': window.open(menu.path, '_blank') break default: ElNotification({ message: 'utils.Navigation failed, the menu type is unrecognized!', type: 'error' }) break } const config = useConfig() if (config.layout.shrink) { closeShade(() => { config.setLayout('menuCollapse', true) }) } } /** * 处理后台的路由 */ export const handleAdminRoute = (routes: any) => { const viewsComponent = import.meta.glob('/src/views/**/*.vue') addRouteAll(viewsComponent, routes, adminBaseRoute.name as string) const menuAdminBaseRoute = (adminBaseRoute.path as string) + '/' // 更新stores中的路由菜单数据 const navTabs = useNavTabs() navTabs.setTabsViewRoutes(handleMenuRule(routes, menuAdminBaseRoute)) navTabs.fillAuthNode(handleAuthNode(routes, menuAdminBaseRoute)) } /** * 获取菜单的paths */ export const getMenuPaths = (menus: RouteRecordRaw[]): string[] => { let menuPaths: string[] = [] menus.forEach(item => { menuPaths.push(item.path) if (item.children && item.children.length > 0) { menuPaths = menuPaths.concat(getMenuPaths(item.children)) } }) return menuPaths } /** * 后台的菜单处理 */ const handleMenuRule = (routes: any, pathPrefix = '/', type = ['menu', 'menu_dir']) => { const menuRule: RouteRecordRaw[] = [] for (const key in routes) { if (routes[key].extend == 'add_rules_only') { continue } if (!type.includes(routes[key].type)) { continue } if (routes[key].type == 'menu_dir' && routes[key].children && !routes[key].children.length) { continue } if ( ['route', 'menu', 'nav_user_menu', 'nav'].includes(routes[key].type) && ((routes[key].menu_type == 'tab' && !routes[key].component) || (['link', 'iframe'].includes(routes[key].menu_type) && !routes[key].url)) ) { continue } const currentPath = ['link', 'iframe'].includes(routes[key].menu_type) ? routes[key].url : pathPrefix + routes[key].path let children: RouteRecordRaw[] = [] if (routes[key].children && routes[key].children.length > 0) { children = handleMenuRule(routes[key].children, pathPrefix, type) } menuRule.push({ path: currentPath, name: routes[key].name, component: routes[key].component, meta: { id: routes[key].id, title: routes[key].title, icon: routes[key].icon, keepalive: routes[key].keepalive, menu_type: routes[key].menu_type, type: routes[key].type }, children: children }) } return menuRule } /** * 处理权限节点 * @param routes 路由数据 * @param prefix 节点前缀 * @returns 组装好的权限节点 */ const handleAuthNode = (routes: any, prefix = '/') => { const authNode: Map = new Map([]) assembleAuthNode(routes, authNode, prefix, prefix) return authNode } const assembleAuthNode = (routes: any, authNode: Map, prefix = '/', parent = '/') => { const authNodeTemp = [] for (const key in routes) { if (routes[key].type == 'button') authNodeTemp.push(prefix + routes[key].name) if (routes[key].children && routes[key].children.length > 0) { assembleAuthNode(routes[key].children, authNode, prefix, prefix + routes[key].name) } } if (authNodeTemp && authNodeTemp.length > 0) { authNode.set(parent, authNodeTemp) } } /** * 动态添加路由-带子路由 * @param viewsComponent * @param routes * @param parentName * @param analyticRelation 根据 name 从已注册路由分析父级路由 */ export const addRouteAll = ( viewsComponent: Record, routes: any, parentName: string, analyticRelation = false ) => { for (const idx in routes) { if (routes[idx].extend == 'add_menu_only') { continue } if ( (routes[idx].menu_type == 'tab' && viewsComponent[routes[idx].component]) || routes[idx].menu_type == 'iframe' ) { addRouteItem(viewsComponent, routes[idx], parentName, analyticRelation) } if (routes[idx].children && routes[idx].children.length > 0) { addRouteAll(viewsComponent, routes[idx].children, parentName, analyticRelation) } } } /** * 动态添加路由 * @param viewsComponent * @param route * @param parentName * @param analyticRelation 根据 name 从已注册路由分析父级路由 */ export const addRouteItem = ( viewsComponent: Record, route: any, parentName: string, analyticRelation: boolean ) => { let path = '', component if (route.menu_type == 'iframe') { path = (isAdminApp() ? adminBaseRoute.path : '') + '/iframe/' + encodeURIComponent(route.url) component = () => import('@/layouts/common/router-view/iframe.vue') } else { path = parentName ? route.path : '/' + route.path component = viewsComponent[route.component] } if (route.menu_type == 'tab' && analyticRelation) { const parentNames = getParentNames(route.name) if (parentNames.length) { for (const key in parentNames) { if (router.hasRoute(parentNames[key])) { parentName = parentNames[key] break } } } } const routeBaseInfo: RouteRecordRaw = { path: path, name: route.name, component: component, meta: { ...route, title: route.title, extend: route.extend, icon: route.icon, keepalive: route.keepalive, menu_type: route.menu_type, type: route.type, url: route.url, addtab: true } } if (parentName) { router.addRoute(parentName, routeBaseInfo) } else { router.addRoute(routeBaseInfo) } } // 刷新菜单 export const getMenu = () => { getRouteMenu().then((res: any) => { const handlerMenu = (data: any) => { data.forEach((item: any) => { item.routePath = item.routePath[0] == '/' ? item.routePath.substring(1, item.routePath.length) : item.routePath item.path = item.routePath item.name = item.routePath item.keepalive = item.routePath item.component = item.routeName ? item.routeName.indexOf('/src/views/') > -1 ? item.routeName : `/src/views/${item.routeName}/index.vue` : '' item.type = item.children && item.children.length > 0 ? 'menu_dir' : 'menu' item.menu_type = item.children && item.children.length > 0 ? null : 'tab' if (item.children) { handlerMenu(item.children) } }) } handlerMenu(res.data) handleAdminRoute(res.data) if (route.params.to) { const lastRoute = JSON.parse(route.params.to as string) if (lastRoute.path != adminBaseRoutePath) { let query = !isEmpty(lastRoute.query) ? lastRoute.query : {} routePush({ path: lastRoute.path, query: query }) return } } }) } /** * 根据name字符串,获取父级name组合的数组 * @param name */ const getParentNames = (name: string) => { const names = compact(name.split('/')) const tempNames = [] const parentNames = [] for (const key in names) { tempNames.push(names[key]) if (parseInt(key) != names.length - 1) { parentNames.push(tempNames.join('/')) } } return reverse(parentNames) }