This commit is contained in:
ygx
2026-03-21 15:52:38 +08:00
parent 090b00002e
commit 4708a8bbf7
36 changed files with 7280 additions and 28 deletions

View File

@@ -1,4 +1,4 @@
import { DEFAULT_LAYOUT } from './routes/base'
import { DEFAULT_LAYOUT, STANDALONE_LAYOUT } from './routes/base'
import type { AppRouteRecordRaw } from './routes/types'
import type { TreeNodeBase } from '@/utils/tree'
@@ -23,6 +23,8 @@ export interface ServerMenuItem extends TreeNodeBase {
requiresAuth?: boolean
roles?: string[]
children?: ServerMenuItem[]
is_full?: boolean // 是否为独立页面(不包含菜单栏)
hide_menu?: boolean // 是否隐藏菜单栏
[key: string]: any
}
@@ -74,42 +76,60 @@ export function transformMenuToRoutes(menuItems: ServerMenuItem[]): AppRouteReco
const routes: AppRouteRecordRaw[] = []
for (const item of menuItems) {
// 根据 is_full 决定如何设置 component
let routeComponent: AppRouteRecordRaw['component']
if (item.is_full) {
// 独立页面:直接加载视图组件,不使用布局
if (item.component) {
routeComponent = loadViewComponent(item.component)
} else {
// 如果没有 component使用默认重定向页面
routeComponent = () => import('@/views/redirect/index.vue')
}
} else {
// 非独立页面:使用默认布局
routeComponent = DEFAULT_LAYOUT
}
const route: AppRouteRecordRaw = {
path: item.menu_path || '',
name: item.title || item.name || `menu_${item.id}`,
component: routeComponent,
meta: {
// ...item,
locale: item.locale || item.title,
requiresAuth: item.requiresAuth !== false,
icon: item.icon || item?.menu_icon,
order: item.sort_key ?? item.order,
hideInMenu: item.hideInMenu,
hideInMenu: item.hideInMenu || item.hide_menu,
hideChildrenInMenu: item.hideChildrenInMenu,
roles: item.roles,
isNewTab: item.is_new_tab,
},
component: DEFAULT_LAYOUT,
}
// 处理子菜单
if (item.children && item.children.length > 0) {
// 传递父级的 component 和 path 给子路由处理函数
route.children = transformChildRoutes(item.children, item.component, item.menu_path)
} else if (item.component) {
// 一级菜单没有 children 但有 component创建一个空路径的子路由
const routeName = route.name
route.children = [
{
path: item.menu_path || '',
name: typeof routeName === 'string' ? `${routeName}Index` : `menu_${item.id}_index`,
component: loadViewComponent(item.component),
meta: {
locale: item.locale || item.title,
requiresAuth: item.requiresAuth !== false,
isNewTab: item.is_new_tab,
// 非独立页面需要处理子菜单
if (!item.is_full) {
if (item.children && item.children.length > 0) {
// 传递父级的 component 和 path 给子路由处理函数
route.children = transformChildRoutes(item.children, item.component, item.menu_path, false)
} else if (item.component) {
// 一级菜单没有 children 但有 component创建一个空路径的子路由
const routeName = route.name
route.children = [
{
path: item.menu_path || '',
name: typeof routeName === 'string' ? `${routeName}Index` : `menu_${item.id}_index`,
component: loadViewComponent(item.component),
meta: {
locale: item.locale || item.title,
requiresAuth: item.requiresAuth !== false,
isNewTab: item.is_new_tab,
},
},
},
]
]
}
}
routes.push(route)
@@ -158,12 +178,14 @@ function extractRelativePath(childPath: string, parentPath: string): string {
* @param children 子菜单项
* @param parentComponent 父级菜单的 component 字段(用于子菜单没有 component 时继承)
* @param parentPath 父级菜单的路径(用于计算相对路径)
* @param parentIsFull 父级菜单的 is_full 字段
* @returns 子路由配置数组
*/
function transformChildRoutes(
children: ServerMenuItem[],
parentComponent?: string,
parentPath?: string
parentPath?: string,
parentIsFull?: boolean
): AppRouteRecordRaw[] {
return children.map((child) => {
// 优先使用子菜单自己的 component否则继承父级的 component
@@ -180,6 +202,7 @@ function transformChildRoutes(
locale: child.locale || child.title,
requiresAuth: child.requiresAuth !== false,
roles: child.roles,
hideInMenu: child.hideInMenu || child.hide_menu,
},
component: componentPath
? loadViewComponent(componentPath)
@@ -191,7 +214,8 @@ function transformChildRoutes(
route.children = transformChildRoutes(
child.children,
child.component || parentComponent,
childFullPath // 传递当前子菜单的完整路径作为下一层的父路径
childFullPath, // 传递当前子菜单的完整路径作为下一层的父路径
child.is_full || parentIsFull // 传递 is_full 标志
)
}

View File

@@ -2,6 +2,7 @@ import { REDIRECT_ROUTE_NAME } from '@/router/constants'
import type { RouteRecordRaw } from 'vue-router'
export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue')
export const STANDALONE_LAYOUT = () => import('@/layout/standalone-layout.vue')
export const REDIRECT_MAIN: RouteRecordRaw = {
path: '/redirect',

View File

@@ -73,6 +73,16 @@ const OPS: AppRouteRecordRaw = {
roles: ['*'],
},
},
{
path: 'report/history',
name: 'ReportHistory',
component: () => import('@/views/ops/pages/report/history/index.vue'),
meta: {
locale: 'menu.ops.report.history',
requiresAuth: true,
roles: ['*'],
},
},
],
}

View File

@@ -0,0 +1,45 @@
import { DEFAULT_LAYOUT } from '../base'
import { AppRouteRecordRaw } from '../types'
const REMOTE: AppRouteRecordRaw = {
path: '/dc',
name: 'DC',
component: DEFAULT_LAYOUT,
meta: {
locale: 'menu.dc',
requiresAuth: true,
icon: 'icon-desktop',
order: 99,
hideInMenu: true,
},
children: [
{
path: 'detail',
name: 'DCDetail',
component: () => import('@/views/ops/pages/dc/detail/index.vue'),
meta: {
locale: 'menu.dc.detail',
requiresAuth: true,
roles: ['*'],
hideInMenu: true,
is_full: true,
isNewTab: true,
},
},
{
path: 'remote',
name: 'DCRemote',
component: () => import('@/views/ops/pages/dc/remote/index.vue'),
meta: {
locale: 'menu.dc.remote',
requiresAuth: true,
roles: ['*'],
hideInMenu: true,
is_full: true,
isNewTab: true,
},
},
],
}
export default REMOTE