web-权限、文章

This commit is contained in:
2025-10-18 18:19:19 +08:00
parent b3424e554f
commit ccc1d6338b
35 changed files with 3314 additions and 463 deletions

View File

@@ -34,21 +34,73 @@ export function generateRoutes(menus: SysMenu[]): RouteRecordRaw[] {
}
const routes: RouteRecordRaw[] = [];
const pageRoutes: RouteRecordRaw[] = [];
// 构建菜单树
const menuTree = buildMenuTree(menus);
// 生成路由
menuTree.forEach(menu => {
if(menu.type === MenuType.PAGE) {
console.log(`[路由生成] 生成独立PAGE路由: ${menu.name} -> ${menu.url}`);
}
const route = generateRouteFromMenu(menu);
if (route) {
routes.push(route);
// 递归提取所有 PAGE 类型的子菜单
extractPageChildren(route, pageRoutes);
}
});
// 将 PAGE 类型的路由添加到路由列表
routes.push(...pageRoutes);
return routes;
}
/**
* 递归提取路由中的 PAGE 类型子菜单
*/
function extractPageChildren(route: any, pageRoutes: RouteRecordRaw[]) {
// 检查当前路由是否有 PAGE 类型的子菜单
if (route.meta?.pageChildren && Array.isArray(route.meta.pageChildren)) {
console.log(`[路由生成] 父路由 ${route.name} 包含 ${route.meta.pageChildren.length} 个PAGE子菜单`);
route.meta.pageChildren.forEach((pageMenu: SysMenu) => {
console.log(`[路由生成] 开始生成PAGE路由:`, {
name: pageMenu.name,
menuID: pageMenu.menuID,
url: pageMenu.url,
component: pageMenu.component,
layout: (pageMenu as any).layout,
type: pageMenu.type
});
const pageRoute = generateRouteFromMenu(pageMenu, true); // 作为顶层路由生成
if (pageRoute) {
console.log(`[路由生成] 生成独立PAGE路由成功:`, {
name: pageMenu.name,
path: pageRoute.path,
component: pageRoute.component,
hasChildren: !!pageRoute.children
});
pageRoutes.push(pageRoute);
} else {
console.error(`[路由生成] 生成独立PAGE路由失败: ${pageMenu.name}`);
}
});
// 清理临时数据
delete route.meta.pageChildren;
}
// 递归检查子路由
if (route.children && Array.isArray(route.children)) {
route.children.forEach((childRoute: any) => {
extractPageChildren(childRoute, pageRoutes);
});
}
}
/**
* 根据单个菜单生成路由
* @param menu 菜单对象
@@ -101,9 +153,11 @@ function generateRouteFromMenu(menu: SysMenu, isTopLevel = true): RouteRecordRaw
} else {
// 没有子菜单,也没有指定布局,使用具体的页面组件
if (menu.component) {
console.log(`[路由生成] 加载组件: ${menu.name} -> ${menu.component}`);
route.component = getComponent(menu.component);
} else {
// 非顶层菜单没有组件时,使用简单的占位组件
console.log(`[路由生成] ${menu.name} 没有组件使用BlankLayout`);
route.component = getComponent('BlankLayout');
}
}
@@ -120,16 +174,39 @@ function generateRouteFromMenu(menu: SysMenu, isTopLevel = true): RouteRecordRaw
} else if (hasChildren) {
// 处理有子菜单的情况
route.children = [];
// 分离 PAGE 类型的子菜单和普通子菜单
const pageChildren: SysMenu[] = [];
const normalChildren: SysMenu[] = [];
menu.children!.forEach(child => {
if (child.type === MenuType.PAGE) {
// PAGE 类型的菜单作为独立路由,不作为子路由
console.log(`[路由生成] 发现PAGE类型子菜单: ${child.name} (${child.menuID}), 父菜单: ${menu.name}`);
pageChildren.push(child);
} else {
normalChildren.push(child);
}
});
// 只将普通子菜单加入 children
normalChildren.forEach(child => {
const childRoute = generateRouteFromMenu(child, false);
if (childRoute) {
route.children!.push(childRoute);
}
});
// PAGE 类型的菜单需要在外层单独处理(不管是哪一层的菜单)
if (pageChildren.length > 0) {
// 将 PAGE 类型的子菜单保存到路由的 meta 中,稍后在外层生成
console.log(`[路由生成] 保存 ${pageChildren.length} 个PAGE子菜单到 ${menu.name} 的meta中`);
route.meta.pageChildren = pageChildren;
}
// 如果没有设置重定向自动重定向到第一个有URL的子菜单
if (!route.redirect && route.children.length > 0) {
const firstChildWithUrl = findFirstMenuWithUrl(menu.children!);
const firstChildWithUrl = findFirstMenuWithUrl(normalChildren);
if (firstChildWithUrl?.url) {
route.redirect = firstChildWithUrl.url;
}
@@ -193,33 +270,39 @@ function getComponent(componentName: string) {
componentPath += '.vue';
}
console.log(`[路由生成] 组件路径转换: ${componentName} -> ${componentPath}`);
// 动态导入组件
return () => {
try {
// 使用动态导入Vite 会自动处理路径解析
return import(/* @vite-ignore */ componentPath);
} catch (error) {
console.warn(`组件加载失败: ${componentPath}`, error);
// 返回404组件
return import('@/views/error/404.vue').catch(() =>
Promise.resolve({
template: `<div class="component-error">
<h3>组件加载失败</h3>
<p>无法加载组件: ${componentPath}</p>
<p>错误: ${error instanceof Error ? error.message : String(error)}</p>
</div>`,
style: `
.component-error {
padding: 20px;
text-align: center;
color: #f56565;
background: #fed7d7;
border-radius: 4px;
}
`
})
);
}
console.log(`[组件加载] 开始加载: ${componentPath}`);
return import(/* @vite-ignore */ componentPath)
.then(module => {
console.log(`[组件加载] 成功: ${componentPath}`, module);
return module;
})
.catch(error => {
console.error(`[组件加载] 失败: ${componentPath}`, error);
// 返回404组件
return import('@/views/error/404.vue').catch(() =>
Promise.resolve({
template: `<div class="component-error">
<h3>组件加载失败</h3>
<p>无法加载组件: ${componentPath}</p>
<p>原始组件名: ${componentName}</p>
<p>错误: ${error instanceof Error ? error.message : String(error)}</p>
</div>`,
style: `
.component-error {
padding: 20px;
text-align: center;
color: #f56565;
background: #fed7d7;
border-radius: 4px;
}
`
})
);
});
};
}