first commit

This commit is contained in:
2026-01-26 09:32:17 +08:00
commit 9429a700d0
2928 changed files with 30330 additions and 0 deletions
+62
View File
@@ -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;
}
+198
View File
@@ -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;