yunzer_go/pc/src/router/dynamicRoutes.js
2025-11-02 23:53:41 +08:00

130 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}