130 lines
5.3 KiB
JavaScript
130 lines
5.3 KiB
JavaScript
import { createComponentLoader } from '@/utils/pathResolver';
|
||
|
||
// 工具函数:将扁平菜单转换为嵌套路由
|
||
export function convertMenusToRoutes(menus) {
|
||
// 1. 构建父子关系映射
|
||
const menuMap = {};
|
||
const routes = [];
|
||
|
||
// 先将所有菜单存入映射表
|
||
menus.forEach(menu => {
|
||
// 处理路径:去掉前导斜杠,因为是子路由
|
||
let routePath = menu.path.startsWith('/') ? menu.path.substring(1) : menu.path;
|
||
|
||
menuMap[menu.id] = {
|
||
path: routePath,
|
||
name: `menu_${menu.id}`, // 使用唯一名称
|
||
meta: {
|
||
icon: menu.icon,
|
||
title: menu.name,
|
||
id: menu.id,
|
||
parentId: menu.parentId,
|
||
menuPath: menu.path // 保存原始路径(完整路径,如 /dashboard)
|
||
},
|
||
// 有组件路径才添加 component(目录菜单可能没有)
|
||
// componentPath 格式: /dashboard/index.vue (来自数据库)
|
||
// 使用通用工具自动转换为别名路径并加载
|
||
...(menu.componentPath
|
||
? {
|
||
component: createComponentLoader(menu.componentPath)
|
||
}
|
||
: {})
|
||
};
|
||
});
|
||
|
||
// 2. 构建嵌套关系,并修正子路由路径
|
||
menus.forEach(menu => {
|
||
const currentRoute = menuMap[menu.id];
|
||
if (menu.parentId === 0) {
|
||
// 顶级菜单直接加入路由
|
||
routes.push(currentRoute);
|
||
} else {
|
||
// 子菜单添加到父菜单的 children 中
|
||
const parentRoute = menuMap[menu.parentId];
|
||
if (parentRoute) {
|
||
// 修正子路由路径:使用原始菜单路径来准确匹配
|
||
// 使用原始路径(menuPath)来准确计算相对路径
|
||
const parentOriginalPath = parentRoute.meta?.menuPath || parentRoute.path;
|
||
const childOriginalPath = currentRoute.meta?.menuPath || currentRoute.path;
|
||
|
||
// 确保路径格式一致(去掉前导斜杠)
|
||
const parentPath = parentOriginalPath.startsWith('/') ? parentOriginalPath.substring(1) : parentOriginalPath;
|
||
const childFullPath = childOriginalPath.startsWith('/') ? childOriginalPath.substring(1) : childOriginalPath;
|
||
|
||
// 如果子路径以父路径开头,则只保留剩余部分
|
||
if (childFullPath.startsWith(parentPath + '/')) {
|
||
currentRoute.path = childFullPath.substring(parentPath.length + 1); // "users" 或 "knowledge/category"
|
||
} else {
|
||
// 如果子路径不以父路径开头,尝试直接使用子路径的最后一部分
|
||
// 这处理了路径不连续的情况
|
||
const pathParts = childFullPath.split('/');
|
||
const parentPathParts = parentPath.split('/');
|
||
// 找到子路径相对于父路径的部分
|
||
if (pathParts.length > parentPathParts.length) {
|
||
currentRoute.path = pathParts.slice(parentPathParts.length).join('/');
|
||
} else {
|
||
// 如果无法确定相对路径,使用最后一个路径段
|
||
currentRoute.path = pathParts[pathParts.length - 1];
|
||
}
|
||
}
|
||
|
||
// 特殊处理:知识库的编辑和详情页需要支持动态参数
|
||
// 如果最终路径是 edit 或 detail,转换为支持参数的路由
|
||
if (currentRoute.path === 'edit') {
|
||
currentRoute.path = 'edit/:id';
|
||
} else if (currentRoute.path === 'detail') {
|
||
currentRoute.path = 'detail/:id';
|
||
}
|
||
|
||
if (!parentRoute.children) {
|
||
parentRoute.children = [];
|
||
}
|
||
parentRoute.children.push(currentRoute);
|
||
} else {
|
||
// 如果找不到父路由,作为顶级路由处理
|
||
routes.push(currentRoute);
|
||
}
|
||
}
|
||
});
|
||
|
||
// 2.5. 为有子路由但没有 component 的父路由自动添加默认组件
|
||
const addDefaultComponent = (routeList) => {
|
||
routeList.forEach(route => {
|
||
// 如果路由有子路由但没有组件,尝试添加默认组件
|
||
if (route.children && route.children.length > 0 && !route.component) {
|
||
// 使用原始菜单路径(menuPath)来推断组件路径,而不是修正后的路径
|
||
const originalPath = route.meta?.menuPath || route.path;
|
||
// 确保路径以 / 开头
|
||
const normalizedPath = originalPath.startsWith('/') ? originalPath : `/${originalPath}`;
|
||
const defaultComponentPath = `${normalizedPath}/index.vue`;
|
||
const defaultComponent = createComponentLoader(defaultComponentPath);
|
||
if (defaultComponent) {
|
||
route.component = defaultComponent;
|
||
}
|
||
}
|
||
// 递归处理子路由
|
||
if (route.children && route.children.length > 0) {
|
||
addDefaultComponent(route.children);
|
||
}
|
||
});
|
||
};
|
||
addDefaultComponent(routes);
|
||
|
||
// 3. 按 order 排序
|
||
const sortRoutes = (routesList) => {
|
||
routesList.sort((a, b) => {
|
||
const orderA = menus.find(m => m.id === a.meta.id)?.order || 0;
|
||
const orderB = menus.find(m => m.id === b.meta.id)?.order || 0;
|
||
return orderA - orderB;
|
||
});
|
||
routesList.forEach(route => {
|
||
if (route.children && route.children.length > 0) {
|
||
sortRoutes(route.children);
|
||
}
|
||
});
|
||
};
|
||
|
||
sortRoutes(routes);
|
||
|
||
return routes;
|
||
} |