Files
yunzerwebsiteallinone/backend/src/router/index.js
T
2026-08-28 16:58:17 +08:00

310 lines
9.1 KiB
JavaScript

import { createRouter, createWebHashHistory } from "vue-router";
import { convertMenusToRoutes } from "./dynamicRoutes";
// 静态子路由:需要在 Main 框架内显示的页面
const staticMainChildren = [
// 默认首页:访问 / 时直接展示工作台,URL 保持为 /
{
path: "",
name: "Dashboard",
component: () => import("@/views/dashboard/index.vue"),
meta: { requiresAuth: true, title: "工作台" }
},
// CMS 文章/产品/解决方案是系统内置功能,不依赖数据库菜单配置。
// 统一结构:index 为列表页,type 为分类页。
{
path: "/apps/cms/analytics/content",
name: "CmsContentAnalytics",
component: () => import("@/views/apps/cms/analytics/content/index.vue"),
meta: { requiresAuth: true, title: "内容统计", modulePath: "/apps/cms" }
},
{
path: "/apps/cms/article",
name: "CmsArticles",
component: () => import("@/views/apps/cms/article/index/index.vue"),
meta: { requiresAuth: true, title: "文章管理", modulePath: "/apps/cms" }
},
{
path: "/apps/cms/article/type",
name: "CmsArticleCategories",
component: () => import("@/views/apps/cms/article/type/index.vue"),
meta: { requiresAuth: true, title: "文章分类", modulePath: "/apps/cms" }
},
{
path: "/apps/cms/products",
name: "CmsProducts",
component: () => import("@/views/apps/cms/product/index/index.vue"),
meta: { requiresAuth: true, title: "产品管理", modulePath: "/apps/cms" }
},
{
path: "/apps/cms/products/types",
name: "CmsProductCategories",
component: () => import("@/views/apps/cms/product/type/index.vue"),
meta: { requiresAuth: true, title: "产品分类", modulePath: "/apps/cms" }
},
{
path: "/apps/cms/solutions",
name: "CmsSolutions",
component: () => import("@/views/apps/cms/solution/index/index.vue"),
meta: { requiresAuth: true, title: "解决方案管理", modulePath: "/apps/cms" }
},
{
path: "/apps/cms/solutions/types",
name: "CmsSolutionCategories",
component: () => import("@/views/apps/cms/solution/type/index.vue"),
meta: { requiresAuth: true, title: "解决方案分类", modulePath: "/apps/cms" }
},
// 兼容旧路径:articles/* -> article/*
{
path: "/apps/cms/articles",
redirect: "/apps/cms/article"
},
{
path: "/apps/cms/articles/category",
redirect: "/apps/cms/article/type"
},
{
path: "/user/userProfile",
name: "userProfile",
component: () => import("@/views/user/userProfile.vue"),
meta: { requiresAuth: true, title: "用户中心" }
},
// 组织架构(组织 / 员工 / 职位)
// 进销存(erp)与办公自动化(oa)各有独立页面,但读写同一份组织数据。
{
path: "/apps/erp/organization",
name: "ErpOrganization",
component: () => import("@/views/apps/erp/organization/index.vue"),
meta: { requiresAuth: true, title: "组织架构", modulePath: "/apps/erp" }
},
{
path: "/apps/erp/employee",
name: "ErpEmployee",
component: () => import("@/views/apps/erp/employee/index.vue"),
meta: { requiresAuth: true, title: "员工管理", modulePath: "/apps/erp" }
},
{
path: "/apps/erp/position",
name: "ErpPosition",
component: () => import("@/views/apps/erp/position/index.vue"),
meta: { requiresAuth: true, title: "职位管理", modulePath: "/apps/erp" }
},
{
path: "/apps/oa/organization",
name: "Organization",
component: () => import("@/views/apps/organization/components/OrganizationPage.vue"),
props: { module: "oa" },
meta: { requiresAuth: true, title: "组织架构", modulePath: "/apps/oa" }
},
{
path: "/apps/oa/employee",
name: "Employee",
component: () => import("@/views/apps/organization/components/EmployeePage.vue"),
props: { module: "oa" },
meta: { requiresAuth: true, title: "人员管理", modulePath: "/apps/oa" }
},
{
path: "/apps/oa/position",
name: "Position",
component: () => import("@/views/apps/organization/components/PositionPage.vue"),
props: { module: "oa" },
meta: { requiresAuth: true, title: "职位管理", modulePath: "/apps/oa" }
},
{
path: "/apps/oa/schedule",
name: "OaSchedule",
component: () => import("@/views/apps/oa/schedule/index.vue"),
meta: { requiresAuth: true, title: "日程管理", modulePath: "/apps/oa" }
},
{
path: "/tools/passwordStore",
name: "BackendPasswordStore",
component: () => import("@/views/tools/passwordStore/index.vue"),
meta: { requiresAuth: true, title: "密码存储" }
},
// 兼容拼写错误的路径重定向
{
path: "/apps/erp/dashborad",
redirect: "/apps/erp/dashboard"
}
];
// 静态路由:登录页独立、home 导航门户独立、404 页面独立
const staticRoutes = [
{
path: "/login",
name: "Login",
component: () => import("@/views/login/index.vue"),
meta: { requiresAuth: false }
},
{
path: "/register",
name: "Register",
component: () => import("@/views/login/register.vue"),
meta: { requiresAuth: false }
},
{
path: "/forget",
name: "ForgetPassword",
component: () => import("@/views/login/forget.vue"),
meta: { requiresAuth: false }
},
{
path: "/home",
name: "Home",
component: () => import("@/views/home/index.vue"),
meta: { requiresAuth: true, title: "系统导航", isStandalone: true }
},
// 兼容路径拼写错误:dashborad -> dashboard
{
path: "/apps/erp/dashborad",
redirect: "/apps/erp/dashboard"
},
{
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 {
// 即使权限菜单为空,也要注册内置文章中心路由。
addDynamicRoutes([]);
dynamicRoutesAdded = true;
routesLoadingPromise = null;
return Promise.resolve();
}
} catch (error) {
console.error('加载动态路由失败:', error);
dynamicRoutesAdded = true;
routesLoadingPromise = null;
throw error;
}
})();
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"),
meta: { requiresAuth: true },
children: [...staticMainChildren, ...dynamicRoutes] // 合并静态和动态子路由
});
dynamicRoutesAdded = true;
}
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");
const publicPaths = ["/login", "/register", "/forget"];
if (publicPaths.includes(to.path)) {
if (token) {
if (!dynamicRoutesAdded) {
await loadAndAddDynamicRoutes();
}
next({ path: "/home" });
} else {
next();
}
return;
}
if (!token) {
next({ path: "/login", query: { redirect: to.path } });
return;
}
if (!dynamicRoutesAdded) {
await loadAndAddDynamicRoutes();
// 路由加载后重新导航,确保路由匹配正确
next({ path: to.path, replace: true });
return;
}
next();
});
export default router;