262 lines
8.7 KiB
TypeScript
262 lines
8.7 KiB
TypeScript
import { RouteRecordRaw, RouteLocationNormalized } from 'vue-router';
|
||
import { getDeviceType, DeviceType } from './deviceUtils';
|
||
|
||
/**
|
||
* 路由适配器接口
|
||
*/
|
||
export interface RouteAdapter {
|
||
original: () => Promise<any>; // web桌面端
|
||
mobile?: () => Promise<any>; // h5移动端
|
||
}
|
||
|
||
/**
|
||
* 移动端路由映射表
|
||
*/
|
||
export const MOBILE_ROUTES_MAP: Record<string, string> = {
|
||
// User Views
|
||
'/home': '@/views/user/home/HomeView.mobile.vue',
|
||
'/resource-center': '@/views/user/resource-center/ResourceCenterView.mobile.vue',
|
||
'/resource-hot': '@/views/user/resource-center/HotResourceView.mobile.vue',
|
||
'/search': '@/views/user/resource-center/SearchView.mobile.vue',
|
||
'/course-center': '@/views/user/study-plan/CourseCenterView.mobile.vue',
|
||
'/course-detail': '@/views/user/study-plan/CourseDetailView.mobile.vue',
|
||
'/course-study': '@/views/user/study-plan/CourseStudyView.mobile.vue',
|
||
'/study-tasks': '@/views/user/study-plan/StudyTasksView.mobile.vue',
|
||
'/learning-task-detail': '@/views/user/study-plan/LearningTaskDetailView.mobile.vue',
|
||
'/user-center': '@/views/user/user-center/UserCenterLayout.mobile.vue',
|
||
'/personal-info': '@/views/user/user-center/profile/PersonalInfoView.mobile.vue',
|
||
'/account-settings': '@/views/user/user-center/profile/AccountSettingsView.mobile.vue',
|
||
'/my-achievements': '@/views/user/user-center/MyAchievementsView.mobile.vue',
|
||
'/my-favorites': '@/views/user/user-center/MyFavoritesView.mobile.vue',
|
||
'/learning-records': '@/views/user/user-center/LearningRecordsView.mobile.vue',
|
||
'/my-messages': '@/views/user/message/MyMessageListView.mobile.vue',
|
||
'/message-detail': '@/views/user/message/MyMessageDetailView.mobile.vue',
|
||
|
||
// Public Views
|
||
'/login': '@/views/public/login/Login.mobile.vue',
|
||
'/register': '@/views/public/login/Register.mobile.vue',
|
||
'/forgot-password': '@/views/public/login/ForgotPassword.mobile.vue',
|
||
'/article-show': '@/views/public/article/ArticleShowView.mobile.vue',
|
||
'/article-add': '@/views/public/article/ArticleAddView.mobile.vue'
|
||
};
|
||
|
||
/**
|
||
* Layout映射表
|
||
*/
|
||
export const LAYOUT_MAP: Record<string, Record<DeviceType, string>> = {
|
||
'NavigationLayout': {
|
||
[DeviceType.MOBILE]: '@/layouts/MobileLayout.vue', // h5移动端
|
||
[DeviceType.DESKTOP]: '@/layouts/NavigationLayout.vue' // web桌面端
|
||
},
|
||
'SidebarLayout': {
|
||
[DeviceType.MOBILE]: '@/layouts/MobileLayout.vue', // h5移动端
|
||
[DeviceType.DESKTOP]: '@/layouts/SidebarLayout.vue' // web桌面端
|
||
},
|
||
'BasicLayout': {
|
||
[DeviceType.MOBILE]: '@/layouts/MobileLayout.vue', // h5移动端
|
||
[DeviceType.DESKTOP]: '@/layouts/BasicLayout.vue' // web桌面端
|
||
},
|
||
'BlankLayout': {
|
||
[DeviceType.MOBILE]: '@/layouts/BlankLayout.vue', // h5移动端
|
||
[DeviceType.DESKTOP]: '@/layouts/BlankLayout.vue' // web桌面端
|
||
},
|
||
'PageLayout': {
|
||
[DeviceType.MOBILE]: '@/layouts/MobileLayout.vue', // h5移动端
|
||
[DeviceType.DESKTOP]: '@/layouts/PageLayout.vue' // web桌面端
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 创建响应式路由组件
|
||
*/
|
||
export function createResponsiveRoute(adapter: RouteAdapter): () => Promise<any> {
|
||
return async () => {
|
||
const deviceType = getDeviceType();
|
||
|
||
try {
|
||
// 尝试加载设备特定的组件
|
||
if (deviceType === DeviceType.MOBILE && adapter.mobile) {
|
||
return await adapter.mobile();
|
||
}
|
||
} catch (error) {
|
||
console.warn(`Failed to load device-specific component for ${deviceType}, falling back to original:`, error);
|
||
}
|
||
|
||
// 回退到原始组件(桌面端/web)
|
||
return await adapter.original();
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取响应式Layout组件
|
||
*/
|
||
export function getResponsiveLayout(layoutName: string): () => Promise<any> {
|
||
const deviceType = getDeviceType();
|
||
const layoutMap = LAYOUT_MAP[layoutName];
|
||
|
||
if (!layoutMap) {
|
||
console.warn(`Unknown layout: ${layoutName}, using original`);
|
||
// 使用具体的导入路径
|
||
switch (layoutName) {
|
||
case 'BlankLayout':
|
||
return () => import('@/layouts/BlankLayout.vue');
|
||
case 'NavigationLayout':
|
||
return () => import('@/layouts/NavigationLayout.vue');
|
||
case 'SidebarLayout':
|
||
return () => import('@/layouts/SidebarLayout.vue');
|
||
case 'BasicLayout':
|
||
return () => import('@/layouts/BasicLayout.vue');
|
||
case 'PageLayout':
|
||
return () => import('@/layouts/PageLayout.vue');
|
||
default:
|
||
throw new Error(`Unknown layout: ${layoutName}`);
|
||
}
|
||
}
|
||
|
||
const targetLayout = layoutMap[deviceType];
|
||
|
||
return async () => {
|
||
try {
|
||
// 使用具体的导入路径而不是动态路径
|
||
switch (targetLayout) {
|
||
case '@/layouts/BlankLayout.vue':
|
||
return await import('@/layouts/BlankLayout.vue');
|
||
case '@/layouts/NavigationLayout.vue':
|
||
return await import('@/layouts/NavigationLayout.vue');
|
||
case '@/layouts/SidebarLayout.vue':
|
||
return await import('@/layouts/SidebarLayout.vue');
|
||
case '@/layouts/BasicLayout.vue':
|
||
return await import('@/layouts/BasicLayout.vue');
|
||
case '@/layouts/MobileLayout.vue':
|
||
return await import('@/layouts/MobileLayout.vue');
|
||
case '@/layouts/PageLayout.vue':
|
||
return await import('@/layouts/PageLayout.vue');
|
||
default:
|
||
throw new Error(`Unknown layout path: ${targetLayout}`);
|
||
}
|
||
} catch (error) {
|
||
console.warn(`Failed to load responsive layout ${targetLayout}, falling back to original:`, error);
|
||
// 回退到原始layout
|
||
switch (layoutName) {
|
||
case 'BlankLayout':
|
||
return await import('@/layouts/BlankLayout.vue');
|
||
case 'NavigationLayout':
|
||
return await import('@/layouts/NavigationLayout.vue');
|
||
case 'SidebarLayout':
|
||
return await import('@/layouts/SidebarLayout.vue');
|
||
case 'BasicLayout':
|
||
return await import('@/layouts/BasicLayout.vue');
|
||
case 'PageLayout':
|
||
return await import('@/layouts/PageLayout.vue');
|
||
default:
|
||
throw new Error(`Unknown layout: ${layoutName}`);
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 创建自适应路由配置
|
||
*/
|
||
export function createAdaptiveRoute(
|
||
path: string,
|
||
originalComponent: string,
|
||
layoutName?: string,
|
||
meta?: any
|
||
): RouteRecordRaw {
|
||
// 创建具体的导入函数而不是使用动态路径
|
||
const getOriginalComponent = () => {
|
||
switch (originalComponent) {
|
||
case '@/views/public/login/Login.vue':
|
||
return import('@/views/public/login/Login.vue');
|
||
case '@/views/public/login/Register.vue':
|
||
return import('@/views/public/login/Register.vue');
|
||
case '@/views/public/login/ForgotPassword.vue':
|
||
return import('@/views/public/login/ForgotPassword.vue');
|
||
default:
|
||
throw new Error(`Unknown component: ${originalComponent}`);
|
||
}
|
||
};
|
||
|
||
const getMobileComponent = (): (() => Promise<any>) | null => {
|
||
const mobilePath = MOBILE_ROUTES_MAP[path];
|
||
if (!mobilePath) return null;
|
||
|
||
switch (mobilePath) {
|
||
case '@/views/public/login/Login.mobile.vue':
|
||
return () => import('@/views/public/login/Login.mobile.vue');
|
||
case '@/views/public/login/Register.mobile.vue':
|
||
return () => import('@/views/public/login/Register.mobile.vue');
|
||
case '@/views/public/login/ForgotPassword.mobile.vue':
|
||
return () => import('@/views/public/login/ForgotPassword.mobile.vue');
|
||
default:
|
||
return null;
|
||
}
|
||
};
|
||
|
||
const adapter: RouteAdapter = {
|
||
original: getOriginalComponent
|
||
};
|
||
|
||
// 检查是否有移动端版本
|
||
const mobileImportFunction = getMobileComponent();
|
||
if (mobileImportFunction) {
|
||
adapter.mobile = mobileImportFunction;
|
||
}
|
||
|
||
// 如果指定了Layout,应用响应式Layout
|
||
if (layoutName) {
|
||
const route: RouteRecordRaw = {
|
||
path,
|
||
component: getResponsiveLayout(layoutName),
|
||
children: [
|
||
{
|
||
path: '',
|
||
component: createResponsiveRoute(adapter),
|
||
meta
|
||
}
|
||
],
|
||
meta
|
||
};
|
||
return route;
|
||
}
|
||
|
||
const route: RouteRecordRaw = {
|
||
path,
|
||
component: createResponsiveRoute(adapter),
|
||
meta
|
||
};
|
||
|
||
return route;
|
||
}
|
||
|
||
/**
|
||
* 监听屏幕尺寸变化,重新加载路由
|
||
*/
|
||
export function setupRouteWatcher(router: any) {
|
||
let currentDeviceType = getDeviceType();
|
||
|
||
const handleResize = () => {
|
||
const newDeviceType = getDeviceType();
|
||
if (newDeviceType !== currentDeviceType) {
|
||
currentDeviceType = newDeviceType;
|
||
// 重新加载当前路由以应用新的组件
|
||
const currentRoute = router.currentRoute.value;
|
||
router.replace({
|
||
...currentRoute,
|
||
query: {
|
||
...currentRoute.query,
|
||
_refresh: Date.now() // 强制重新加载
|
||
}
|
||
});
|
||
}
|
||
};
|
||
|
||
window.addEventListener('resize', handleResize);
|
||
|
||
// 返回清理函数
|
||
return () => {
|
||
window.removeEventListener('resize', handleResize);
|
||
};
|
||
}
|