diff --git a/frontend/index.html b/frontend/index.html
index ca8701b..700f3a5 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,7 +2,7 @@
-
+
Game Group V2
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
index 7c7181a..7965628 100644
--- a/frontend/src/main.ts
+++ b/frontend/src/main.ts
@@ -3,7 +3,10 @@ import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
+import Vant from 'vant'
+import 'vant/lib/index.css'
import './assets/design.css'
+import './assets/mobile.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
import router from './router'
@@ -19,5 +22,6 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.use(pinia)
app.use(router)
app.use(ElementPlus)
+app.use(Vant)
app.mount('#app')
diff --git a/frontend/src/mobile/MobileLayout.vue b/frontend/src/mobile/MobileLayout.vue
new file mode 100644
index 0000000..2af1971
--- /dev/null
+++ b/frontend/src/mobile/MobileLayout.vue
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 首页
+ 群组
+ 游戏
+ 通知
+ 我的
+
+
+
+
+
diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts
index 082a076..4bd0a4e 100644
--- a/frontend/src/router/index.ts
+++ b/frontend/src/router/index.ts
@@ -2,84 +2,144 @@
import { createRouter, createWebHistory } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
import { isAuthenticated } from '@/api/pocketbase'
+import { isMobile } from '@/mobile/useDevice'
+
+// 动态选择布局:手机端用 MobileLayout,桌面端用 Layout
+const LayoutComponent = () =>
+ isMobile()
+ ? import('@/mobile/MobileLayout.vue')
+ : import('@/views/Layout.vue')
+
+// 动态选择视图:同一路由名,根据设备加载桌面/手机视图
+function view(desktop: () => Promise, mobile: () => Promise) {
+ return isMobile() ? mobile : desktop
+}
// 路由配置
const routes: RouteRecordRaw[] = [
{
path: '/login',
name: 'Login',
- component: () => import('@/views/Login.vue'),
+ component: view(
+ () => import('@/views/Login.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 2 替换为 LoginMobile
+ ),
meta: { requiresGuest: true }
},
{
path: '/register',
name: 'Register',
- component: () => import('@/views/Register.vue'),
+ component: view(
+ () => import('@/views/Register.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 2 替换为 RegisterMobile
+ ),
meta: { requiresGuest: true }
},
{
path: '/',
- component: () => import('@/views/Layout.vue'),
+ component: LayoutComponent,
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'Home',
- component: () => import('@/views/Home.vue')
+ component: view(
+ () => import('@/views/Home.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 3 替换为 HomeMobile
+ )
+ },
+ {
+ path: 'mobile-groups',
+ name: 'MobileGroups',
+ component: view(
+ () => import('@/views/Home.vue'), // 桌面端无此路由,回退首页
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 3 替换为 GroupsMobile
+ )
+ },
+ {
+ path: 'mobile-notifications',
+ name: 'MobileNotifications',
+ component: view(
+ () => import('@/views/Home.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 3 替换为 NotificationsMobile
+ )
},
{
path: 'group/:id',
name: 'GroupView',
- component: () => import('@/views/GroupView.vue'),
+ component: view(
+ () => import('@/views/GroupView.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 4 替换为 GroupViewMobile
+ ),
props: true
},
{
path: 'group/:groupId/ledger',
name: 'LedgerView',
- component: () => import('@/views/LedgerView.vue'),
- props: true,
- meta: { requiresAuth: true }
+ component: view(
+ () => import('@/views/LedgerView.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 8 替换为 LedgerMobile
+ ),
+ props: true
},
{
path: 'group/:groupId/assets',
name: 'AssetView',
- component: () => import('@/views/AssetView.vue'),
- props: true,
- meta: { requiresAuth: true }
+ component: view(
+ () => import('@/views/AssetView.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 8 替换为 AssetMobile
+ ),
+ props: true
},
{
path: 'group/:groupId/blacklist',
name: 'BlacklistView',
- component: () => import('@/views/BlacklistView.vue'),
- props: true,
- meta: { requiresAuth: true }
+ component: view(
+ () => import('@/views/BlacklistView.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 8 替换为 BlacklistMobile
+ ),
+ props: true
},
{
path: 'group/:groupId/voice/:sessionId',
name: 'VoiceRoom',
- component: () => import('@/views/VoiceRoom.vue'),
- props: true,
- meta: { requiresAuth: true }
+ component: view(
+ () => import('@/views/VoiceRoom.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 5 替换为 VoiceRoomMobile
+ ),
+ props: true
},
{
path: 'games',
name: 'GamesLibrary',
- component: () => import('@/views/GamesLibrary.vue')
+ component: view(
+ () => import('@/views/GamesLibrary.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 7 替换为 GamesLibraryMobile
+ )
},
{
path: 'profile',
name: 'Profile',
- component: () => import('@/views/Profile.vue')
+ component: view(
+ () => import('@/views/Profile.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 9 替换为 ProfileMobile
+ )
},
{
path: 'settings',
name: 'Settings',
- component: () => import('@/views/Settings.vue')
+ component: view(
+ () => import('@/views/Settings.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 9 替换为 SettingsMobile
+ )
},
{
path: 'changelog',
name: 'Changelog',
- component: () => import('@/views/Changelog.vue')
+ component: view(
+ () => import('@/views/Changelog.vue'),
+ () => import('@/views-mobile/Placeholder.vue') // 阶段 9 替换为 ChangelogMobile
+ )
}
]
},
@@ -96,17 +156,15 @@ const router = createRouter({
routes
})
-// 路由守卫
+// 路由守卫(与原逻辑一致)
router.beforeEach((to, _from, next) => {
const authenticated = isAuthenticated()
- // 需要登录的页面
if (to.meta.requiresAuth && !authenticated) {
next({ name: 'Login', query: { redirect: to.fullPath } })
return
}
- // 已登录用户访问登录/注册页
if (to.meta.requiresGuest && authenticated) {
next({ name: 'Home' })
return