فهرست منبع

service store职责分离,新增history页面

wuzj 1 هفته پیش
والد
کامیت
ef70637fb6
7فایلهای تغییر یافته به همراه1253 افزوده شده و 309 حذف شده
  1. 1 0
      components.d.ts
  2. 2 0
      src/assets/styles/variables.scss
  3. 3 0
      src/pages/history/index.config.ts
  4. 571 36
      src/pages/history/index.vue
  5. 84 67
      src/pages/room/join/index.vue
  6. 252 142
      src/services/api/room.ts
  7. 340 64
      src/stores/room.ts

+ 1 - 0
components.d.ts

@@ -22,6 +22,7 @@ declare module 'vue' {
     NutRange: typeof import('@nutui/nutui-taro')['Range']
     NutRate: typeof import('@nutui/nutui-taro')['Rate']
     NutSearchbar: typeof import('@nutui/nutui-taro')['Searchbar']
+    NutSkeleton: typeof import('@nutui/nutui-taro')['Skeleton']
     NutTabbar: typeof import('@nutui/nutui-taro')['Tabbar']
     NutTabbarItem: typeof import('@nutui/nutui-taro')['TabbarItem']
     NutTabPane: typeof import('@nutui/nutui-taro')['TabPane']

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

@@ -16,6 +16,7 @@ $yellow-color: #FFB11B;           // 黄色(评分星星)
 $text-color-primary: #333;        // 主要文字
 $text-color-regular: #666;        // 常规文字
 $text-color-secondary: #999;      // 次要文字
+$text-color-disabled: #C8C9CC;    // 禁用状态文字
 $text-color-light: #FFF;          // 白色文字(按钮内)
 
 // 背景色
@@ -26,6 +27,7 @@ $background-color-orange: #FFF8E8; // 主持人背景
 $background-color-blue: #EBF5FF;  // 玩家背景
 $background-color-gold: #FFF8DC;  // 金色背景(海龟汤介绍卡片)
 $background-color-gray: #F5F7FA;  // 灰色背景(输入框、提示区域)
+$background-color-disabled: #F7F8FA;  // 禁用状态背景色
 
 // 边框颜色
 $border-color-base: #EBEDF0;      // 基本边框

+ 3 - 0
src/pages/history/index.config.ts

@@ -0,0 +1,3 @@
+export default {
+  navigationBarTitleText: '游戏历史',
+} 

+ 571 - 36
src/pages/history/index.vue

@@ -1,48 +1,583 @@
 <template>
-    <view class="history-page">
-      <view class="title">历史记录</view>
-      <nut-empty description="正在开发中..." image="empty" />
+  <view class="history-page">
+    <view class="tab-container">
+      <nut-tabs v-model="activeTab">
+        <nut-tab-pane title="最近游戏" pane-key="0">
+          <nut-skeleton v-if="isLoading" animated :rows="3" />
+    
+          <block v-else>
+            <!-- 进行中的房间 -->
+            <view class="game-section" v-if="activeRooms.length > 0">
+              <view class="section-title">进行中的房间</view>
+              
+              <view class="room-list">
+                <view 
+                  v-for="room in activeRooms" 
+                  :key="room.id" 
+                  class="room-item active"
+                  @click="joinActiveRoom(room.id)"
+                >
+                  <view class="game-icon">
+                    <view v-if="room.gameId === '1'" class="game-icon-inner duck"></view>
+                    <view v-else-if="room.gameId === '2'" class="game-icon-inner detective"></view>
+                    <view v-else class="game-icon-inner default"></view>
+                  </view>
+                  
+                  <view class="room-info">
+                    <view class="room-name">{{ room.name }}</view>
+                    <view class="room-details">
+                      <text class="game-type">{{ room.gameTitle }}</text>
+                      <text class="room-time">房间号: {{ room.id }}</text>
+                    </view>
+                  </view>
+                  
+                  <view class="status-tag playing" v-if="room.status === 'playing'">进行中</view>
+                  <view class="status-tag waiting" v-else>等待中</view>
+                </view>
+              </view>
+            </view>
+            
+            <!-- 已结束的游戏 -->
+            <view class="game-section" v-if="endedGames.length > 0">
+              <view class="section-title">已结束的游戏</view>
+              
+              <view class="room-list">
+                <view 
+                  v-for="game in endedGames" 
+                  :key="game.id" 
+                  class="room-item ended"
+                  @click="viewGameDetails(game.id)"
+                >
+                  <view class="game-icon">
+                    <view v-if="game.gameId === '1'" class="game-icon-inner duck"></view>
+                    <view v-else-if="game.gameId === '2'" class="game-icon-inner detective"></view>
+                    <view v-else class="game-icon-inner default"></view>
+                  </view>
+                  
+                  <view class="room-info">
+                    <view class="room-name">{{ game.roomName }}</view>
+                    <view class="room-details">
+                      <text class="game-type">{{ game.gameTitle }}</text>
+                      <text class="room-time">{{ formatTime(game.endTime) }}</text>
+                    </view>
+                    <view class="game-duration">游戏时长: {{ game.duration }}分钟</view>
+                  </view>
+                  
+                  <view class="status-tag ended">已结束</view>
+                </view>
+              </view>
+            </view>
+            
+            <!-- 空状态 -->
+            <nut-empty 
+              v-if="activeRooms.length === 0 && endedGames.length === 0" 
+              description="暂无游戏记录"
+              image="empty" 
+            />
+          </block>
+          
+          <view class="load-more" v-if="activeRooms.length > 0 || endedGames.length > 0">
+            <nut-divider>{{ isLoading ? '加载中...' : '没有更多了' }}</nut-divider>
+          </view>
+        </nut-tab-pane>
+        
+        <nut-tab-pane title="我创建的" pane-key="1">
+          <nut-skeleton v-if="isLoading" animated :rows="3" />
+          
+          <block v-else>
+            <!-- 进行中的房间 -->
+            <view class="game-section" v-if="activeRooms.length > 0">
+              <view class="section-title">进行中的房间</view>
+              
+              <view class="room-list">
+                <view 
+                  v-for="room in activeRooms" 
+                  :key="room.id" 
+                  class="room-item active"
+                  @click="joinActiveRoom(room.id)"
+                >
+                  <view class="game-icon">
+                    <view v-if="room.gameId === '1'" class="game-icon-inner duck"></view>
+                    <view v-else-if="room.gameId === '2'" class="game-icon-inner detective"></view>
+                    <view v-else class="game-icon-inner default"></view>
+                  </view>
+                  
+                  <view class="room-info">
+                    <view class="room-name">{{ room.name }}</view>
+                    <view class="room-details">
+                      <text class="game-type">{{ room.gameTitle }}</text>
+                      <text class="room-time">房间号: {{ room.id }}</text>
+                    </view>
+                  </view>
+                  
+                  <view class="status-tag playing" v-if="room.status === 'playing'">进行中</view>
+                  <view class="status-tag waiting" v-else>等待中</view>
+                </view>
+              </view>
+            </view>
+            
+            <!-- 已结束的游戏 -->
+            <view class="game-section" v-if="endedGames.length > 0">
+              <view class="section-title">已结束的游戏</view>
+              
+              <view class="room-list">
+                <view 
+                  v-for="game in endedGames" 
+                  :key="game.id" 
+                  class="room-item ended"
+                  @click="viewGameDetails(game.id)"
+                >
+                  <view class="game-icon">
+                    <view v-if="game.gameId === '1'" class="game-icon-inner duck"></view>
+                    <view v-else-if="game.gameId === '2'" class="game-icon-inner detective"></view>
+                    <view v-else class="game-icon-inner default"></view>
+                  </view>
+                  
+                  <view class="room-info">
+                    <view class="room-name">{{ game.roomName }}</view>
+                    <view class="room-details">
+                      <text class="game-type">{{ game.gameTitle }}</text>
+                      <text class="room-time">{{ formatTime(game.endTime) }}</text>
+                    </view>
+                    <view class="game-duration">游戏时长: {{ game.duration }}分钟</view>
+                  </view>
+                  
+                  <view class="status-tag ended">已结束</view>
+                </view>
+              </view>
+            </view>
+            
+            <!-- 空状态 -->
+            <nut-empty 
+              v-if="activeRooms.length === 0 && endedGames.length === 0" 
+              description="暂无创建的游戏"
+              image="empty" 
+            />
+          </block>
+          
+          <view class="load-more" v-if="activeRooms.length > 0 || endedGames.length > 0">
+            <nut-divider>{{ isLoading ? '加载中...' : '没有更多了' }}</nut-divider>
+          </view>
+        </nut-tab-pane>
+        
+        <nut-tab-pane title="我参与的" pane-key="2">
+          <nut-skeleton v-if="isLoading" animated :rows="3" />
+          
+          <block v-else>
+            <!-- 进行中的房间 -->
+            <view class="game-section" v-if="activeRooms.length > 0">
+              <view class="section-title">进行中的房间</view>
+              
+              <view class="room-list">
+                <view 
+                  v-for="room in activeRooms" 
+                  :key="room.id" 
+                  class="room-item active"
+                  @click="joinActiveRoom(room.id)"
+                >
+                  <view class="game-icon">
+                    <view v-if="room.gameId === '1'" class="game-icon-inner duck"></view>
+                    <view v-else-if="room.gameId === '2'" class="game-icon-inner detective"></view>
+                    <view v-else class="game-icon-inner default"></view>
+                  </view>
+                  
+                  <view class="room-info">
+                    <view class="room-name">{{ room.name }}</view>
+                    <view class="room-details">
+                      <text class="game-type">{{ room.gameTitle }}</text>
+                      <text class="room-time">房间号: {{ room.id }}</text>
+                    </view>
+                  </view>
+                  
+                  <view class="status-tag playing" v-if="room.status === 'playing'">进行中</view>
+                  <view class="status-tag waiting" v-else>等待中</view>
+                </view>
+              </view>
+            </view>
+            
+            <!-- 已结束的游戏 -->
+            <view class="game-section" v-if="endedGames.length > 0">
+              <view class="section-title">已结束的游戏</view>
+              
+              <view class="room-list">
+                <view 
+                  v-for="game in endedGames" 
+                  :key="game.id" 
+                  class="room-item ended"
+                  @click="viewGameDetails(game.id)"
+                >
+                  <view class="game-icon">
+                    <view v-if="game.gameId === '1'" class="game-icon-inner duck"></view>
+                    <view v-else-if="game.gameId === '2'" class="game-icon-inner detective"></view>
+                    <view v-else class="game-icon-inner default"></view>
+                  </view>
+                  
+                  <view class="room-info">
+                    <view class="room-name">{{ game.roomName }}</view>
+                    <view class="room-details">
+                      <text class="game-type">{{ game.gameTitle }}</text>
+                      <text class="room-time">{{ formatTime(game.endTime) }}</text>
+                    </view>
+                    <view class="game-duration">游戏时长: {{ game.duration }}分钟</view>
+                  </view>
+                  
+                  <view class="status-tag ended">已结束</view>
+                </view>
+              </view>
+            </view>
+            
+            <!-- 空状态 -->
+            <nut-empty 
+              v-if="activeRooms.length === 0 && endedGames.length === 0" 
+              description="暂无参与的游戏"
+              image="empty" 
+            />
+          </block>
+          
+          <view class="load-more" v-if="activeRooms.length > 0 || endedGames.length > 0">
+            <nut-divider>{{ isLoading ? '加载中...' : '没有更多了' }}</nut-divider>
+          </view>
+        </nut-tab-pane>
+      </nut-tabs>
     </view>
-    <Tabbar></Tabbar>
-  </template>
+  </view>
+  <Tabbar></Tabbar>
+</template>
+
+<script lang="ts">
+import { ref, onMounted, computed, watch } from 'vue'
+import Tabbar from '@/components/Tabbar.vue'
+import Taro from '@tarojs/taro'
+import { useRoomStore } from '@/stores/room'
+import { useUserStore } from '@/stores/user'
+
+export default {
+  components: {
+    Tabbar
+  },
   
-  <script lang="ts">
-  import Tabbar from '@/components/Tabbar.vue'
-  import Taro from '@tarojs/taro'
+  // 页面显示时的生命周期钩子
+  onShow() {
+    // 隐藏返回首页按钮
+    Taro.hideHomeButton()
+    
+    // 刷新数据
+    this.refreshHistory()
+  },
   
-  // 历史记录页逻辑
-  export default {
-    components: {
-      Tabbar
-    },
-    
-    // 页面显示时的生命周期钩子
-    onShow() {
-      // 隐藏返回首页按钮
-      Taro.hideHomeButton()
-      console.log('已隐藏返回首页按钮')
-    },
-    
-    // setup函数用于Composition API
-    setup() {
-      // 这里可以添加其他响应式数据和方法
+  // setup函数用于Composition API
+  setup() {
+    const roomStore = useRoomStore()
+    const userStore = useUserStore()
+    
+    // 修改为字符串类型与pane-key匹配
+    const activeTab = ref('0')
+    
+    // 计算属性获取historyState中的数据
+    const activeRooms = computed(() => roomStore.historyState.activeRooms)
+    const endedGames = computed(() => roomStore.historyState.endedGames)
+    const isLoading = computed(() => roomStore.historyState.isLoading)
+    
+    // 监听标签变化
+    watch(activeTab, (newVal) => {
+      loadHistoryByTab(newVal)
+    })
+    
+    // 加载历史记录
+    const loadHistory = async () => {
+      await loadHistoryByTab(activeTab.value)
+    }
+    
+    // 根据标签加载数据
+    const loadHistoryByTab = async (tabIndex: string) => {
+      // 将字符串转换为数字
+      const tabIndexNum = parseInt(tabIndex)
+      
+      // 确保用户已登录
+      if (!userStore.openid) {
+        console.warn('用户未登录,无法加载历史记录')
+        return
+      }
       
-      return {
-        // 返回需要在模板中使用的数据和方法
+      await roomStore.loadHistoryByTab(tabIndexNum)
+      
+      // 同步更新最近房间列表
+      if (tabIndexNum === 0) {
+        await roomStore.loadRecentRooms()
+      }
+    }
+    
+    // 刷新历史记录
+    const refreshHistory = () => {
+      loadHistory()
+    }
+    
+    // 格式化时间显示
+    const formatTime = (timestamp) => {
+      const date = new Date(timestamp)
+      const now = new Date()
+      
+      // 今天内
+      if (date.toDateString() === now.toDateString()) {
+        return `今天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
       }
+      
+      // 昨天
+      const yesterday = new Date()
+      yesterday.setDate(now.getDate() - 1)
+      if (date.toDateString() === yesterday.toDateString()) {
+        return `昨天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
+      }
+      
+      // 其他日期
+      return `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`
+    }
+    
+    // 加入进行中的房间
+    const joinActiveRoom = async (roomId: string) => {
+      try {
+        Taro.showLoading({ title: '加入房间中...' })
+        
+        // 使用Store中的统一方法加入房间
+        const result = await roomStore.joinRoomById(roomId)
+        
+        if (result.success) {
+          roomStore.navigateToRoomPage(roomId)
+        } else {
+          if (result.needPassword) {
+            // 如果需要密码,跳转到join页面
+            Taro.navigateTo({
+              url: `/pages/room/join/index?roomId=${roomId}&needPassword=true`
+            })
+          } else {
+            Taro.showToast({
+              title: result.message || '加入房间失败',
+              icon: 'none'
+            })
+          }
+        }
+      } catch (error: any) {
+        console.error('加入房间失败:', error)
+        Taro.showToast({
+          title: error.message || '加入房间失败',
+          icon: 'none'
+        })
+      } finally {
+        Taro.hideLoading()
+      }
+    }
+    
+    // 查看游戏详情
+    const viewGameDetails = (gameId) => {
+      Taro.showToast({
+        title: '游戏回顾功能开发中',
+        icon: 'none'
+      })
+    }
+    
+    // 初始加载
+    onMounted(() => {
+      loadHistory()
+    })
+    
+    return {
+      activeTab,
+      activeRooms,
+      endedGames,
+      isLoading,
+      loadHistoryByTab, // 导出新函数
+      refreshHistory,
+      formatTime,
+      joinActiveRoom,
+      viewGameDetails
     }
   }
-  </script>
+}
+</script>
+
+<style lang="scss">
+.history-page {
+  padding: $spacing-base;
+  background-color: $background-color-base;
+  min-height: 100vh;
+  padding-bottom: $spacing-large * 4; // 为底部tabbar留出空间
+  scroll-behavior: smooth;
   
-  <style lang="scss">
-  .history-page {
-    padding: 20px;
+  .tab-container {
+    background-color: $background-color-light;
+    border-radius: $border-radius-base;
+    margin-bottom: $spacing-base;
+    overflow: hidden;
+    box-shadow: $shadow-light;
     
-    .title {
-      font-size: 20px;
-      font-weight: bold;
-      margin-bottom: 20px;
-      text-align: center;
+    .nut-tabs__content {
+      transition: all 0.3s ease;
     }
   }
-  </style>
+  
+  .game-section {
+    background-color: $background-color-light;
+    border-radius: $border-radius-base;
+    padding: $spacing-base;
+    margin-bottom: $spacing-base;
+    box-shadow: $shadow-light;
+    
+    .section-title {
+      font-size: $font-size-medium;
+      font-weight: $font-weight-bold;
+      color: $text-color-primary;
+      margin-bottom: $spacing-base;
+      padding-left: $spacing-small;
+      border-left: 3px solid $primary-color;
+    }
+    
+    .room-list {
+      .room-item {
+        display: flex;
+        align-items: center;
+        padding: $spacing-base;
+        border-bottom: 1px solid $border-color-light;
+        position: relative;
+        transition: transform 0.2s, background-color 0.2s;
+        
+        &:last-child {
+          border-bottom: none;
+        }
+        
+        &.active {
+          background-color: rgba(60, 146, 251, 0.05);
+        }
+        
+        &.ended {
+          background-color: rgba(153, 153, 153, 0.05);
+        }
+        
+        &:active {
+          transform: scale(0.98);
+          background-color: rgba(0, 0, 0, 0.02);
+        }
+        
+        .game-icon {
+          width: 48px;
+          height: 48px;
+          border-radius: 8px;
+          margin-right: $spacing-base;
+          overflow: hidden;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          background-color: $background-color-blue;
+          
+          .game-icon-inner {
+            width: 36px;
+            height: 36px;
+            
+            &.duck {
+              background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%23FFCC00" d="M12,3C7.58,3 4,5.58 4,10C4,14.42 7.58,17 12,17C16.42,17 20,14.42 20,10C20,5.58 16.42,3 12,3M12,5C13.93,5 15.5,6.57 15.5,8.5C15.5,10.43 13.93,12 12,12C10.07,12 8.5,10.43 8.5,8.5C8.5,6.57 10.07,5 12,5M5,20V23H19V20H5Z"/></svg>');
+              background-size: cover;
+            }
+            
+            &.detective {
+              background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%233C92FB" d="M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z"/></svg>');
+              background-size: cover;
+            }
+            
+            &.default {
+              background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="%23999999" d="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4M12,10.5A1.5,1.5 0 0,1 13.5,12A1.5,1.5 0 0,1 12,13.5A1.5,1.5 0 0,1 10.5,12A1.5,1.5 0 0,1 12,10.5M7.5,10.5A1.5,1.5 0 0,1 9,12A1.5,1.5 0 0,1 7.5,13.5A1.5,1.5 0 0,1 6,12A1.5,1.5 0 0,1 7.5,10.5M16.5,10.5A1.5,1.5 0 0,1 18,12A1.5,1.5 0 0,1 16.5,13.5A1.5,1.5 0 0,1 15,12A1.5,1.5 0 0,1 16.5,10.5Z"/></svg>');
+              background-size: cover;
+            }
+          }
+        }
+        
+        .room-info {
+          flex: 1;
+          
+          .room-name {
+            font-size: $font-size-medium;
+            color: $text-color-primary;
+            font-weight: $font-weight-medium;
+            margin-bottom: $spacing-mini;
+          }
+          
+          .room-details {
+            display: flex;
+            align-items: center;
+            margin-bottom: $spacing-mini;
+            
+            .game-type {
+              font-size: $font-size-small;
+              color: $primary-color;
+              background-color: $background-color-blue;
+              padding: $spacing-mini $spacing-small;
+              border-radius: $border-radius-mini;
+              margin-right: $spacing-base;
+            }
+            
+            .room-time {
+              font-size: $font-size-small;
+              color: $text-color-secondary;
+            }
+          }
+          
+          .game-duration {
+            font-size: $font-size-small;
+            color: $text-color-secondary;
+          }
+        }
+        
+        .status-tag {
+          position: absolute;
+          top: $spacing-base;
+          right: $spacing-base;
+          padding: $spacing-mini $spacing-small;
+          border-radius: $border-radius-mini;
+          font-size: $font-size-small;
+          animation: pulse 2s infinite;
+          
+          &.playing {
+            background-color: $success-color;
+            color: white;
+          }
+          
+          &.waiting {
+            background-color: $warning-color;
+            color: white;
+          }
+          
+          &.ended {
+            background-color: $text-color-disabled;
+            color: white;
+          }
+        }
+        @keyframes pulse {
+          0% { opacity: 0.8; }
+          50% { opacity: 1; }
+          100% { opacity: 0.8; }
+        }
+      }
+    }
+  }
+  
+  .load-more {
+    margin: $spacing-large 0;
+    text-align: center;
+  }
+}
+
+.nut-empty {
+  margin: $spacing-large 0;
+  
+  &__description {
+    color: $text-color-secondary;
+    font-size: $font-size-base;
+  }
+}
+
+.nut-skeleton {
+  padding: $spacing-base;
+  background-color: $background-color-light;
+  border-radius: $border-radius-base;
+  box-shadow: $shadow-light;
+  margin-bottom: $spacing-base;
+}
+</style>

+ 84 - 67
src/pages/room/join/index.vue

@@ -50,6 +50,12 @@
               <text class="room-time">{{ formatTime(room.time) }}</text>
             </view>
           </view>
+          
+          <!-- 添加状态标签 -->
+          <view class="status-tag" :class="room.status ? room.status : 'unknown'">
+            {{ getStatusText(room.status) }}
+          </view>
+          
           <view class="join-icon">
             <right size="16" color="#999"></right>
           </view>
@@ -61,22 +67,13 @@
 </template>
 
 <script lang="ts">
-import { ref } from 'vue'
+import { ref, computed } from 'vue'
 import Taro from '@tarojs/taro'
 import { useUserStore } from '@/stores/user'
+import { useRoomStore, type RecentRoom } from '@/stores/room'
 import { Scan2, Right } from '@nutui/icons-vue-taro'
-import { roomAPI } from '@/services/api'
 import Tabbar from '@/components/Tabbar.vue'
 
-// 定义房间类型接口
-interface RecentRoom {
-  id: string;
-  name: string;
-  game: string;
-  time: number;
-  needPassword?: boolean;
-}
-
 export default {
   components: {
     Tabbar,
@@ -88,40 +85,42 @@ export default {
   onShow() {
     // 隐藏返回首页按钮
     Taro.hideHomeButton()
-    console.log('已隐藏返回首页按钮')
   },
   
   // Composition API
   setup() {
     const userStore = useUserStore()
+    const roomStore = useRoomStore()
 
     // 加入房间表单
     const roomCode = ref('')
     const password = ref('')
     const needPassword = ref(false)
 
-    // 最近房间列表 - 添加类型注解
-    const recentRooms = ref<RecentRoom[]>([])
+    // 从roomStore获取限制条数的最近房间列表
+    const recentRooms = computed(() => roomStore.limitedRecentRooms)
+    
+    // 处理页面参数 - 从其他页面跳转过来时使用
+    const handleRouteParams = (options: Record<string, string>) => {
+      if (options.roomId) {
+        roomCode.value = options.roomId
+      }
+      
+      if (options.needPassword === 'true') {
+        needPassword.value = true
+      }
+    }
     
-    // 获取最近房间
+    // 获取最近房间 - 改用新方法加载所有数据源
     const loadRecentRooms = async () => {
       // 确保用户是玩家角色
       userStore.setRole('player')
       
       try {
-        // 从本地存储获取最近房间
-        const recent = Taro.getStorageSync('recentRooms')
-        if (recent) {
-          recentRooms.value = JSON.parse(recent) as RecentRoom[]
-        }
-        
-        // 从API获取
-        const roomsData = await roomAPI.getRecentRooms();
-        if (roomsData && roomsData.length > 0) {
-          recentRooms.value = roomsData;
-          // 保存到本地存储
-          Taro.setStorageSync('recentRooms', JSON.stringify(roomsData))
-        }
+        // 使用store加载最近房间 - 这将加载完整的历史记录和活跃房间
+        await roomStore.loadActiveRooms()
+        await roomStore.loadEndedGames()
+        await roomStore.loadRecentRooms()
       } catch (error) {
         console.error('获取最近房间失败:', error)
       }
@@ -176,18 +175,10 @@ export default {
       try {
         Taro.showLoading({ title: '加入房间中...' })
         
-        // 验证房间
-        const checkResult = await roomAPI.checkRoomExist(roomCode.value);
+        // 使用Store中的统一方法,处理房间验证和加入
+        const joinResult = await roomStore.joinRoomById(roomCode.value, password.value)
         
-        if (!checkResult || !checkResult.exists) {
-          Taro.showToast({
-            title: '房间不存在',
-            icon: 'none'
-          })
-          return
-        }
-        
-        if (checkResult.needPassword && !password.value) {
+        if (joinResult.needPassword && !password.value) {
           needPassword.value = true
           Taro.showToast({
             title: '该房间需要密码',
@@ -196,32 +187,10 @@ export default {
           return
         }
         
-        // 验证密码
-        if (checkResult.needPassword) {
-          const pwdResult = await roomAPI.verifyRoomPassword(roomCode.value, password.value);
-          
-          if (!pwdResult || !pwdResult.valid) {
-            Taro.showToast({
-              title: '房间密码错误',
-              icon: 'none'
-            })
-            return
-          }
-        }
-        
-        // 加入房间
-        const joinResult = await roomAPI.joinRoom(roomCode.value);
-        
-        if (joinResult && joinResult.success) {
-          // 保存房间信息
-          userStore.setCurrentRoom(roomCode.value)
-          
-          // 跳转到等待页面
-          Taro.navigateTo({
-            url: `/pages/room/waiting/index?roomId=${roomCode.value}`
-          })
+        if (joinResult.success) {
+          roomStore.navigateToRoomPage(roomCode.value)
         } else {
-          throw new Error(joinResult?.message || '加入房间失败')
+          throw new Error(joinResult.message || '加入房间失败')
         }
       } catch (error: any) {
         console.error('加入房间失败:', error)
@@ -265,6 +234,20 @@ export default {
       return `${date.getMonth() + 1}月${date.getDate()}日`
     }
     
+    // 获取状态文本
+    const getStatusText = (status?: string) => {
+      switch(status) {
+        case 'playing':
+          return '进行中';
+        case 'waiting':
+          return '等待中';
+        case 'ended':
+          return '已结束';
+        default:
+          return '未知';
+      }
+    }
+    
     // 页面初始化时加载最近房间
     loadRecentRooms()
     
@@ -276,17 +259,21 @@ export default {
       scanQrCode,
       joinRoom,
       joinRecentRoom,
-      formatTime
+      formatTime,
+      getStatusText,
+      handleRouteParams
     }
   },
   
   // 生命周期钩子 - 页面加载
-  onLoad() {
-    // 其他初始化逻辑
+  onLoad(options) {
+    // 使用setup中返回的方法处理参数
+    this.handleRouteParams(options)
   }
 }
 </script>
 
+<!-- CSS部分保持不变 -->
 <style lang="scss">
 .join-room-page {
   padding: $spacing-base;
@@ -367,6 +354,7 @@ export default {
         justify-content: space-between;
         padding: $spacing-large $spacing-base;
         border-bottom: 1px solid $border-color-light;
+        position: relative;
         
         &:last-child {
           border-bottom: none;
@@ -402,6 +390,35 @@ export default {
           }
         }
         
+        .status-tag {
+          padding: $spacing-mini $spacing-small;
+          border-radius: $border-radius-mini;
+          font-size: $font-size-small;
+          position: absolute;
+          top: $spacing-base;
+          right: $spacing-large * 2; // 放在右箭头图标左侧
+          
+          &.playing {
+            background-color: $success-color;
+            color: white;
+          }
+          
+          &.waiting {
+            background-color: $warning-color;
+            color: white;
+          }
+          
+          &.ended {
+            background-color: $text-color-disabled;
+            color: white;
+          }
+          
+          &.unknown {
+            background-color: $text-color-secondary;
+            color: white;
+          }
+        }
+        
         .join-icon {
           padding: $spacing-small;
         }

+ 252 - 142
src/services/api/room.ts

@@ -1,15 +1,38 @@
-//处理房间创建、加入、退出等操作
+// services/api/room.ts - 处理房间相关API请求
+
 import Taro from '@tarojs/taro'
-import { useUserStore } from '@/stores/user'
-import { useRoomStore, type Room, type RoomUser } from '@/stores/room'
 
-// 添加 RecentRoom 接口定义
-interface RecentRoom {
-  id: string;
-  name: string;
-  game: string;
-  time: number;
-  needPassword?: boolean;
+// 导出类型定义以便共享
+export interface RoomUser {
+  id: string
+  name: string
+  avatar: string
+  role: 'hoster' | 'player'
+  joinTime: number
+}
+
+export interface Room {
+  id: string
+  name: string
+  gameId: string
+  gameTitle: string
+  maxPlayers: number
+  visibility: 'private' | 'public'
+  password?: string
+  hosterId: string
+  hosterName: string
+  createTime: number
+  status: 'waiting' | 'playing' | 'ended'
+  users: RoomUser[]
+}
+
+export interface RecentRoom {
+  id: string
+  name: string
+  game: string
+  time: number
+  needPassword?: boolean
+  status?: string
 }
 
 // Mock房间数据(测试用)
@@ -59,10 +82,10 @@ const mockRooms = {
   }
 };
 
-// 房间API - 简化
+// 房间API - 重构后的纯净
 export const roomAPI = {
   // 获取最近房间
-  async getRecentRooms() {
+  async getRecentRooms(): Promise<RecentRoom[]> {
     try {
       // 从缓存获取
       const cachedRooms = Taro.getStorageSync('recentRooms')
@@ -72,8 +95,8 @@ export const roomAPI = {
       
       // 返回默认mock数据
       return [
-        { id: 'room_001', name: '海龟汤房间', game: '海龟汤', time: Date.now() - 3600000, needPassword: false },
-        { id: 'room_002', name: '狼人杀小队', game: '狼人杀', time: Date.now() - 86400000, needPassword: true }
+        { id: 'room_001', name: '海龟汤房间', game: '海龟汤', time: Date.now() - 3600000, needPassword: false, status: 'waiting' },
+        { id: 'room_002', name: '狼人杀小队', game: '狼人杀', time: Date.now() - 86400000, needPassword: true, status: 'playing' }
       ]
     } catch (error) {
       console.error('获取最近房间失败:', error)
@@ -82,7 +105,7 @@ export const roomAPI = {
   },
   
   // 检查房间是否存在
-  async checkRoomExist(roomId: string) {
+  async checkRoomExist(roomId: string): Promise<{exists: boolean, needPassword: boolean}> {
     try {
       // 先查询本地存储
       const recentRooms = Taro.getStorageSync('recentRooms')
@@ -103,15 +126,15 @@ export const roomAPI = {
         }
       }
       
-      return { exists: false }
+      return { exists: false, needPassword: false }
     } catch (error) {
       console.error('检查房间失败:', error)
-      return { exists: false }
+      return { exists: false, needPassword: false }
     }
   },
   
   // 验证房间密码
-  async verifyRoomPassword(roomId: string, password: string) {
+  async verifyRoomPassword(roomId: string, password: string): Promise<{valid: boolean}> {
     try {
       // 简化实现:仅检查mock数据中的密码
       const mockRoom = mockRooms[roomId]
@@ -126,44 +149,12 @@ export const roomAPI = {
     }
   },
   
-  // 创建新房间
-  async createRoom(roomData: Room) {
-    const userStore = useUserStore()
-    
-    // 检查用户是否注册
-    if (!userStore.isRegistered) {
-      return new Promise<{roomId: string | null, success: boolean, message?: string}>((resolve) => {
-        Taro.showModal({
-          title: '完善信息',
-          content: '创建房间需要您的昵称和头像信息,是否现在完善?',
-          confirmText: '立即完善',
-          cancelText: '暂不',
-          success: async (res) => {
-            if (res.confirm) {
-              const result = await userStore.registerUser()
-              if (result) {
-                // 用户注册成功,继续创建房间
-                const roomResult = await this.createRoomAction(roomData)
-                resolve(roomResult)
-              } else {
-                resolve({ roomId: null, success: false, message: '用户信息获取失败' })
-              }
-            } else {
-              resolve({ roomId: null, success: false, message: '需要完善用户信息' })
-            }
-          }
-        })
-      })
-    } else {
-      // 用户已注册,直接创建房间
-      return this.createRoomAction(roomData)
-    }
-  },
-  
-  // 实际创建房间的操作
-  async createRoomAction(roomData: Room) {
+  // 创建新房间 - 不再依赖store,返回纯数据
+  async createRoom(roomData: Room, userInfo: {openid: string, nickname: string, avatar: string}): Promise<{roomId: string | null, success: boolean, message?: string}> {
     try {
-      const roomStore = useRoomStore()
+      if (!userInfo.openid) {
+        return { roomId: null, success: false, message: '用户未登录' }
+      }
       
       // 本地存储一份房间信息
       const recentRooms = Taro.getStorageSync('recentRooms') || '[]'
@@ -175,7 +166,8 @@ export const roomAPI = {
         name: roomData.name,
         game: roomData.gameTitle,
         time: roomData.createTime,
-        needPassword: roomData.visibility === 'private'
+        needPassword: roomData.visibility === 'private',
+        status: roomData.status
       }
       
       rooms.unshift(roomInfo)
@@ -186,9 +178,6 @@ export const roomAPI = {
       
       Taro.setStorageSync('recentRooms', JSON.stringify(rooms))
       
-      // 在内存中保存当前房间
-      roomStore.setCurrentRoom(roomData)
-      
       // 简化实现:仅存储在本地缓存
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
       const roomsObj = JSON.parse(allRooms)
@@ -202,55 +191,9 @@ export const roomAPI = {
     }
   },
   
-  // 加入房间
-  async joinRoom(roomId: string) {
-    const userStore = useUserStore()
-    
-    // 检查用户是否注册
-    if (!userStore.isRegistered) {
-      return new Promise<{success: boolean, message?: string}>((resolve) => {
-        Taro.showModal({
-          title: '完善信息',
-          content: '加入房间需要您的昵称和头像信息,是否现在完善?',
-          confirmText: '立即完善',
-          cancelText: '暂不',
-          success: async (res) => {
-            if (res.confirm) {
-              const result = await userStore.registerUser()
-              if (result) {
-                // 用户注册成功,继续加入房间
-                const joinResult = await this.joinRoomAction(roomId)
-                resolve(joinResult)
-              } else {
-                resolve({ success: false, message: '用户信息获取失败' })
-              }
-            } else {
-              resolve({ success: false, message: '需要完善用户信息' })
-            }
-          }
-        })
-      })
-    } else {
-      // 用户已注册,直接加入房间
-      return this.joinRoomAction(roomId)
-    }
-  },
-  
-  // 实际加入房间的操作
-  async joinRoomAction(roomId: string) {
+  // 加入房间 - 纯粹的数据操作,不依赖store
+  async joinRoom(roomId: string, userData: RoomUser): Promise<{success: boolean, roomData?: Room, message?: string}> {
     try {
-      const userStore = useUserStore()
-      const roomStore = useRoomStore()
-      
-      // 构建用户数据
-      const userData: RoomUser = {
-        id: userStore.openid || `temp_${Date.now()}`,
-        name: userStore.nickname || '玩家',
-        avatar: userStore.avatar || '',
-        role: 'player',
-        joinTime: Date.now()
-      }
-      
       // 查找房间 - 先从本地缓存查询
       let room: Room | null = null
       
@@ -285,22 +228,20 @@ export const roomAPI = {
         Taro.setStorageSync('allRooms', JSON.stringify(roomsObj))
       }
       
-      // 保存到roomStore
-      roomStore.setCurrentRoom(room)
-      
-      // 设置当前房间ID
-      userStore.setCurrentRoom(roomId)
-      
       // 保存到最近加入的房间
-      this.saveToRecentRooms({
+      const roomInfo: RecentRoom = {
         id: room.id,
         name: room.name,
         game: room.gameTitle,
         time: Date.now(),
-        needPassword: room.visibility === 'private'
-      })
+        needPassword: room.visibility === 'private',
+        status: room.status
+      }
       
-      return { success: true }
+      // 更新最近房间列表
+      this.saveToRecentRooms(roomInfo)
+      
+      return { success: true, roomData: room }
     } catch (error) {
       console.error('加入房间失败:', error)
       return { success: false, message: '加入房间失败' }
@@ -308,7 +249,7 @@ export const roomAPI = {
   },
   
   // 保存到最近加入的房间
-  async saveToRecentRooms(roomInfo: RecentRoom) {
+  async saveToRecentRooms(roomInfo: RecentRoom): Promise<void> {
     try {
       const recentRooms = Taro.getStorageSync('recentRooms') || '[]'
       const rooms = JSON.parse(recentRooms)
@@ -329,21 +270,14 @@ export const roomAPI = {
       }
       
       Taro.setStorageSync('recentRooms', JSON.stringify(rooms))
-      
-      // 更新roomStore的recentRooms
-      const roomStore = useRoomStore()
-      roomStore.loadRecentRooms(rooms)
     } catch (error) {
       console.error('保存最近房间失败:', error)
     }
   },
   
   // 退出房间
-  async leaveRoom(roomId: string) {
+  async leaveRoom(roomId: string, userId: string): Promise<{success: boolean, message?: string}> {
     try {
-      const userStore = useUserStore()
-      const roomStore = useRoomStore()
-      
       // 从本地缓存找到房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
       const roomsObj = JSON.parse(allRooms)
@@ -352,17 +286,13 @@ export const roomAPI = {
         const room = roomsObj[roomId]
         
         // 过滤掉当前用户
-        room.users = room.users.filter(u => u.id !== userStore.openid)
+        room.users = room.users.filter(u => u.id !== userId)
         
         // 更新本地缓存
         roomsObj[roomId] = room
         Taro.setStorageSync('allRooms', JSON.stringify(roomsObj))
       }
       
-      // 清除当前房间信息
-      userStore.setCurrentRoom('')
-      roomStore.setCurrentRoom(null)
-      
       return { success: true }
     } catch (error) {
       console.error('退出房间失败:', error)
@@ -371,25 +301,19 @@ export const roomAPI = {
   },
   
   // 获取房间信息
-  async getRoomInfo(roomId: string) {
+  async getRoomInfo(roomId: string): Promise<Room | null> {
     try {
       // 检查本地缓存中是否有该房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
       const roomsObj = JSON.parse(allRooms)
       
       if (roomsObj[roomId]) {
-        const roomStore = useRoomStore()
-        const roomData = roomsObj[roomId]
-        roomStore.setCurrentRoom(roomData)
-        return roomData
+        return roomsObj[roomId]
       }
       
       // 如果本地没有,使用mock数据
       if (mockRooms[roomId]) {
-        const roomStore = useRoomStore()
-        const roomData = mockRooms[roomId]
-        roomStore.setCurrentRoom(roomData as Room)
-        return roomData
+        return mockRooms[roomId] as Room
       }
       
       return null
@@ -397,7 +321,193 @@ export const roomAPI = {
       console.error('获取房间信息失败:', error)
       return null
     }
+  },
+
+  // 获取进行中的房间
+  async getActiveRooms(userId: string): Promise<Room[]> {
+    try {
+      // 从缓存或模拟数据获取所有房间
+      const allRooms = Taro.getStorageSync('allRooms') || '{}'
+      const roomsObj = JSON.parse(allRooms)
+      
+      // 添加类型断言,确保 Object.values 返回的是 Room[] 类型
+      const activeRooms = Object.values(roomsObj) as Room[];
+      
+      // 过滤出用户参与的且状态为"waiting"或"playing"的房间
+      const userActiveRooms = activeRooms.filter(room => {
+        return room.status !== 'ended' && 
+          room.users && 
+          room.users.some(u => u.id === userId)
+      })
+      
+      // 添加mock数据用于测试
+      if (userActiveRooms.length === 0) {
+        return [
+          { 
+            id: 'room_001', 
+            name: '欢乐海龟汤', 
+            gameId: '1', 
+            gameTitle: '海龟汤',
+            hosterId: 'host_123',
+            hosterName: '小明',
+            status: 'playing',
+            users: [{ id: userId, name: '玩家', avatar: '', role: 'player', joinTime: Date.now() }],
+            createTime: Date.now() - 3600000,
+            maxPlayers: 10,
+            visibility: 'public'
+          },
+          { 
+            id: 'room_002', 
+            name: '趣味谜题', 
+            gameId: '1', 
+            gameTitle: '海龟汤',
+            hosterId: userId,
+            hosterName: '玩家',
+            status: 'waiting',
+            users: [{ id: userId, name: '玩家', avatar: '', role: 'hoster', joinTime: Date.now() }],
+            createTime: Date.now() - 7200000,
+            maxPlayers: 8,
+            visibility: 'public'
+          }
+        ] as Room[]
+      }
+      
+      return userActiveRooms
+    } catch (error) {
+      console.error('获取进行中房间失败:', error)
+      return []
+    }
+  },
+
+  // 获取已结束的游戏
+  async getEndedGames(userId: string): Promise<any[]> {
+    try {
+      // 从缓存获取
+      const endedGames = Taro.getStorageSync('endedGames') || '[]'
+      const games = JSON.parse(endedGames)
+      
+      // 筛选用户参与的游戏
+      const userGames = games.filter((game: any) => 
+        game.users && game.users.some((u: any) => u.id === userId)
+      )
+      
+      // 添加mock数据用于测试
+      if (userGames.length === 0) {
+        return [
+          { 
+            id: 'game_001', 
+            roomId: 'room_003',
+            roomName: '神秘的手表',
+            gameId: '1', 
+            gameTitle: '海龟汤', 
+            hosterId: 'host_123',
+            hosterName: '小李',
+            endTime: Date.now() - 86400000, // 昨天
+            duration: 45, // 分钟
+            users: [{ id: userId }]
+          },
+          { 
+            id: 'game_002', 
+            roomId: 'room_004',
+            roomName: '迷路的青蛙',
+            gameId: '1', 
+            gameTitle: '海龟汤', 
+            hosterId: userId,
+            hosterName: '玩家',
+            endTime: Date.now() - 172800000, // 前天
+            duration: 28, // 分钟
+            users: [{ id: userId }]
+          },
+          { 
+            id: 'game_003', 
+            roomId: 'room_005',
+            roomName: '动物园奇案',
+            gameId: '2', 
+            gameTitle: '谜星探案', 
+            hosterId: 'host_456',
+            hosterName: '探长',
+            endTime: Date.now() - 259200000, // 3天前
+            duration: 35, // 分钟
+            users: [{ id: userId }]
+          }
+        ]
+      }
+      
+      return userGames
+    } catch (error) {
+      console.error('获取已结束游戏失败:', error)
+      return []
+    }
+  },
+
+  // 获取我创建的房间/游戏
+  async getCreatedRooms(userId: string): Promise<any[]> {
+    try {
+      // 合并进行中和已结束的,筛选出我创建的
+      const activeRooms = await this.getActiveRooms(userId)
+      const endedGames = await this.getEndedGames(userId)
+      
+      const createdRooms = activeRooms.filter((room: any) => room.hosterId === userId)
+      const createdGames = endedGames.filter((game: any) => game.hosterId === userId)
+      
+      // 合并结果并按时间排序
+      const result = [
+        ...createdRooms.map((room: any) => ({
+          ...room,
+          isActive: true,
+          time: room.createTime
+        })),
+        ...createdGames.map((game: any) => ({
+          ...game,
+          isActive: false,
+          time: game.endTime
+        }))
+      ].sort((a, b) => b.time - a.time)
+      
+      return result
+    } catch (error) {
+      console.error('获取我创建的房间/游戏失败:', error)
+      return []
+    }
+  },
+
+  // 获取我参与的房间/游戏
+  async getJoinedRooms(userId: string): Promise<any[]> {
+    try {
+      // 合并进行中和已结束的,筛选出我参与但不是我创建的
+      const activeRooms = await this.getActiveRooms(userId)
+      const endedGames = await this.getEndedGames(userId)
+      
+      const joinedRooms = activeRooms.filter((room: any) => 
+        room.hosterId !== userId &&
+        room.users && 
+        room.users.some((u: any) => u.id === userId)
+      )
+      
+      const joinedGames = endedGames.filter((game: any) => 
+        game.hosterId !== userId &&
+        game.users && 
+        game.users.some((u: any) => u.id === userId)
+      )
+      
+      // 合并结果并按时间排序
+      const result = [
+        ...joinedRooms.map((room: any) => ({
+          ...room,
+          isActive: true,
+          time: room.createTime
+        })),
+        ...joinedGames.map((game: any) => ({
+          ...game,
+          isActive: false,
+          time: game.endTime
+        }))
+      ].sort((a, b) => b.time - a.time)
+      
+      return result
+    } catch (error) {
+      console.error('获取我参与的房间/游戏失败:', error)
+      return []
+    }
   }
-  
-  // 监听房间变化功能已被移除,以简化实现
 }

+ 340 - 64
src/stores/room.ts

@@ -1,53 +1,214 @@
-//管理房间创建、加入、退出等状态
+// stores/room.ts - 管理房间状态
 import { defineStore } from 'pinia'
-import { ref } from 'vue'
+import { ref, reactive, computed } from 'vue'
+import { roomAPI, type Room, type RoomUser, type RecentRoom } from '@/services/api/room'
+import { useUserStore } from '@/stores/user'
+import Taro from '@tarojs/taro'
 
-// 房间用户类型
-export interface RoomUser {
-  id: string
-  name: string
-  avatar: string
-  role: 'hoster' | 'player'
-  joinTime: number
-}
-
-// 房间类型
-export interface Room {
-  id: string
-  name: string
-  gameId: string
-  gameTitle: string
-  maxPlayers: number
-  visibility: 'private' | 'public'
-  password?: string
-  hosterId: string
-  hosterName: string
-  createTime: number
-  status: 'waiting' | 'playing' | 'ended'
-  users: RoomUser[]
-}
-
-// 最近房间类型
-export interface RecentRoom {
-  id: string
-  name: string
-  game: string
-  time: number
-  needPassword?: boolean
-}
+// 重新导出类型以便其他组件使用
+export type { Room, RoomUser, RecentRoom } from '@/services/api/room'
 
 export const useRoomStore = defineStore('room', () => {
+  // 获取userStore
+  const userStore = useUserStore()
+  
   // 当前房间信息
   const currentRoom = ref<Room | null>(null)
   
   // 最近加入的房间
   const recentRooms = ref<RecentRoom[]>([])
   
+  // 历史记录状态
+  const historyState = reactive({
+    activeRooms: [] as Room[],
+    endedGames: [] as any[],
+    isLoading: false
+  })
+  
   // 设置当前房间
   function setCurrentRoom(room: Room | null) {
     currentRoom.value = room
   }
   
+  // 加载最近房间列表 - 使用统一的数据源
+  async function loadRecentRooms() {
+    // 首先确保活跃房间已加载 
+    if (historyState.activeRooms.length === 0) {
+      await loadActiveRooms()
+    }
+    
+    // 从已加载的活跃房间和历史记录构建最近房间列表
+    const recentList: RecentRoom[] = [
+      // 活跃房间
+      ...historyState.activeRooms.map(room => ({
+        id: room.id,
+        name: room.name,
+        game: room.gameTitle,
+        time: room.createTime,
+        needPassword: room.visibility === 'private',
+        status: room.status
+      })),
+      // 已结束游戏
+      ...historyState.endedGames.filter((game, index) => index < 5).map(game => ({
+        id: game.roomId || game.id,
+        name: game.roomName,
+        game: game.gameTitle,
+        time: game.endTime,
+        status: 'ended'
+      }))
+    ]
+    
+    // 按时间排序并只保留最近的条目
+    recentRooms.value = recentList
+      .sort((a, b) => b.time - a.time)
+      .slice(0, 10)
+    
+    return recentRooms.value
+  }
+  
+  // 加载活跃房间 - 统一数据源
+  async function loadActiveRooms() {
+    try {
+      historyState.activeRooms = await roomAPI.getActiveRooms(userStore.openid)
+      return historyState.activeRooms
+    } catch (error) {
+      console.error('加载活跃房间失败:', error)
+      return []
+    }
+  }
+  
+  // 加载已结束游戏 - 统一数据源
+  async function loadEndedGames() {
+    try {
+      historyState.endedGames = await roomAPI.getEndedGames(userStore.openid)
+      return historyState.endedGames
+    } catch (error) {
+      console.error('加载已结束游戏失败:', error)
+      return []
+    }
+  }
+  
+  // 检查用户是否已注册,如果未注册则返回错误
+  async function checkUserRegistered(): Promise<{valid: boolean, message?: string}> {
+    if (!userStore.isRegistered) {
+      return { 
+        valid: false, 
+        message: '需要先完善个人信息才能继续操作' 
+      }
+    }
+    return { valid: true }
+  }
+  
+  // 创建房间 - 调用API并更新store
+  async function createRoom(roomData: Room) {
+    try {
+      // 检查用户是否已注册
+      const userCheck = await checkUserRegistered()
+      if (!userCheck.valid) {
+        return { roomId: null, success: false, message: userCheck.message }
+      }
+      
+      const result = await roomAPI.createRoom(roomData, {
+        openid: userStore.openid,
+        nickname: userStore.nickname,
+        avatar: userStore.avatar
+      })
+      
+      if (result.success && result.roomId) {
+        // 获取完整房间信息
+        const room = await roomAPI.getRoomInfo(result.roomId)
+        if (room) {
+          setCurrentRoom(room)
+          
+          // 更新最近房间列表
+          await loadRecentRooms()
+        }
+      }
+      
+      return result
+    } catch (error) {
+      console.error('创建房间失败:', error)
+      return { roomId: null, success: false, message: '创建房间失败' }
+    }
+  }
+  
+  // 加入房间 - 调用API并更新store
+  async function joinRoom(roomId: string) {
+    try {
+      // 检查用户是否已注册
+      const userCheck = await checkUserRegistered()
+      if (!userCheck.valid) {
+        return { success: false, message: userCheck.message }
+      }
+      
+      // 构建用户数据
+      const userData: RoomUser = {
+        id: userStore.openid,
+        name: userStore.nickname,
+        avatar: userStore.avatar,
+        role: 'player',
+        joinTime: Date.now()
+      }
+      
+      // 调用API加入房间
+      const result = await roomAPI.joinRoom(roomId, userData)
+      
+      if (result.success && result.roomData) {
+        // 更新当前房间
+        setCurrentRoom(result.roomData)
+        
+        // 更新用户当前房间ID
+        userStore.setCurrentRoom(roomId)
+        
+        // 重新加载活跃房间和最近房间列表以保持数据一致性
+        await loadActiveRooms()
+        await loadRecentRooms()
+      }
+      
+      return result
+    } catch (error) {
+      console.error('加入房间失败:', error)
+      return { success: false, message: '加入房间失败' }
+    }
+  }
+  
+  // 退出房间 - 调用API并更新store
+  async function leaveRoom(roomId: string) {
+    try {
+      const result = await roomAPI.leaveRoom(roomId, userStore.openid)
+      
+      if (result.success) {
+        // 清除当前房间信息
+        clearRoom()
+        
+        // 清除用户当前房间ID
+        userStore.setCurrentRoom('')
+      }
+      
+      return result
+    } catch (error) {
+      console.error('退出房间失败:', error)
+      return { success: false, message: '退出房间失败' }
+    }
+  }
+  
+  // 加载房间信息 - 通过API获取
+  async function loadRoomInfo(roomId: string) {
+    try {
+      const room = await roomAPI.getRoomInfo(roomId)
+      
+      if (room) {
+        setCurrentRoom(room)
+        return { success: true, room }
+      }
+      
+      return { success: false, message: '房间不存在' }
+    } catch (error) {
+      console.error('加载房间信息失败:', error)
+      return { success: false, message: '加载房间信息失败' }
+    }
+  }
+  
   // 添加用户到当前房间
   function addUserToRoom(user: RoomUser) {
     if (currentRoom.value) {
@@ -93,29 +254,29 @@ export const useRoomStore = defineStore('room', () => {
     }
   }
   
-  // 加载最近房间列表
-  function loadRecentRooms(rooms: RecentRoom[]) {
-    recentRooms.value = rooms
+  // 清除当前房间
+  function clearRoom() {
+    currentRoom.value = null
   }
   
-  // 获取当前房间用户数量
+  // 辅助方法:获取当前房间用户数量
   function getUserCount() {
     return currentRoom.value?.users.length || 0
   }
   
-  // 检查房间是否已满
+  // 辅助方法:检查房间是否已满
   function isRoomFull() {
     if (!currentRoom.value) return false
     return currentRoom.value.users.length >= currentRoom.value.maxPlayers
   }
   
-  // 获取主持人信息
+  // 辅助方法:获取主持人信息
   function getHoster() {
     if (!currentRoom.value) return null
     return currentRoom.value.users.find(u => u.role === 'hoster') || null
   }
   
-  // 获取玩家列表(不包括主持人)
+  // 辅助方法:获取玩家列表(不包括主持人)
   function getPlayers() {
     if (!currentRoom.value) return []
     return currentRoom.value.users.filter(u => u.role === 'player')
@@ -144,47 +305,162 @@ export const useRoomStore = defineStore('room', () => {
     }
   }
   
-  // 清除当前房间
-  function clearRoom() {
-    currentRoom.value = null
+  // 加载历史记录 - 修改为使用共享数据源
+  async function loadHistoryByTab(tabIndex: number) {
+    historyState.isLoading = true
+    
+    try {
+      if (tabIndex === 0) { // 最近游戏
+        // 加载进行中的房间和已结束的游戏
+        await loadActiveRooms()
+        await loadEndedGames()
+        
+      } else if (tabIndex === 1) { // 我创建的
+        // 确保数据已加载
+        await loadActiveRooms()
+        await loadEndedGames()
+        
+        // 筛选我创建的内容
+        historyState.activeRooms = historyState.activeRooms.filter(
+          room => room.hosterId === userStore.openid
+        )
+        historyState.endedGames = historyState.endedGames.filter(
+          game => game.hosterId === userStore.openid
+        )
+        
+      } else if (tabIndex === 2) { // 我参与的
+        // 确保数据已加载
+        await loadActiveRooms()
+        await loadEndedGames()
+        
+        // 筛选我参与但不是我创建的内容
+        historyState.activeRooms = historyState.activeRooms.filter(
+          room => room.hosterId !== userStore.openid && 
+                room.users && 
+                room.users.some(u => u.id === userStore.openid)
+        )
+        historyState.endedGames = historyState.endedGames.filter(
+          game => game.hosterId !== userStore.openid &&
+                game.users && 
+                game.users.some(u => u.id === userStore.openid)
+        )
+      }
+      
+      // 每次加载历史记录后更新最近房间列表
+      await loadRecentRooms()
+    } catch (error) {
+      console.error('加载历史记录失败:', error)
+    } finally {
+      historyState.isLoading = false
+    }
   }
   
-  // 添加房间到最近列表
-  function addToRecentRooms(room: RecentRoom) {
-    // 检查是否已存在
-    const existingIndex = recentRooms.value.findIndex(r => r.id === room.id)
-    if (existingIndex !== -1) {
-      // 移除已存在的
-      recentRooms.value.splice(existingIndex, 1)
+  // 为join页面提供的限制条数的计算属性
+  const limitedRecentRooms = computed(() => {
+    return recentRooms.value.slice(0, 3) // 只显示最近3条
+  })
+  
+  // 修改 joinRoomById 方法,确保返回房间数据
+  async function joinRoomById(roomId: string, password?: string): Promise<{
+    success: boolean, 
+    message?: string, 
+    needPassword?: boolean,
+    roomData?: Room  // 添加房间数据
+  }> {
+    try {
+      // 1. 检查用户是否注册
+      const userCheck = await checkUserRegistered()
+      if (!userCheck.valid) {
+        return { success: false, message: userCheck.message }
+      }
+      
+      // 2. 验证房间
+      const checkResult = await roomAPI.checkRoomExist(roomId)
+      if (!checkResult || !checkResult.exists) {
+        return { success: false, message: '房间不存在' }
+      }
+      
+      // 3. 验证密码
+      if (checkResult.needPassword && !password) {
+        return { success: false, message: '需要密码', needPassword: true }
+      }
+      
+      if (checkResult.needPassword && password) {
+        const pwdResult = await roomAPI.verifyRoomPassword(roomId, password)
+        if (!pwdResult || !pwdResult.valid) {
+          return { success: false, message: '房间密码错误' }
+        }
+      }
+      
+      // 4. 加入房间
+      const joinResult = await joinRoom(roomId)
+      
+      // 返回结果时同时包含房间数据
+      return joinResult
+      
+    } catch (error) {
+      console.error('加入房间失败:', error)
+      return { success: false, message: '加入房间失败' }
     }
+  }
+  
+  // 添加一个根据房间状态执行跳转的辅助方法
+  function navigateToRoomPage(roomId: string) {
+    // 获取当前房间状态
+    const status = currentRoom.value?.status
     
-    // 添加到最前面
-    recentRooms.value.unshift(room)
-    
-    // 只保留最近10个房间
-    if (recentRooms.value.length > 10) {
-      recentRooms.value.pop()
+    if (status === 'playing') {
+      // 如果房间状态为"进行中",跳转到游戏页面
+      Taro.navigateTo({
+        url: `/pages/room/play/index?roomId=${roomId}`
+      })
+    } else {
+      // 如果房间状态为"等待中"或其他状态,跳转到等待页面
+      Taro.navigateTo({
+        url: `/pages/room/waiting/index?roomId=${roomId}`
+      })
     }
   }
   
   // 返回状态和方法
   return {
+    // 状态
     currentRoom,
     recentRooms,
+    limitedRecentRooms, // 新增计算属性
+    historyState,
+    
+    // 房间操作方法
     setCurrentRoom,
+    createRoom,
+    joinRoom,
+    leaveRoom,
+    loadRoomInfo,
+    loadRecentRooms,
+    loadActiveRooms,
+    loadEndedGames,
+    clearRoom,
+    
+    // 用户管理方法
     addUserToRoom,
     removeUserFromRoom,
     updateUserInRoom,
+    isUserInRoom,
+    getUserRole,
+    
+    // 房间属性方法
     updateRoomStatus,
-    loadRecentRooms,
+    updateRoomSettings,
+    
+    // 辅助方法
     getUserCount,
     isRoomFull,
     getHoster,
     getPlayers,
-    isUserInRoom,
-    getUserRole,
-    updateRoomSettings,
-    clearRoom,
-    addToRecentRooms
+    
+    // 历史记录方法
+    loadHistoryByTab,
+    joinRoomById, // 添加新方法
+    navigateToRoomPage, // 添加新方法
   }
 })