225 lines
6.8 KiB
TypeScript
225 lines
6.8 KiB
TypeScript
|
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
|
|||
|
import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
|
|||
|
import { isUrl } from '@/utils/is'
|
|||
|
import { omit, cloneDeep } from 'lodash-es'
|
|||
|
|
|||
|
const modules = import.meta.glob('../views/**/*.{vue,tsx}')
|
|||
|
|
|||
|
/* Layout */
|
|||
|
export const Layout = () => import('@/layout/Layout.vue')
|
|||
|
|
|||
|
export const getParentLayout = () => {
|
|||
|
return () =>
|
|||
|
new Promise((resolve) => {
|
|||
|
resolve({
|
|||
|
name: 'ParentLayout'
|
|||
|
})
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
// 按照路由中meta下的rank等级升序来排序路由
|
|||
|
export const ascending = (arr: any[]) => {
|
|||
|
arr.forEach((v) => {
|
|||
|
if (v?.meta?.rank === null) v.meta.rank = undefined
|
|||
|
if (v?.meta?.rank === 0) {
|
|||
|
if (v.name !== 'home' && v.path !== '/') {
|
|||
|
console.warn('rank only the home page can be 0')
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
return arr.sort((a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
|
|||
|
return a?.meta?.rank - b?.meta?.rank
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormalized => {
|
|||
|
if (!route) return route
|
|||
|
const { matched, ...opt } = route
|
|||
|
return {
|
|||
|
...opt,
|
|||
|
matched: (matched
|
|||
|
? matched.map((item) => ({
|
|||
|
meta: item.meta,
|
|||
|
name: item.name,
|
|||
|
path: item.path
|
|||
|
}))
|
|||
|
: undefined) as RouteRecordNormalized[]
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 后端控制路由生成
|
|||
|
export const generateRoute = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => {
|
|||
|
const res: AppRouteRecordRaw[] = []
|
|||
|
const modulesRoutesKeys = Object.keys(modules)
|
|||
|
for (const route of routes) {
|
|||
|
const meta = {
|
|||
|
title: route.name,
|
|||
|
icon: route.icon,
|
|||
|
hidden: !route.visible,
|
|||
|
noCache: !route.keepAlive,
|
|||
|
alwaysShow:
|
|||
|
route.children &&
|
|||
|
route.children.length === 1 &&
|
|||
|
(route.alwaysShow !== undefined ? route.alwaysShow : true)
|
|||
|
}
|
|||
|
// 路由地址转首字母大写驼峰,作为路由名称,适配keepAlive
|
|||
|
let data: AppRouteRecordRaw = {
|
|||
|
path: route.path,
|
|||
|
name:
|
|||
|
route.componentName && route.componentName.length > 0
|
|||
|
? route.componentName
|
|||
|
: toCamelCase(route.path, true),
|
|||
|
redirect: route.redirect,
|
|||
|
meta: meta
|
|||
|
}
|
|||
|
//处理顶级非目录路由
|
|||
|
if (!route.children && route.parentId == 0 && route.component) {
|
|||
|
data.component = Layout
|
|||
|
data.meta = {}
|
|||
|
data.name = toCamelCase(route.path, true) + 'Parent'
|
|||
|
data.redirect = ''
|
|||
|
meta.alwaysShow = true
|
|||
|
const childrenData: AppRouteRecordRaw = {
|
|||
|
path: '',
|
|||
|
name: toCamelCase(route.path, true),
|
|||
|
redirect: route.redirect,
|
|||
|
meta: meta
|
|||
|
}
|
|||
|
const index = route?.component
|
|||
|
? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component))
|
|||
|
: modulesRoutesKeys.findIndex((ev) => ev.includes(route.path))
|
|||
|
childrenData.component = modules[modulesRoutesKeys[index]]
|
|||
|
data.children = [childrenData]
|
|||
|
} else {
|
|||
|
// 目录
|
|||
|
if (route.children) {
|
|||
|
data.component = Layout
|
|||
|
data.redirect = getRedirect(route.path, route.children)
|
|||
|
// 外链
|
|||
|
} else if (isUrl(route.path)) {
|
|||
|
data = {
|
|||
|
path: '/external-link',
|
|||
|
component: Layout,
|
|||
|
meta: {
|
|||
|
name: route.name
|
|||
|
},
|
|||
|
children: [data]
|
|||
|
} as AppRouteRecordRaw
|
|||
|
// 菜单
|
|||
|
} else {
|
|||
|
// 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会根path保持一致)
|
|||
|
const index = route?.component
|
|||
|
? modulesRoutesKeys.findIndex((ev) => ev.includes(route.component))
|
|||
|
: modulesRoutesKeys.findIndex((ev) => ev.includes(route.path))
|
|||
|
data.component = modules[modulesRoutesKeys[index]]
|
|||
|
}
|
|||
|
if (route.children) {
|
|||
|
data.children = generateRoute(route.children)
|
|||
|
}
|
|||
|
}
|
|||
|
res.push(data as AppRouteRecordRaw)
|
|||
|
}
|
|||
|
return res
|
|||
|
}
|
|||
|
export const getRedirect = (parentPath: string, children: AppCustomRouteRecordRaw[]) => {
|
|||
|
if (!children || children.length == 0) {
|
|||
|
return parentPath
|
|||
|
}
|
|||
|
const path = generateRoutePath(parentPath, children[0].path)
|
|||
|
// 递归子节点
|
|||
|
if (children[0].children) return getRedirect(path, children[0].children)
|
|||
|
}
|
|||
|
const generateRoutePath = (parentPath: string, path: string) => {
|
|||
|
if (parentPath.endsWith('/')) {
|
|||
|
parentPath = parentPath.slice(0, -1) // 移除默认的 /
|
|||
|
}
|
|||
|
if (!path.startsWith('/')) {
|
|||
|
path = '/' + path
|
|||
|
}
|
|||
|
return parentPath + path
|
|||
|
}
|
|||
|
export const pathResolve = (parentPath: string, path: string) => {
|
|||
|
if (isUrl(path)) return path
|
|||
|
const childPath = path.startsWith('/') || !path ? path : `/${path}`
|
|||
|
return `${parentPath}${childPath}`.replace(/\/\//g, '/')
|
|||
|
}
|
|||
|
|
|||
|
// 路由降级
|
|||
|
export const flatMultiLevelRoutes = (routes: AppRouteRecordRaw[]) => {
|
|||
|
const modules: AppRouteRecordRaw[] = cloneDeep(routes)
|
|||
|
for (let index = 0; index < modules.length; index++) {
|
|||
|
const route = modules[index]
|
|||
|
if (!isMultipleRoute(route)) {
|
|||
|
continue
|
|||
|
}
|
|||
|
promoteRouteLevel(route)
|
|||
|
}
|
|||
|
return modules
|
|||
|
}
|
|||
|
|
|||
|
// 层级是否大于2
|
|||
|
const isMultipleRoute = (route: AppRouteRecordRaw) => {
|
|||
|
if (!route || !Reflect.has(route, 'children') || !route.children?.length) {
|
|||
|
return false
|
|||
|
}
|
|||
|
|
|||
|
const children = route.children
|
|||
|
|
|||
|
let flag = false
|
|||
|
for (let index = 0; index < children.length; index++) {
|
|||
|
const child = children[index]
|
|||
|
if (child.children?.length) {
|
|||
|
flag = true
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
return flag
|
|||
|
}
|
|||
|
|
|||
|
// 生成二级路由
|
|||
|
const promoteRouteLevel = (route: AppRouteRecordRaw) => {
|
|||
|
let router: Router | null = createRouter({
|
|||
|
routes: [route as RouteRecordRaw],
|
|||
|
history: createWebHashHistory()
|
|||
|
})
|
|||
|
|
|||
|
const routes = router.getRoutes()
|
|||
|
addToChildren(routes, route.children || [], route)
|
|||
|
router = null
|
|||
|
|
|||
|
route.children = route.children?.map((item) => omit(item, 'children'))
|
|||
|
}
|
|||
|
|
|||
|
// 添加所有子菜单
|
|||
|
const addToChildren = (
|
|||
|
routes: RouteRecordNormalized[],
|
|||
|
children: AppRouteRecordRaw[],
|
|||
|
routeModule: AppRouteRecordRaw
|
|||
|
) => {
|
|||
|
for (let index = 0; index < children.length; index++) {
|
|||
|
const child = children[index]
|
|||
|
const route = routes.find((item) => item.name === child.name)
|
|||
|
if (!route) {
|
|||
|
continue
|
|||
|
}
|
|||
|
routeModule.children = routeModule.children || []
|
|||
|
if (!routeModule.children.find((item) => item.name === route.name)) {
|
|||
|
routeModule.children?.push(route as unknown as AppRouteRecordRaw)
|
|||
|
}
|
|||
|
if (child.children?.length) {
|
|||
|
addToChildren(routes, child.children, routeModule)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
const toCamelCase = (str: string, upperCaseFirst: boolean) => {
|
|||
|
str = (str || '').toLowerCase().replace(/-(.)/g, function (group1: string) {
|
|||
|
return group1.toUpperCase()
|
|||
|
})
|
|||
|
|
|||
|
if (upperCaseFirst && str) {
|
|||
|
str = str.charAt(0).toUpperCase() + str.slice(1)
|
|||
|
}
|
|||
|
|
|||
|
return str
|
|||
|
}
|