feat: 架构调整
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Vite import.meta.glob 动态收集所有页面组件
|
||||
*
|
||||
* TS 不理解 import.meta.glob 的 glob 模式语法(含 **),
|
||||
* 所以整个文件禁用类型检查。
|
||||
* Vite 在构建时会正确处理 glob 模式并生成对应的动态 import。
|
||||
*
|
||||
* 返回格式: { '/src/pages/dashboard/index.tsx': () => Promise<Module> }
|
||||
* 新增页面只需在 src/pages/ 下创建目录和 index.tsx,无需修改映射表。
|
||||
*/
|
||||
const pageModules = import.meta.glob('/src/pages/**/index.tsx');
|
||||
|
||||
export { pageModules };
|
||||
@@ -0,0 +1,30 @@
|
||||
import router from '@/router';
|
||||
import { auth, handleRouteGuard } from '@/hooks/useAuth';
|
||||
|
||||
/**
|
||||
* 全局路由守卫
|
||||
* 核心流程:
|
||||
* 1. 白名单(/login 等) → 直接放行
|
||||
* 2. 已登录 → 加载菜单权限(首次) → 放行
|
||||
* 3. 未登录 → 跳转 /login
|
||||
* 4. 页面标题自动设置
|
||||
*/
|
||||
router.beforeEach(async (to, _from, next) => {
|
||||
// 设置页面标题
|
||||
const appTitle = import.meta.env.VITE_APP_TITLE || 'CPMS 运营平台';
|
||||
document.title = to.meta.title ? `${to.meta.title} - ${appTitle}` : appTitle;
|
||||
|
||||
const { allow, redirect } = await handleRouteGuard(to.path);
|
||||
|
||||
if (allow) {
|
||||
// 已登录访问 /login → 跳首页
|
||||
if (to.path === '/login' && auth.isLoggedIn()) {
|
||||
const { homePath } = await import('@/stores/menuStore').then((m) => m.useMenuStore());
|
||||
next(homePath.value);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
} else {
|
||||
next(redirect || '/login');
|
||||
}
|
||||
});
|
||||
+20
-28
@@ -1,40 +1,32 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import { routes } from './routes';
|
||||
import { auth } from '@/hooks/useAuth';
|
||||
import { constantRoutes } from './routes';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.VITE_BASE_URL || '/'),
|
||||
routes,
|
||||
routes: constantRoutes,
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
});
|
||||
|
||||
// 路由守卫:登录鉴权 + 页面标题
|
||||
router.beforeEach((to, _from, next) => {
|
||||
const isLoggedIn = auth.isLoggedIn();
|
||||
/**
|
||||
* 路由白名单:不需要登录即可访问的路径
|
||||
*/
|
||||
const WHITE_LIST = ['/login', '/404'];
|
||||
|
||||
// 设置页面标题
|
||||
const appTitle = import.meta.env.VITE_APP_TITLE || 'CPMS 运营平台';
|
||||
document.title = to.meta.title ? `${to.meta.title} - ${appTitle}` : appTitle;
|
||||
/**
|
||||
* 重置路由:清除动态添加的子路由,恢复到只包含静态路由的状态
|
||||
* 用于退出登录时清理
|
||||
*/
|
||||
export function resetRouter() {
|
||||
const currentRoutes = router.getRoutes();
|
||||
const protectedNames = new Set(['Login', 'BasicLayout', 'NotFound']);
|
||||
|
||||
// 如果访问的是登录页
|
||||
if (to.path === '/login') {
|
||||
if (isLoggedIn) {
|
||||
// 已登录则重定向到首页
|
||||
next('/dashboard');
|
||||
} else {
|
||||
// 未登录则允许访问登录页
|
||||
next();
|
||||
currentRoutes.forEach((route) => {
|
||||
// 保留静态路由(Login, BasicLayout, NotFound),移除动态添加的子路由
|
||||
if (!protectedNames.has(route.name as string)) {
|
||||
router.removeRoute(route.name!);
|
||||
}
|
||||
} else {
|
||||
// 访问其他页面
|
||||
if (isLoggedIn) {
|
||||
// 已登录则允许访问
|
||||
next();
|
||||
} else {
|
||||
// 未登录则重定向到登录页
|
||||
next('/login');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default router;
|
||||
export { WHITE_LIST };
|
||||
|
||||
+12
-44
@@ -1,34 +1,13 @@
|
||||
import type { RouteRecordRaw } from 'vue-router';
|
||||
import { createRoute } from './utils';
|
||||
|
||||
/**
|
||||
* ====== 业务路由模块 ======
|
||||
* 实际项目中按模块拆分,如 eventsRoutes、walletRoutes 等,
|
||||
* 然后在下方 routes 数组的布局 children 中引入。
|
||||
* 参考示例(嵌套路由):
|
||||
*
|
||||
* const eventsRoutes = {
|
||||
* path: '/events',
|
||||
* name: 'EventsModule',
|
||||
* redirect: '/events/list',
|
||||
* meta: { title: '我的赛事', activeMenu: '/events' },
|
||||
* children: [
|
||||
* createRoute('list', () => import('@/pages/events/list'), { title: '赛事列表' }),
|
||||
* {
|
||||
* path: 'bracket',
|
||||
* name: 'BracketPage',
|
||||
* component: () => import('@/pages/events/bracket'),
|
||||
* meta: { title: '赛事详情' },
|
||||
* children: [
|
||||
* createRoute('player-management', () => import('@/pages/events/bracket/player-management'), { title: '选手管理' }),
|
||||
* createRoute('match-result', () => import('@/pages/events/bracket/match-result'), { title: '比赛结果' }),
|
||||
* ],
|
||||
* },
|
||||
* ],
|
||||
* };
|
||||
* ====== 静态路由(不需要后端下发) ======
|
||||
* 登录页、404 等不依赖权限的页面写在这里。
|
||||
* 动态路由由 menuStore 在登录后通过 router.addRoute 注入,
|
||||
* 后端下发 MenuNode 包含 component 字段,
|
||||
* 前端通过 import.meta.glob 动态匹配 src/pages 下的组件。
|
||||
*/
|
||||
|
||||
export const routes: RouteRecordRaw[] = [
|
||||
export const constantRoutes: RouteRecordRaw[] = [
|
||||
// ── 登录页(不经过布局)──
|
||||
{
|
||||
path: '/login',
|
||||
@@ -37,32 +16,21 @@ export const routes: RouteRecordRaw[] = [
|
||||
meta: { title: '登录' },
|
||||
},
|
||||
|
||||
// ── 主布局(需要登录)──
|
||||
// ── 主布局(动态路由的父容器) ──
|
||||
// name 为 'BasicLayout',动态路由通过 router.addRoute('BasicLayout', child) 挂载到此布局下
|
||||
{
|
||||
path: '/',
|
||||
name: 'BasicLayout',
|
||||
component: () => import('@/layouts/BasicLayout'),
|
||||
children: [
|
||||
// 业务模块路由在此添加,例如:eventsRoutes, walletRoutes
|
||||
|
||||
createRoute('/dashboard', () => import('@/views/Dashboard'), {
|
||||
title: '工作台',
|
||||
icon: 'DashboardOutlined',
|
||||
}),
|
||||
createRoute('/about', () => import('@/views/About'), {
|
||||
title: '关于',
|
||||
icon: 'InfoCircleOutlined',
|
||||
}),
|
||||
|
||||
// 默认重定向
|
||||
{ path: '/', redirect: '/dashboard' },
|
||||
],
|
||||
children: [],
|
||||
meta: { title: '首页' },
|
||||
},
|
||||
|
||||
// ── 404 ──
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
name: 'NotFound',
|
||||
component: () => import('@/views/NotFound'),
|
||||
component: () => import('@/pages/not-found'),
|
||||
meta: { title: '页面不存在' },
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user