first commit
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import { createComponentLoader } from '@/utils/pathResolver';
|
||||
|
||||
// 递归转换嵌套菜单为嵌套路由
|
||||
export function convertMenusToRoutes(menus) {
|
||||
if (!menus || menus.length === 0) return [];
|
||||
|
||||
return menus.map(menu => {
|
||||
// 基础路由配置
|
||||
const route = {
|
||||
path: menu.path || '', // 优先使用菜单path,无则为空字符串
|
||||
name: `menu_${menu.id}`,
|
||||
meta: {
|
||||
icon: menu.icon,
|
||||
title: menu.title,
|
||||
id: menu.id,
|
||||
parentId: menu.pid,
|
||||
menuPath: menu.path,
|
||||
componentPath: menu.component_path
|
||||
}
|
||||
};
|
||||
|
||||
// 处理组件路径
|
||||
if (menu.type === 4) {
|
||||
// 单页类型:使用单页组件,根据路由从单页表获取内容
|
||||
route.component = () => import('@/views/onepage/index.vue');
|
||||
route.meta.menuType = 4; // 标记为单页类型
|
||||
} else if (menu.component_path) {
|
||||
// 页面类型:直接使用component_path加载组件
|
||||
route.component = createComponentLoader(menu.component_path);
|
||||
} else if (menu.children && menu.children.length > 0) {
|
||||
// 无组件但有子路由的父菜单,添加默认空组件(避免路由报错)
|
||||
route.component = () => import('@/views/layouts/EmptyLayout.vue');
|
||||
// 为父菜单添加重定向,指向第一个有效子路由
|
||||
const firstValidChild = findFirstValidRoute(menu.children);
|
||||
if (firstValidChild && firstValidChild.path) {
|
||||
route.redirect = firstValidChild.path;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理子菜单为子路由
|
||||
if (menu.children && menu.children.length > 0) {
|
||||
route.children = convertMenusToRoutes(menu.children);
|
||||
}
|
||||
|
||||
return route;
|
||||
});
|
||||
}
|
||||
|
||||
// 查找第一个有效的子路由
|
||||
function findFirstValidRoute(children) {
|
||||
if (!children || children.length === 0) return null;
|
||||
for (const child of children) {
|
||||
if (child.path && (child.component_path || (child.children && child.children.length > 0))) {
|
||||
return child;
|
||||
}
|
||||
if (child.children && child.children.length > 0) {
|
||||
const grandChild = findFirstValidRoute(child.children);
|
||||
if (grandChild) return grandChild;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import { convertMenusToRoutes } from "./dynamicRoutes";
|
||||
|
||||
// 静态路由:登录页独立,404 页面独立
|
||||
const staticRoutes = [
|
||||
{
|
||||
path: "/login",
|
||||
name: "Login",
|
||||
component: () => import("@/views/login/index.vue"),
|
||||
meta: { requiresAuth: false }
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: "Main",
|
||||
component: () => import("@/views/Main.vue"),
|
||||
redirect: "/dashboard",
|
||||
children: [
|
||||
{
|
||||
path: "dashboard",
|
||||
name: "Dashboard",
|
||||
component: () => import("@/views/dashboard/index.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "仪表盘",
|
||||
icon: "fa-solid fa-gauge",
|
||||
id: 1,
|
||||
isStatic: true
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
name: "NotFound",
|
||||
component: () => import("@/views/404/404.vue"),
|
||||
meta: { requiresAuth: false }
|
||||
}
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: staticRoutes
|
||||
});
|
||||
|
||||
let dynamicRoutesAdded = false;
|
||||
let dynamicRoutesData = [];
|
||||
let routesLoadingPromise = null;
|
||||
|
||||
export function resetDynamicRoutes() {
|
||||
dynamicRoutesAdded = false;
|
||||
routesLoadingPromise = null;
|
||||
}
|
||||
|
||||
export async function loadAndAddDynamicRoutes() {
|
||||
if (routesLoadingPromise) {
|
||||
return routesLoadingPromise;
|
||||
}
|
||||
|
||||
if (dynamicRoutesAdded) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
routesLoadingPromise = (async () => {
|
||||
try {
|
||||
const { useMenuStore } = await import("@/stores/menu");
|
||||
const menuStore = useMenuStore();
|
||||
const menuData = await menuStore.fetchMenus();
|
||||
|
||||
if (menuData && menuData.length > 0) {
|
||||
addDynamicRoutes(menuData);
|
||||
dynamicRoutesAdded = true;
|
||||
routesLoadingPromise = null;
|
||||
return Promise.resolve();
|
||||
} else {
|
||||
dynamicRoutesAdded = true;
|
||||
routesLoadingPromise = null;
|
||||
return Promise.resolve();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载动态路由失败:', error);
|
||||
dynamicRoutesAdded = true;
|
||||
routesLoadingPromise = null;
|
||||
return Promise.resolve();
|
||||
}
|
||||
})();
|
||||
|
||||
return routesLoadingPromise;
|
||||
}
|
||||
|
||||
// 核心修改:移除扁平化,直接使用嵌套菜单生成路由
|
||||
function addDynamicRoutes(menus) {
|
||||
if (!menus?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 直接转换嵌套菜单为嵌套路由(不再扁平化)
|
||||
const dynamicRoutes = convertMenusToRoutes(menus);
|
||||
|
||||
if (router.hasRoute('Main')) {
|
||||
router.removeRoute('Main');
|
||||
}
|
||||
|
||||
// 重新添加主路由
|
||||
router.addRoute({
|
||||
path: "/",
|
||||
name: "Main",
|
||||
component: () => import("@/views/Main.vue"),
|
||||
redirect: "/dashboard",
|
||||
meta: { requiresAuth: true },
|
||||
children: [
|
||||
{
|
||||
path: "dashboard",
|
||||
name: "Dashboard",
|
||||
component: () => import("@/views/dashboard/index.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
title: "仪表盘",
|
||||
icon: "fa-solid fa-gauge",
|
||||
id: 1,
|
||||
isStatic: true
|
||||
}
|
||||
},
|
||||
...dynamicRoutes // 嵌套路由直接加入 children
|
||||
]
|
||||
});
|
||||
|
||||
dynamicRoutesAdded = true;
|
||||
// console.log('动态路由添加完成:', router.getRoutes().map(r => r.path));
|
||||
}
|
||||
|
||||
function findRouteByName(routes, routeName) {
|
||||
for (const route of routes) {
|
||||
if (route.name === routeName) {
|
||||
return route;
|
||||
}
|
||||
if (route.children) {
|
||||
const found = findRouteByName(route.children, routeName);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function findFirstValidRoute(routes) {
|
||||
for (const route of routes) {
|
||||
if (route.component) {
|
||||
return route;
|
||||
}
|
||||
if (route.children && route.children.length > 0) {
|
||||
const childRoute = findFirstValidRoute(route.children);
|
||||
if (childRoute) {
|
||||
return childRoute;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
if (to.path === "/login") {
|
||||
if (token) {
|
||||
if (!dynamicRoutesAdded) {
|
||||
await loadAndAddDynamicRoutes();
|
||||
}
|
||||
next({ path: "/" });
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
next({ path: "/login", query: { redirect: to.path } });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dynamicRoutesAdded) {
|
||||
await loadAndAddDynamicRoutes();
|
||||
// 路由加载后重新导航,确保路由匹配正确
|
||||
next({ path: to.path, replace: true });
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果匹配不到路由,跳转到404
|
||||
if (to.matched.length === 0) {
|
||||
next({ name: "NotFound" });
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user