Files
admin-govern/src/utils/router.ts
2025-10-24 16:17:40 +08:00

334 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string, string[]> = new Map([])
assembleAuthNode(routes, authNode, prefix, prefix)
return authNode
}
const assembleAuthNode = (routes: any, authNode: Map<string, string[]>, 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<string, any>,
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<string, any>,
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)
}