|
|
import { listToTree } from '@/utils/tree-utils'
|
|
|
import { firstUpperCase } from '@/utils/str-utils'
|
|
|
import type { SysMenuRouterVO } from '@/api/system/menu/types'
|
|
|
import type { RouteMeta, RouteRecordRaw } from 'vue-router'
|
|
|
import { buildNotFoundRoute, ExceptionComponentImport } from '@/router/constant-routes'
|
|
|
|
|
|
type SysMenuRouterTree = SysMenuRouterVO & { key: number; children: SysMenuRouterTree[] }
|
|
|
|
|
|
// 动态组件模块
|
|
|
const dynamicViewModules = import.meta.glob('/src/views/**/*.{vue,tsx}')
|
|
|
|
|
|
// 根路由
|
|
|
const HOME_ROUTE: RouteRecordRaw = {
|
|
|
path: '/',
|
|
|
name: '/',
|
|
|
component: () => import('@/layouts/BasicLayout.vue'),
|
|
|
meta: {
|
|
|
keepAlive: false
|
|
|
},
|
|
|
children: []
|
|
|
}
|
|
|
|
|
|
export const generatorDynamicRouter = (userMenus: SysMenuRouterVO[]): RouteRecordRaw => {
|
|
|
const routes: RouteRecordRaw = { ...HOME_ROUTE }
|
|
|
// 后端数据, 根级树数组, 根级 PID
|
|
|
const menuTree = listToTree(userMenus, 0) as SysMenuRouterTree[]
|
|
|
|
|
|
routes.children = menuToRoutes(menuTree)
|
|
|
console.log('routes', routes)
|
|
|
routes.children.push(buildNotFoundRoute('PageNotFound'))
|
|
|
fillRedirect(routes)
|
|
|
|
|
|
return routes
|
|
|
}
|
|
|
|
|
|
const menuToRoutes = (menuTree: SysMenuRouterTree[], parent?: RouteRecordRaw) => {
|
|
|
return menuTree.map(item => {
|
|
|
// 内容打开方式
|
|
|
const targetType = item.targetType
|
|
|
let path = `${(parent && parent.path) || ''}/${item.path}`
|
|
|
|
|
|
// 路由名称,由路由地址生成,大驼峰形式
|
|
|
const name = path
|
|
|
.replace('-', '/')
|
|
|
.split('/')
|
|
|
.filter(x => x && x !== '')
|
|
|
.map(x => firstUpperCase(x))
|
|
|
.join('')
|
|
|
|
|
|
const meta: RouteMeta = {
|
|
|
name: item.title,
|
|
|
icon: item.icon || undefined,
|
|
|
targetType: targetType
|
|
|
}
|
|
|
|
|
|
let component
|
|
|
switch (item.type) {
|
|
|
case 0:
|
|
|
// 目录类型组件, 固定使用 RouterLayout
|
|
|
component = () => import('@/layouts/RouterLayout.vue')
|
|
|
break
|
|
|
case 1:
|
|
|
// 菜单类型需要拼接组件地址
|
|
|
if (targetType === 1) {
|
|
|
// 内置组件
|
|
|
item.uri && (component = getComponent(item.uri))
|
|
|
} else if (targetType === 2) {
|
|
|
// 内嵌iframe
|
|
|
meta.target = item.uri
|
|
|
component = () => import('@/views/basic/iframe/index.vue')
|
|
|
} else if (targetType === 3) {
|
|
|
// 外链
|
|
|
path = item.uri
|
|
|
meta.target = item.uri
|
|
|
meta.hideInTab = true
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 是否设置了隐藏菜单
|
|
|
if (item.hidden === 1) {
|
|
|
meta.hideInMenu = true
|
|
|
}
|
|
|
|
|
|
// @ts-ignore
|
|
|
const currentRouter: RouteRecordRaw = {
|
|
|
// 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
|
|
|
path,
|
|
|
// 路由名称,建议唯一
|
|
|
name,
|
|
|
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
|
|
|
meta,
|
|
|
// 组件
|
|
|
component
|
|
|
}
|
|
|
|
|
|
// 有子菜单则递归处理
|
|
|
if (item.children && item.children.length > 0) {
|
|
|
// 给子节点添加一个默认的 404 页面,以便在 content 中显示 404
|
|
|
currentRouter.children = menuToRoutes(item.children, currentRouter)
|
|
|
fillRedirect(currentRouter)
|
|
|
}
|
|
|
return currentRouter
|
|
|
})
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 设置当前路由的默认跳转地址为其子路由的path
|
|
|
* @param currentRouter
|
|
|
*/
|
|
|
function fillRedirect(currentRouter: RouteRecordRaw) {
|
|
|
if (!currentRouter.children) return
|
|
|
const redirectRouter = currentRouter.children.find(x => !x.meta?.hideInMenu)
|
|
|
redirectRouter && (currentRouter.redirect = redirectRouter.path)
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 动态获取组件
|
|
|
* @param componentPath 组件地址
|
|
|
*/
|
|
|
const getComponent = function (componentPath: string) {
|
|
|
// 如果有后缀,直接返回
|
|
|
const isFullPath = componentPath.endsWith('.vue') || componentPath.endsWith('.tsx')
|
|
|
if (isFullPath) {
|
|
|
return dynamicViewModules[componentPath]
|
|
|
}
|
|
|
|
|
|
// 没有后缀的情况下,按顺序尝试加载
|
|
|
let viewModule = dynamicViewModules[`/src/views/${componentPath}.vue`]
|
|
|
if (!viewModule) {
|
|
|
viewModule = dynamicViewModules[`/src/views/${componentPath}/index.vue`]
|
|
|
}
|
|
|
if (!viewModule) {
|
|
|
viewModule = dynamicViewModules[`/src/views/${componentPath}.tsx`]
|
|
|
}
|
|
|
if (!viewModule) {
|
|
|
viewModule = dynamicViewModules[`/src/views/${componentPath}/index.tsx`]
|
|
|
}
|
|
|
if (!viewModule) {
|
|
|
import.meta.env.DEV &&
|
|
|
console.warn(
|
|
|
'在src/views/下找不到`' +
|
|
|
componentPath +
|
|
|
'.vue` 或 `' +
|
|
|
componentPath +
|
|
|
'.tsx`, 请自行创建!'
|
|
|
)
|
|
|
viewModule = ExceptionComponentImport
|
|
|
}
|
|
|
|
|
|
return viewModule
|
|
|
}
|