wuzj пре 5 дана
родитељ
комит
6b416caf56

+ 3 - 0
src/assets/styles/variables.scss

@@ -6,6 +6,7 @@ $secondary-color: #5DABFF;        // 次要蓝色
 $success-color: #07C160;          // 成功色(添加这行)
 $green-color: #07C160;            // 绿色标签(已解谜、进行中)
 $orange-color: #FF9F2D;           // 橙色标签、按钮(主持人)
+$orange-light-color: #FFEED6;     // 浅橙色标签(玩家)
 $blue-light-color: #6EBCFF;       // 浅蓝色标签(玩家)
 $danger-color: #FF5650;           // 危险色(添加这行)
 $red-color: #FF5650;              // 红色标签(新标签)
@@ -18,6 +19,8 @@ $text-color-regular: #666;        // 常规文字
 $text-color-secondary: #999;      // 次要文字
 $text-color-disabled: #C8C9CC;    // 禁用状态文字
 $text-color-light: #FFF;          // 白色文字(按钮内)
+$text-color-orange: #FF9F2D;      // 橙色文字(主持人)
+$text-color-blue: #6EBCFF;       // 蓝色文字(玩家)
 
 // 背景色
 $background-color-base: #F7F8FA;  // 页面背景色 - 修复变量名称

+ 1 - 2
src/components/Tabbar.vue

@@ -46,9 +46,8 @@ const getIconComponent = (iconName) => {
 
 // 处理tab切换
 const handleTabSwitch = (item, index) => {
-  // 调用store中的切换方法
   const tabItem = tabBarStore.tabList[index]
-  tabBarStore.switchTab(tabItem.pagePath, index)
+  tabBarStore.navigate(tabItem.pagePath, { isTab: true })
 }
 
 // 生命周期钩子

+ 9 - 8
src/pages/game-detail/index.vue

@@ -150,6 +150,7 @@
 import Taro from '@tarojs/taro'
 import { ref } from 'vue'
 import { useGameStore} from '@/stores/game'
+import { useTabBarStore } from '@/stores/tabbar'
 import { type Game } from '@/types/game'
 import { People, Clock } from '@nutui/icons-vue-taro'
 import Tabbar from '@/components/Tabbar.vue'
@@ -176,7 +177,7 @@ export default {
   setup() {
     // 初始化store
     const gameStore = useGameStore()
-
+    const tabbarStore = useTabBarStore()
     // 页面状态
     const loading = ref(true)
     const game = ref<Game | null>(null)
@@ -185,7 +186,7 @@ export default {
     const getGameDetail = async () => {
       const pages = Taro.getCurrentPages()
       const currentPage = pages[pages.length - 1]
-      const gameId = currentPage.$taroParams?.id
+      const gameId = currentPage.$taroParams?.gameId
       
       if (!gameId) {
         loading.value = false
@@ -220,9 +221,11 @@ export default {
     // 导航到创建房间页面
     const navigateToCreateRoom = () => {
       if (game.value) {
-        // 直接跳转到创建房间页面,只传递必要参数
-        Taro.navigateTo({
-          url: `/pages/room/create/index?gameId=${game.value.id}`
+        console.log('game.value:', game.value)
+        tabbarStore.switchToRoomTab('/pages/room/create/index', {
+          params: {
+            gameId: game.value.id
+          }
         })
       }
     }
@@ -230,9 +233,7 @@ export default {
     // 导航到加入房间页面
     const navigateToJoinRoom = () => {
       // 直接跳转到加入房间页面,不需要传递任何参数
-      Taro.navigateTo({
-        url: '/pages/room/join/index'
-      })
+      tabbarStore.switchToRoomTab()
     }
     
     // 页面载入时获取游戏详情

+ 6 - 2
src/pages/history/index.vue

@@ -250,6 +250,7 @@ import { ref, onMounted, computed, watch } from 'vue'
   import Taro from '@tarojs/taro'
 import { useRoomStore } from '@/stores/room'
 import { useUserStore } from '@/stores/user'
+import { useTabBarStore } from '@/stores/tabbar'
   
   export default {
     components: {
@@ -269,6 +270,7 @@ import { useUserStore } from '@/stores/user'
     setup() {
     const roomStore = useRoomStore()
     const userStore = useUserStore()
+    const tabbarStore = useTabBarStore()
     
     // 修改为字符串类型与pane-key匹配
     const activeTab = ref('0')
@@ -346,8 +348,10 @@ import { useUserStore } from '@/stores/user'
         } else {
           if (result.needPassword) {
             // 如果需要密码,跳转到join页面
-            Taro.navigateTo({
-              url: `/pages/room/join/index?roomId=${roomId}&needPassword=true`
+            tabbarStore.switchToRoomTab('/pages/room/join/index', {
+              params: {
+                roomId: roomId
+              }
             })
           } else {
             Taro.showToast({

+ 14 - 11
src/pages/index/index.vue

@@ -23,7 +23,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="filteredGames.length === 0" description="没有找到相关游戏" image="empty" />
           </nut-tab-pane>
@@ -41,7 +41,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="hotGames.length === 0" description="暂无热门游戏" image="empty" />
           </nut-tab-pane>
@@ -59,7 +59,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="socialGames.length === 0" description="暂无社交游戏" image="empty" />
           </nut-tab-pane>
@@ -77,7 +77,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="musicGames.length === 0" description="暂无音乐游戏" image="empty" />
           </nut-tab-pane>
@@ -107,7 +107,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="filteredGames.length === 0" description="没有找到相关游戏" image="empty" />
           </template>
@@ -126,7 +126,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="hotGames.length === 0" description="暂无热门游戏" image="empty" />
           </template>
@@ -145,7 +145,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="socialGames.length === 0" description="暂无社交游戏" image="empty" />
           </template>
@@ -164,7 +164,7 @@
               :is-new="game.isNew"
               :is-hot="game.isHot"
               :description="game.description"
-              @click="navigateToGameDetail"
+              @click="() => navigateToGameDetail(game.id)"
             ></game-card>
             <nut-empty v-if="musicGames.length === 0" description="暂无音乐游戏" image="empty" />
           </template>
@@ -195,6 +195,7 @@
 import { ref, computed, watch } from 'vue'
 import Taro from '@tarojs/taro'
 import { useGameStore } from '@/stores/game'
+import { useTabBarStore } from '@/stores/tabbar'
 import GameCard from '@/components/GameCard/index.vue'
 import Tabbar from '@/components/Tabbar.vue'
 
@@ -207,7 +208,7 @@ export default {
   setup() {
     // 初始化store
     const gameStore = useGameStore()
-
+    const tabbarStore = useTabBarStore()
     // 搜索相关
     const searchValue = ref('')
     const activeTab = ref('0')
@@ -328,8 +329,10 @@ export default {
     const navigateToGameDetail = (gameId: string) => {
       const game = gameStore.games.find(g => g.id === gameId)
       if (game && (game.id === "1" || game.title.includes('海龟汤'))) {
-        Taro.navigateTo({
-          url: `/pages/game-detail/index?id=${gameId}`
+        tabbarStore.navigate('/pages/game-detail/index', {
+          params: {
+            gameId: gameId
+          }
         })
       } else {
         // 使用Modal替代Toast

+ 2 - 3
src/pages/room/create/index.vue

@@ -119,9 +119,8 @@ export default {
     // 获取游戏信息
     const initGameInfo = async () => {
       
-      const pages = Taro.getCurrentPages()
-      const currentPage = pages[pages.length - 1]
-      const gameId = currentPage.$taroParams?.gameId
+      const params = Taro.getCurrentInstance()?.router?.params
+      const gameId = params?.gameId
       
       if (!gameId) {
         Taro.showToast({

+ 57 - 49
src/pages/room/waiting/index.vue

@@ -1,16 +1,21 @@
 <template>
   <view class="waiting-room-page">
-    <!-- 房间头部信息 保持不变 -->
-    <view class="room-header">
-      <view class="room-title">
-        <view class="title">{{ currentRoom?.name || '等待房间' }}</view>
-        <view class="tag" :class="isHost ? 'host-tag' : 'player-tag'">
-          {{ isHost ? '主持人' : '玩家' }}
-        </view>
+    <!-- 顶部标题 -->
+    <view
+      class="room-header"
+      :class="{ 'host-background': isHost, 'player-background': !isHost }"
+    >
+      <view 
+        class="room-title"
+        :class="{ 'host-title': isHost, 'player-title': !isHost }"
+      >
+        {{ currentRoom?.name || '等待房间' }}
       </view>
-      <view class="status-info">
-        <view class="players-count">{{ playerCount }}/{{ currentRoom?.maxPlayers || 0 }}人已加入</view>
-        <view class="status-text">{{ statusText }}</view>
+      <view 
+        class="room-status"
+        :class="{ 'host-status': isHost, 'player-status': !isHost }"
+      >
+        {{ playerCount }}/{{ currentRoom?.maxPlayers || 0 }} 已加入,{{ statusText }}
       </view>
     </view>
 
@@ -312,6 +317,7 @@ import Taro from '@tarojs/taro'
 import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
 import { useRoomStore } from '@/stores/room'
 import { useUserStore } from '@/stores/user'
+import { useTabBarStore } from '@/stores/tabbar'
 import { useTurtleSoupStore } from '@/stores/games/turtlesoup'
 import Tabbar from '@/components/Tabbar.vue'
 import RoomCode from '@/components/RoomCode/index.vue'
@@ -349,6 +355,7 @@ export default {
     // 初始化store
     const roomStore = useRoomStore()
     const userStore = useUserStore()
+    const tabbarStore = useTabBarStore()
     const turtleSoupStore = useTurtleSoupStore()
     
     // 房间ID
@@ -764,8 +771,11 @@ export default {
           await roomStore.updateRoomStatus(RoomStatus.PLAYING)
           
           // 导航到游戏页面
-          Taro.redirectTo({
-            url: `/pages/room/play/index?roomId=${currentRoom.value.id}&gameId=${result.gameId}`
+          tabbarStore.switchToRoomTab('/pages/room/play/index', {
+            params: {
+              roomId: currentRoom.value.id,
+              gameId: result.gameId
+            }
           })
         } else {
           throw new Error('创建游戏失败')
@@ -796,8 +806,10 @@ export default {
             stopRoomListener()
             
             // 跳转到游戏页面
-            Taro.redirectTo({
-              url: `/pages/room/play/index?roomId=${roomId.value}`
+            tabbarStore.switchToRoomTab('/pages/room/play/index', {
+              params: {
+                roomId: roomId.value
+              }
             })
           }
         }
@@ -914,47 +926,43 @@ export default {
   padding-bottom: $spacing-large * 4; // 为底部tabbar留出空间
   
   .room-header {
-    margin-bottom: $spacing-base;
-    
+    padding: 8px;
+    height: 45px;
+    text-align: center;
+
     .room-title {
-      display: flex;
-      align-items: center;
-      margin-bottom: $spacing-base;
-    
-    .title {
-        font-size: $font-size-large;
-        font-weight: $font-weight-bold;
-        color: $text-color-primary;
+      font-size: 12px;
+      font-weight: bold;
+      margin-bottom: 4px;
+
+      &.host-title {
+        color: $text-color-orange;
       }
-      
-      .tag {
-        margin-left: $spacing-base;
-        padding: $spacing-mini $spacing-base;
-        border-radius: $border-radius-mini;
-        font-size: $font-size-small;
-        
-        &.host-tag {
-          background-color: $orange-color;
-          color: white;
-        }
-        
-        &.player-tag {
-          background-color: $blue-light-color;
-          color: white;
-        }
+
+      &.player-title {
+        color: $text-color-blue;
       }
     }
-    
-    .status-info {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      
-      .players-count, .status-text {
-        font-size: $font-size-base;
-        color: $text-color-secondary;
+    .room-status {
+      font-size: 8px;
+      margin-bottom: 4px;
+
+      &.host-status {
+        color: $text-color-orange;
+      }
+
+      &.player-status {
+        color: $text-color-blue;
       }
     }
+
+    &.host-background {
+    background-color: $orange-light-color;
+    }
+
+    &.player-background {
+      background-color: $blue-light-color;
+    }
   }
   
   .host-view, .player-view {

+ 54 - 28
src/stores/game.ts

@@ -1,28 +1,27 @@
-// 管理游戏列表和通用游戏详情
 import { defineStore } from 'pinia'
 import { ref, computed } from 'vue'
-import { gameService} from '@/services/game'
+import { gameService } from '@/services/game'
 import { Game } from '@/types/game'
 
 export const useGameStore = defineStore('game', () => {
   // 状态
-  const games = ref<Game[]>([])
-  const currentGame = ref<Game | null>(null)
-  const loading = ref(false)
-  const error = ref<string | null>(null)
-  
+  const games = ref<Game[]>([]) // 游戏列表
+  const currentGame = ref<Game | null>(null) // 当前选中的游戏
+  const loading = ref(false) // 加载状态
+  const error = ref<string | null>(null) // 错误信息
+
   // 计算属性
-  const hotGames = computed(() => games.value.filter(game => game.isHot))
-  const newGames = computed(() => games.value.filter(game => game.isNew))
-  
+  const hotGames = computed(() => games.value.filter((game) => game.isHot)) // 热门游戏
+  const newGames = computed(() => games.value.filter((game) => game.isNew)) // 新游戏
+
   // 获取游戏列表
   async function fetchGames() {
     loading.value = true
     error.value = null
-    
+
     try {
       const response = await gameService.getGames()
-      
+
       if (response.success && response.data) {
         games.value = response.data
       } else {
@@ -37,53 +36,80 @@ export const useGameStore = defineStore('game', () => {
       loading.value = false
     }
   }
-  
+
   // 获取游戏详情
   async function getGameDetail(id: string): Promise<Game | null> {
-    // 先从缓存中查找
-    const cachedGame = games.value.find(game => game.id === id)
+    // 如果当前正在加载,直接返回 null 防止重复请求
+    if (loading.value) {
+      console.warn('详情加载中,避免重复请求:', id)
+      return null
+    }
+
+    // 优先从缓存中查找
+    const cachedGame = games.value.find((game) => game.id === id)
     if (cachedGame) {
+      console.log('从缓存中找到游戏:', cachedGame)
+
+      // 如果缓存中的游戏缺少详细信息,从 API 补充数据
+      if (!cachedGame.description || !cachedGame.title) {
+        console.log('缓存数据不完整,从 API 获取详情:', id)
+        return await fetchGameDetailFromApi(id, cachedGame)
+      }
+
+      // 如果缓存数据完整,直接返回
       currentGame.value = cachedGame
       return cachedGame
     }
-    
-    // 如果缓存中没有,从API获取
+
+    // 缓存中没有,直接从 API 获取
+    return await fetchGameDetailFromApi(id)
+  }
+
+  // 从 API 获取游戏详情并更新缓存
+  async function fetchGameDetailFromApi(id: string, cachedGame?: Game): Promise<Game | null> {
     loading.value = true
     error.value = null
-    
+
     try {
       const response = await gameService.getGameDetail(id)
-      
+
       if (response.success && response.data) {
-        currentGame.value = response.data
-        return response.data
+        const detailGame = response.data
+
+        // 更新缓存中的数据
+        if (cachedGame) {
+          Object.assign(cachedGame, detailGame) // 合并新数据到缓存
+        } else {
+          games.value.push(detailGame) // 如果缓存中没有,直接添加到列表
+        }
+
+        currentGame.value = detailGame
+        return detailGame
       } else {
         error.value = response.message || '获取游戏详情失败'
-        currentGame.value = null
         return null
       }
     } catch (e) {
       console.error('获取游戏详情失败:', e)
       error.value = '获取游戏详情失败'
-      currentGame.value = null
       return null
     } finally {
       loading.value = false
     }
   }
-  
+
   // 设置当前游戏
   function setCurrentGame(game: Game | null) {
     currentGame.value = game
   }
-  
+
   // 清除游戏数据
   function clearGames() {
     games.value = []
     currentGame.value = null
     error.value = null
   }
-  
+
   return {
     // 状态
     games,
@@ -91,10 +117,10 @@ export const useGameStore = defineStore('game', () => {
     loading,
     error,
 
-    // Getters
+    // 计算属性
     hotGames,
     newGames,
-    
+
     // 方法
     fetchGames,
     getGameDetail,

+ 24 - 17
src/stores/room.ts

@@ -11,12 +11,13 @@ import {
   RoomVisibility 
 } from '@/types/room'
 import { useUserStore } from '@/stores/user'
-import Taro from '@tarojs/taro'
+import { useTabBarStore } from '@/stores/tabbar'
+
 
 export const useRoomStore = defineStore('room', () => {
   // 获取userStore
   const userStore = useUserStore()
-  
+  const tabbarStore = useTabBarStore()
   // 当前房间信息
   const currentRoom = ref<Room | null>(null)
   
@@ -488,23 +489,29 @@ export const useRoomStore = defineStore('room', () => {
     }
   }
   
-  // 添加一个根据房间状态执行跳转的辅助方法
-  function navigateToRoomPage(roomId: string) {
-    // 获取当前房间状态
-    const status = currentRoom.value?.status
+    // 添加一个根据房间状态执行跳转的辅助方法
+    function navigateToRoomPage(
+      roomId: string,
+      additionalParams: Record<string, any> = {}
+    ) {
+      // 获取当前房间状态
+      const status = currentRoom.value?.status
+    
+      // 构建基础参数
+      const params = {
+        roomId,
+        ...additionalParams
+      }
     
-    if (status === RoomStatus.PLAYING) {
-      // 如果房间状态为"进行中",跳转到游戏页面
-      Taro.navigateTo({
-        url: `/pages/room/play/index?roomId=${roomId}`
-      })
-    } else {
-      // 如果房间状态为"等待中"或其他状态,跳转到等待页面
-      Taro.navigateTo({
-        url: `/pages/room/waiting/index?roomId=${roomId}`
-      })
+      // 根据房间状态跳转到不同页面
+      if (status === RoomStatus.PLAYING) {
+        // 游戏进行中,跳转到游戏页面
+        tabbarStore.switchToRoomTab('/pages/room/play/index', { params })
+      } else {
+        // 等待中或其他状态,跳转到等待页面
+        tabbarStore.switchToRoomTab('/pages/room/waiting/index', { params })
+      }
     }
-  }
   
   // 返回状态和方法
   return {

+ 54 - 0
src/stores/tabbar.ts

@@ -60,6 +60,60 @@ export const useTabBarStore = defineStore('tabbar', {
         console.log('当前页面不在 tab 列表中:', url)
       }
     },
+
+    /**
+     * 封装页面跳转逻辑
+     * @param url 目标页面路径
+     * @param options 配置项
+     */
+    navigate(url: string, options: { params?: Record<string, any>; isTab?: boolean } = {}) {
+      const { params, isTab } = options
+
+      // 拼接参数
+      let fullUrl = url
+      if (params) {
+        const queryString = Object.entries(params)
+          .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
+          .join('&')
+        fullUrl = `${url}?${queryString}`
+      }
+      console.log('fullUrl:', fullUrl)
+
+      // 判断是否为 TabBar 页面
+      const tabIndex = this.tabList.findIndex((item) => item.pagePath === url)
+      if (isTab || tabIndex !== -1) {
+        // 如果是 TabBar 页面,切换 TabBar
+        this.currentIndex = tabIndex
+        this.switchTab(fullUrl, tabIndex)
+      } else {
+        // 非 TabBar 页面,普通跳转
+        Taro.navigateTo({
+          url: fullUrl
+        }).catch((err) => {
+          console.error('navigateTo 错误:', err)
+        })
+      }
+    },
+
+    /**
+     * 切换到房间 Tab 的逻辑
+     * @param subPage 子页面路径
+     * @param options 带有 params 的选项对象
+     */
+    switchToRoomTab(subPage = '/pages/room/join/index', options: { params?: Record<string, any> } = {}) {
+      const isSubPage = !this.tabList.some((item) => item.pagePath === subPage)
+
+      if (isSubPage) {
+        // 如果是房间的子页面,更新 TabBar 状态并跳转
+        this.currentIndex = 1 // 房间 Tab 的索引
+        this.navigate(subPage, options) // 正确传递整个 options 对象
+      } else {
+        // 否则直接切换到 `房间` Tab
+        this.navigate('/pages/room/join/index', { 
+          isTab: true
+        })
+      }
+    },
     
     // 切换 tab
     switchTab(url: string, index: number) {