Browse Source

分离type数据结构

wuzj 1 week ago
parent
commit
b692dc227c

+ 9 - 2
README.md

@@ -375,9 +375,16 @@ src/
 │       ├── turtlesoup.ts    # 海龟汤游戏 Store
 │       ├── werewolf.ts      # 狼人杀游戏 Store
 │       └── script.ts        # 剧本杀游戏 Store
-├── types/              # TypeScript类型
+├── types/              # 数据结构定义
 │   ├── global.d.ts      # 全局
-│   └── vue.d.ts         # vue
+│   ├── user.ts              # 用户数据结构
+│   ├── room.ts              # 房间数据结构
+│   ├── game.ts              # 游戏定义数据结构
+│   └── games/               # 特定游戏 数据结构 目录
+│       ├── index.ts         # 导出所有游戏 数据结构
+│       ├── turtlesoup.ts    # 海龟汤游戏 数据结构
+│       ├── werewolf.ts      # 狼人杀游戏 数据结构
+│       └── script.ts        # 剧本杀游戏 数据结构
 └── utils/              # 工具函数
     ├── common.ts       # 通用工具
     ├── format.ts       # 格式化工具

+ 0 - 3
components.d.ts

@@ -27,9 +27,6 @@ declare module 'vue' {
     NutTabbarItem: typeof import('@nutui/nutui-taro')['TabbarItem']
     NutTabPane: typeof import('@nutui/nutui-taro')['TabPane']
     NutTabs: typeof import('@nutui/nutui-taro')['Tabs']
-    PlayerList: typeof import('./src/components/PlayerList/index.vue')['default']
-    PlayerOnly: typeof import('./src/components/PlayerOnly/index.vue')['default']
-    QuestionCard: typeof import('./src/components/QuestionCard/index.vue')['default']
     RoomCode: typeof import('./src/components/RoomCode/index.vue')['default']
     Tabbar: typeof import('./src/components/Tabbar.vue')['default']
     WereWolf: typeof import('./src/components/WereWolf.vue')['default']

+ 0 - 3
src/app.ts

@@ -10,8 +10,6 @@ import { useUserStore } from './stores/user'
 const App = createApp({
   // 入口组件不需要实现render方法,即使实现了也会被忽略
   onLaunch() {
-    // 注释掉云环境初始化,直到需要时再启用
-    /*
     if (process.env.TARO_ENV === 'weapp') {
       const Taro = require('@tarojs/taro')
       Taro.cloud.init({
@@ -21,7 +19,6 @@ const App = createApp({
       })
       console.log('已启用微信云开发默认环境')
     }
-    */
     
     // 当小程序初始化完成时
     const tabBarStore = useTabBarStore()

+ 0 - 152
src/components/PlayerList/index.vue

@@ -1,152 +0,0 @@
-<template>
-    <view class="player-list">
-      <view class="player-list__title">
-        玩家 ({{ players.length }}/{{ maxPlayers }})
-      </view>
-      <view class="player-list__content">
-        <view 
-          v-for="(player, index) in players" 
-          :key="player.id" 
-          class="player-list__item"
-        >
-          <view class="player-list__avatar">
-            <image :src="player.avatarUrl" mode="aspectFill" />
-            <view 
-              v-if="player.role === 'host'" 
-              class="player-list__role player-list__role--host"
-            >
-              主持人
-            </view>
-            <view 
-              v-else 
-              class="player-list__role player-list__role--player"
-            >
-              玩家
-            </view>
-          </view>
-          <view class="player-list__name">{{ player.nickname }}</view>
-          <view v-if="showStatus" class="player-list__status">
-            <view 
-              v-if="player.isReady" 
-              class="player-list__ready player-list__ready--true"
-            >
-              已准备
-            </view>
-            <view 
-              v-else 
-              class="player-list__ready player-list__ready--false"
-            >
-              未准备
-            </view>
-          </view>
-        </view>
-      </view>
-    </view>
-  </template>
-  
-  <script setup lang="ts">
-  import { defineProps } from 'vue';
-  import type { RoomMember } from '@/types/room';
-  
-  defineProps({
-    players: { 
-      type: Array as () => RoomMember[], 
-      required: true 
-    },
-    maxPlayers: { 
-      type: Number, 
-      default: 10 
-    },
-    showStatus: { 
-      type: Boolean, 
-      default: true 
-    }
-  });
-  </script>
-  
-  <style lang="scss">
-  .player-list {
-    margin: $spacing-base 0;
-    
-    &__title {
-      font-size: $font-size-medium;
-      font-weight: $font-weight-medium;
-      color: $text-color-primary;
-      margin-bottom: $spacing-base;
-    }
-    
-    &__content {
-      display: flex;
-      flex-wrap: wrap;
-      gap: $spacing-base;
-    }
-    
-    &__item {
-      display: flex;
-      flex-direction: column;
-      align-items: center;
-      width: 60px;
-    }
-    
-    &__avatar {
-      position: relative;
-      width: 44px;
-      height: 44px;
-      border-radius: $border-radius-circle;
-      overflow: hidden;
-      margin-bottom: $spacing-mini;
-      
-      image {
-        width: 100%;
-        height: 100%;
-      }
-    }
-    
-    &__role {
-      position: absolute;
-      bottom: 0;
-      left: 0;
-      right: 0;
-      font-size: 10px;
-      padding: 1px 0;
-      text-align: center;
-      color: $text-color-light;
-      
-      &--host {
-        background-color: $orange-color;
-      }
-      
-      &--player {
-        background-color: $blue-light-color;
-      }
-    }
-    
-    &__name {
-      font-size: $font-size-small;
-      color: $text-color-primary;
-      max-width: 60px;
-      text-align: center;
-      @include text-ellipsis;
-    }
-    
-    &__status {
-      margin-top: $spacing-mini;
-    }
-    
-    &__ready {
-      font-size: 10px;
-      padding: 1px $spacing-mini;
-      border-radius: $border-radius-mini;
-      
-      &--true {
-        background-color: rgba($green-color, 0.1);
-        color: $green-color;
-      }
-      
-      &--false {
-        background-color: rgba($text-color-secondary, 0.1);
-        color: $text-color-secondary;
-      }
-    }
-  }
-  </style>

+ 0 - 15
src/components/PlayerOnly/index.vue

@@ -1,15 +0,0 @@
-<template>
-    <slot v-if="isPlayer"></slot>
-  </template>
-  
-  <script setup lang="ts">
-  import { computed } from 'vue'
-  import { useRoomStore } from '@/stores'
-  
-  const props = defineProps<{
-    roomId?: string
-  }>()
-  
-  const roomStore = useRoomStore()
-  const isPlayer = computed(() => roomStore.isPlayer)
-  </script>

+ 0 - 136
src/components/QuestionCard/index.vue

@@ -1,136 +0,0 @@
-<template>
-    <view class="question-card" :class="{'question-card--answered': question.answeredAt}">
-      <view class="question-card__header">
-        <view class="question-card__user">
-          <image :src="question.playerAvatar" mode="aspectFill" class="question-card__avatar" />
-          <text class="question-card__name">{{ question.playerName }}</text>
-        </view>
-        <view class="question-card__time">{{ formatTime(question.createdAt) }}</view>
-      </view>
-      <view class="question-card__content">
-        {{ question.content }}
-      </view>
-      <view v-if="question.answeredAt" class="question-card__answer">
-        <view class="question-card__answer-label">回答</view>
-        <view class="question-card__answer-content">{{ question.answer }}</view>
-      </view>
-      <view v-else-if="isHost" class="question-card__actions">
-        <nut-button size="small" type="primary" @click="onAnswer('是')">是</nut-button>
-        <nut-button size="small" type="primary" @click="onAnswer('否')">否</nut-button>
-        <nut-button size="small" @click="onAnswer('不相关')">不相关</nut-button>
-      </view>
-    </view>
-  </template>
-  
-  <script setup lang="ts">
-  import { defineProps, defineEmits } from 'vue';
-  import type { Question } from '@/types/game';
-  import { formatDistanceToNow } from 'date-fns';
-  import { zhCN } from 'date-fns/locale';
-  
-  interface QuestionWithAvatar extends Question {
-    playerAvatar: string;
-  }
-  
-  const props = defineProps({
-    question: { 
-      type: Object as () => QuestionWithAvatar, 
-      required: true 
-    },
-    isHost: { 
-      type: Boolean, 
-      default: false 
-    }
-  });
-  
-  const emit = defineEmits(['answer']);
-  
-  const formatTime = (date: Date) => {
-    return formatDistanceToNow(new Date(date), { 
-      addSuffix: true,
-      locale: zhCN
-    });
-  };
-  
-  const onAnswer = (answer: string) => {
-    emit('answer', { 
-      questionId: props.question.id, 
-      answer 
-    });
-  };
-  </script>
-  
-  <style lang="scss">
-  .question-card {
-    background-color: $background-color-light;
-    border-radius: $border-radius-small;
-    padding: $spacing-base;
-    margin-bottom: $spacing-base;
-    box-shadow: $shadow-light;
-    
-    &--answered {
-      background-color: $background-color-gray;
-    }
-    
-    &__header {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      margin-bottom: $spacing-small;
-    }
-    
-    &__user {
-      display: flex;
-      align-items: center;
-    }
-    
-    &__avatar {
-      width: 24px;
-      height: 24px;
-      border-radius: $border-radius-circle;
-      margin-right: $spacing-mini;
-    }
-    
-    &__name {
-      font-size: $font-size-small;
-      color: $text-color-primary;
-      font-weight: $font-weight-medium;
-    }
-    
-    &__time {
-      font-size: $font-size-small;
-      color: $text-color-secondary;
-    }
-    
-    &__content {
-      font-size: $font-size-base;
-      color: $text-color-primary;
-      margin-bottom: $spacing-base;
-      line-height: $line-height-base;
-    }
-    
-    &__answer {
-      background-color: rgba($primary-color, 0.05);
-      padding: $spacing-small;
-      border-radius: $border-radius-small;
-      display: flex;
-    }
-    
-    &__answer-label {
-      font-size: $font-size-small;
-      color: $primary-color;
-      font-weight: $font-weight-medium;
-      margin-right: $spacing-small;
-    }
-    
-    &__answer-content {
-      font-size: $font-size-small;
-      color: $text-color-primary;
-    }
-    
-    &__actions {
-      display: flex;
-      gap: $spacing-small;
-    }
-  }
-  </style>

+ 3 - 2
src/pages/game-detail/index.vue

@@ -149,7 +149,8 @@
 <script lang="ts">
 import Taro from '@tarojs/taro'
 import { ref } from 'vue'
-import { useGameStore, type Game } from '@/stores/game'
+import { useGameStore} from '@/stores/game'
+import { type IGame } from '@/types/game'
 import { useUserStore } from '@/stores/user'
 import { People, Clock } from '@nutui/icons-vue-taro'
 import Tabbar from '@/components/Tabbar.vue'
@@ -180,7 +181,7 @@ export default {
 
     // 页面状态
     const loading = ref(true)
-    const game = ref<Game | null>(null)
+    const game = ref<IGame | null>(null)
     
     // 获取游戏详情
     const getGameDetail = async () => {

+ 4 - 7
src/pages/index/index.vue

@@ -193,7 +193,6 @@
 <script setup lang="ts">
 import { ref, computed, onMounted, watch } from 'vue'
 import Taro from '@tarojs/taro'
-import { useTabBarStore } from '@/stores/tabbar'
 import { useGameStore } from '@/stores/game'
 import GameCard from '@/components/GameCard/index.vue'
 import Tabbar from '@/components/Tabbar.vue'
@@ -208,16 +207,14 @@ const activeTab = ref('0')
 // 弹幕组件相关
 const barrageRef = ref()
 const barrageList = ref([
-  '这个游戏太好玩了!',
-  '强烈推荐海龟汤!',
-  '和朋友一起玩很开心',
+  '在排队吗?',
+  '在团建吗?',
+  '快来玩海龟汤!',
   '这个解谜游戏很有意思',
   '周末约起来玩狼人杀',
   '剧本杀真的很沉浸',
   '这个游戏逻辑性很强',
-  '适合团建的好游戏',
-  '新手也能快速上手',
-  '画面很精美,体验好'
+  '适合团建的好游戏'
 ])
 
 // 获取游戏数据

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

@@ -63,7 +63,8 @@
 <script lang="ts">
 import Taro from '@tarojs/taro'
 import { ref } from 'vue'
-import { useGameStore, type Game } from '@/stores/game'
+import { useGameStore } from '@/stores/game'
+import { type IGame } from '@/types/game'
 import { useUserStore } from '@/stores/user'
 import { useRoomStore } from '@/stores/room'
 // 移除 roomAPI 导入
@@ -99,7 +100,7 @@ export default {
     const roomPassword = ref('')
 
     // 添加明确的类型注解
-    const gameInfo = ref<Game | null>(null)
+    const gameInfo = ref<IGame | null>(null)
     
     // 生成随机房间名称
     const generateRandomName = () => {

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

@@ -70,7 +70,8 @@
 import { ref, computed } from 'vue'
 import Taro from '@tarojs/taro'
 import { useUserStore } from '@/stores/user'
-import { useRoomStore, type RecentRoom } from '@/stores/room'
+import { useRoomStore } from '@/stores/room'
+import { type IRecentRoom } from '@/types/room'
 import { Scan2, Right } from '@nutui/icons-vue-taro'
 import Tabbar from '@/components/Tabbar.vue'
 
@@ -204,7 +205,7 @@ export default {
     }
 
     // 加入最近的房间
-    const joinRecentRoom = (room: RecentRoom) => {
+    const joinRecentRoom = (room: IRecentRoom) => {
       roomCode.value = room.id
       if (room.needPassword) {
         needPassword.value = true

+ 11 - 31
src/services/api/game.ts

@@ -1,38 +1,12 @@
 //处理通用游戏列表和游戏详情的获取
 import Taro from '@tarojs/taro'
-
-// 游戏数据接口移动到API层
-export interface Game {
-  id: string;
-  title: string;
-  image: string;
-  players: string;
-  duration: string;
-  rating: number;
-  isNew: boolean;
-  isHot: boolean;
-  description: string;
-  rules: string;
-  category: string;
-  tips: string[];
-  examples: {
-    question: string;
-    answer: string;
-  }[];
-}
-
-// API返回结果接口
-export interface ApiResult<T> {
-  success: boolean;
-  data?: T;
-  message?: string;
-}
+import { type IGame , type IApiResult } from '@/types/game'
 
 // 控制是否使用云函数(true)或mock数据(false)
 const USE_CLOUD = false
 
 // Mock数据
-const mockGames: Game[] = [
+const mockGames: IGame[] = [
   {
     id: '1',
     title: '海龟汤',
@@ -45,6 +19,8 @@ const mockGames: Game[] = [
     description: '海龟汤是一种猜谜游戏,游戏开始时,主持人会讲述一个故事的结果,参与者需要通过提问来猜测故事的真相。',
     rules: '1. 主持人讲述一个故事的结果\n2. 参与者通过提问来猜测故事的真相\n3. 主持人只能回答"是"、"否"或"不重要"',
     category: '推理',
+    playCount: 1000,
+    completionRate: 0.8,
     tips: [
       '提问时要注意逻辑思维',
       '尝试从多个角度思考问题',
@@ -69,6 +45,8 @@ const mockGames: Game[] = [
     description: '剧本杀是一种角色扮演游戏,每个玩家扮演一个角色,通过阅读剧本和相互交流来解决一个谜题。',
     rules: '1. 每个玩家扮演一个角色\n2. 阅读剧本并获取个人信息\n3. 通过交流和推理解决谜题',
     category: '角色扮演',
+    playCount: 1500,
+    completionRate: 0.7,
     tips: [
       '认真阅读你的角色背景',
       '注意收集和分析信息',
@@ -93,6 +71,8 @@ const mockGames: Game[] = [
     description: '狼人杀是一种桌游,玩家分为狼人和村民两个阵营,狼人试图消灭村民,村民则要找出并消灭狼人。',
     rules: '1. 玩家分为狼人和村民两个阵营\n2. 夜晚狼人选择一名玩家"杀死"\n3. 白天所有人讨论并投票处决一名玩家',
     category: '桌游',
+    playCount: 2000,
+    completionRate: 0.6,
     tips: [
       '仔细观察其他玩家的言行',
       '理性分析每晚的死亡情况',
@@ -108,9 +88,9 @@ const mockGames: Game[] = [
 ]
 
 // 游戏通用API
-export const gameAPI = {
+export const gameService = {
   // 获取游戏列表
-  async getGames(): Promise<ApiResult<Game[]>> {
+  async getGames(): Promise<IApiResult<IGame[]>> {
     try {
       if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
         // 云函数实现
@@ -145,7 +125,7 @@ export const gameAPI = {
   },
 
   // 获取游戏详情
-  async getGameDetail(id: string): Promise<ApiResult<Game | null>> {
+  async getGameDetail(id: string): Promise<IApiResult<IGame | null>> {
     try {
       if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
         // 云函数实现

+ 6 - 6
src/services/api/games/index.ts

@@ -1,7 +1,7 @@
 // 导出所有特定游戏API
-export { turtleSoupAPI } from './turtlesoup'
+export { turtleSoupService } from './turtlesoup'
 // 未来会添加其他游戏API
-export { werewolfAPI } from './werewolf'
+export { werewolfService } from './werewolf'
 // export { scriptAPI } from './script'
 
 // 游戏类型常量
@@ -12,16 +12,16 @@ export const GAME_TYPES = {
 }
 
 // 根据游戏类型获取对应的API
-import { turtleSoupAPI } from './turtlesoup'
-import { werewolfAPI } from './werewolf'
+import { turtleSoupService } from './turtlesoup'
+import { werewolfService } from './werewolf'
 
 export function getGameAPIByType(type: string) {
   switch(type) {
     case GAME_TYPES.TURTLE_SOUP:
-      return turtleSoupAPI
+      return turtleSoupService
     // 未来会添加其他游戏类型
     case GAME_TYPES.WEREWOLF:
-      return werewolfAPI
+      return werewolfService
     // case GAME_TYPES.SCRIPT:
     //   return scriptAPI
     default:

+ 4 - 29
src/services/api/games/turtlesoup.ts

@@ -1,35 +1,10 @@
 //海龟汤游戏特定的 API
+import { TurtleSoupGame } from '@/stores/games/turtlesoup'
 import { callCloudFunction } from '../../cloud'
-
-// 海龟汤游戏数据类型
-export interface TurtleSoupGame {
-  id: string;
-  title: string;
-  story: string;
-  solution: string;
-  hints: string[];
-  questions: TurtleSoupQuestion[];
-  // 其他海龟汤特有属性
-}
-
-export interface TurtleSoupQuestion {
-  id: string;
-  content: string;
-  answer: string;
-  askedBy: string;
-  timestamp: number;
-}
-
-export interface GameResult {
-  solved: boolean;
-  solvedBy?: string;
-  timeUsed: number;
-  questionsCount: number;
-  hintsUsed: number[];
-}
+import { type ITurtleSoupGame, type ITurtleSoupQuestion, type ITurtleSoupGameResult } from '@/types/games/turtlesoup'
 
 // 海龟汤游戏相关API
-export const turtleSoupAPI = {
+export const turtleSoupService = {
   /**
    * 获取游戏数据
    * @param gameId 游戏ID
@@ -72,6 +47,6 @@ export const turtleSoupAPI = {
    * @param result 游戏结果
    */
   endGame(gameId: string, result: { solved: boolean; solvedBy?: string }) {
-    return callCloudFunction<{ gameResult: GameResult }>('endTurtleSoupGame', { gameId, result })
+    return callCloudFunction<{ gameResult: ITurtleSoupGameResult }>('endTurtleSoupGame', { gameId, result })
   }
 }

+ 1 - 1
src/services/api/games/werewolf.ts

@@ -2,7 +2,7 @@ import { callCloudFunction } from '../../cloud'
 import type { WerewolfGame, WerewolfPlayer } from '@/stores/games/werewolf'
 
 // 狼人杀游戏相关API
-export const werewolfAPI = {
+export const werewolfService = {
   // 获取游戏数据
   getGameData(gameId: string, role: string) {
     return callCloudFunction<{ game: WerewolfGame }>('getWerewolfGame', { gameId, role })

+ 3 - 8
src/services/api/index.ts

@@ -1,8 +1,3 @@
-export { gameAPI } from './game'
-export { roomAPI } from './room'
-export * from './games'
-
-// 导出用户API
-export const userAPI = {
-  // ... 保留现有的userAPI实现
-}
+export { gameService } from './game'
+export { roomService } from './room'
+export * from './games'

+ 14 - 46
src/services/api/room.ts

@@ -1,39 +1,7 @@
 // services/api/room.ts - 处理房间相关API请求
 
 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
-  status?: string
-}
+import { type IRecentRoom, type IRoom, type IRoomUser } from '@/types/room'
 
 // Mock房间数据(测试用)
 const mockRooms = {
@@ -83,9 +51,9 @@ const mockRooms = {
 };
 
 // 房间API - 重构后的纯净版
-export const roomAPI = {
+export const roomService = {
   // 获取最近房间
-  async getRecentRooms(): Promise<RecentRoom[]> {
+  async getRecentRooms(): Promise<IRecentRoom[]> {
     try {
       // 从缓存获取
       const cachedRooms = Taro.getStorageSync('recentRooms')
@@ -150,7 +118,7 @@ export const roomAPI = {
   },
   
   // 创建新房间 - 不再依赖store,返回纯数据
-  async createRoom(roomData: Room, userInfo: {openid: string, nickname: string, avatar: string}): Promise<{roomId: string | null, success: boolean, message?: string}> {
+  async createRoom(roomData: IRoom, userInfo: {openid: string, nickname: string, avatar: string}): Promise<{roomId: string | null, success: boolean, message?: string}> {
     try {
       if (!userInfo.openid) {
         return { roomId: null, success: false, message: '用户未登录' }
@@ -192,10 +160,10 @@ export const roomAPI = {
   },
   
   // 加入房间 - 纯粹的数据操作,不依赖store
-  async joinRoom(roomId: string, userData: RoomUser): Promise<{success: boolean, roomData?: Room, message?: string}> {
+  async joinRoom(roomId: string, userData: IRoomUser): Promise<{success: boolean, roomData?: IRoom, message?: string}> {
     try {
       // 查找房间 - 先从本地缓存查询
-      let room: Room | null = null
+      let room: IRoom | null = null
       
       // 检查本地缓存中是否有该房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
@@ -205,7 +173,7 @@ export const roomAPI = {
         room = roomsObj[roomId]
       } else if (mockRooms[roomId]) {
         // 使用mock数据
-        room = mockRooms[roomId] as Room
+        room = mockRooms[roomId] as IRoom
       }
       
       if (!room) {
@@ -229,7 +197,7 @@ export const roomAPI = {
       }
       
       // 保存到最近加入的房间
-      const roomInfo: RecentRoom = {
+      const roomInfo: IRecentRoom = {
         id: room.id,
         name: room.name,
         game: room.gameTitle,
@@ -249,7 +217,7 @@ export const roomAPI = {
   },
   
   // 保存到最近加入的房间
-  async saveToRecentRooms(roomInfo: RecentRoom): Promise<void> {
+  async saveToRecentRooms(roomInfo: IRecentRoom): Promise<void> {
     try {
       const recentRooms = Taro.getStorageSync('recentRooms') || '[]'
       const rooms = JSON.parse(recentRooms)
@@ -301,7 +269,7 @@ export const roomAPI = {
   },
   
   // 获取房间信息
-  async getRoomInfo(roomId: string): Promise<Room | null> {
+  async getRoomInfo(roomId: string): Promise<IRoom | null> {
     try {
       // 检查本地缓存中是否有该房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
@@ -313,7 +281,7 @@ export const roomAPI = {
       
       // 如果本地没有,使用mock数据
       if (mockRooms[roomId]) {
-        return mockRooms[roomId] as Room
+        return mockRooms[roomId] as IRoom
       }
       
       return null
@@ -324,14 +292,14 @@ export const roomAPI = {
   },
 
   // 获取进行中的房间
-  async getActiveRooms(userId: string): Promise<Room[]> {
+  async getActiveRooms(userId: string): Promise<IRoom[]> {
     try {
       // 从缓存或模拟数据获取所有房间
       const allRooms = Taro.getStorageSync('allRooms') || '{}'
       const roomsObj = JSON.parse(allRooms)
       
       // 添加类型断言,确保 Object.values 返回的是 Room[] 类型
-      const activeRooms = Object.values(roomsObj) as Room[];
+      const activeRooms = Object.values(roomsObj) as IRoom[];
       
       // 过滤出用户参与的且状态为"waiting"或"playing"的房间
       const userActiveRooms = activeRooms.filter(room => {
@@ -369,7 +337,7 @@ export const roomAPI = {
             maxPlayers: 8,
             visibility: 'public'
           }
-        ] as Room[]
+        ] as IRoom[]
       }
       
       return userActiveRooms

+ 6 - 24
src/services/api/user.ts

@@ -1,25 +1,7 @@
 // services/api/user.ts
 import Taro from '@tarojs/taro'
 import { ensureStringId } from '@/utils/db-helper'
-
-// 明确定义接口类型
-export interface UserInfo {
-  openid: string;
-  nickname?: string;
-  avatar?: string;
-  role?: string;
-}
-
-export interface OpenIdResult {
-  openid: string;
-  [key: string]: any;
-}
-
-export interface ApiResult<T> {
-  success: boolean;
-  data?: T;
-  message?: string;
-}
+import { type IApiResult,type IUserInfo,type IOpenIdResult } from '@/types/user'
 
 // 控制是否使用云函数(true)或mock数据(false)
 const USE_CLOUD = true
@@ -31,9 +13,9 @@ const mockUser = {
   openid: 'mock_openid_123456',
 }
 
-export const userAPI = {
+export const userService = {
   // 获取用户信息
-  async getUserInfo(): Promise<ApiResult<UserInfo>> {
+  async getUserInfo(): Promise<IApiResult<IUserInfo>> {
     try {
       if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
         // 云函数实现
@@ -65,7 +47,7 @@ export const userAPI = {
   },
   
   // 获取OpenID
-  async getOpenId(code: string): Promise<ApiResult<OpenIdResult>> {
+  async getOpenId(code: string): Promise<IApiResult<IOpenIdResult>> {
     try {
       if (USE_CLOUD && process.env.TARO_ENV === 'weapp') {
         const result = await Taro.cloud.callFunction({
@@ -74,7 +56,7 @@ export const userAPI = {
         })
         
         if (result && result.result) {
-          return { success: true, data: result.result as OpenIdResult }
+          return { success: true, data: result.result as IOpenIdResult }
         }
         return { success: false, message: '获取OpenID失败' }
       }
@@ -91,7 +73,7 @@ export const userAPI = {
   },
   
   // 保存用户信息到云端
-  async saveUserToCloud(userData: UserInfo): Promise<ApiResult<string>> {
+  async saveUserToCloud(userData: IUserInfo): Promise<IApiResult<string>> {
     try {
       if (!USE_CLOUD || process.env.TARO_ENV !== 'weapp') {
         return { success: true, data: 'mock_doc_id' } // Mock成功

+ 2 - 2
src/services/sync.ts

@@ -1,11 +1,11 @@
 // 提供数据同步服务,确保本地数据与云端保持一致
 import Taro from '@tarojs/taro'
-import { Room } from '@/stores/room'
+import { type IRoom } from '@/types/room'
 
 // 数据同步服务
 export const syncService = {
   // 同步房间到云端
-  async syncRoom(room: Room) {
+  async syncRoom(room: IRoom) {
     if (process.env.TARO_ENV !== 'weapp' || !Taro.cloud) {
       return false
     }

+ 11 - 11
src/stores/game.ts

@@ -1,12 +1,13 @@
 // 管理游戏列表和通用游戏详情
 import { defineStore } from 'pinia'
 import { ref, computed } from 'vue'
-import { gameAPI, type Game } from '@/services/api/game'
+import { gameService} from '@/services/api/game'
+import { IGame } from '@/types/game'
 
 export const useGameStore = defineStore('game', () => {
   // 状态
-  const games = ref<Game[]>([])
-  const currentGame = ref<Game | null>(null)
+  const games = ref<IGame[]>([])
+  const currentGame = ref<IGame | null>(null)
   const loading = ref(false)
   const error = ref<string | null>(null)
   
@@ -20,7 +21,7 @@ export const useGameStore = defineStore('game', () => {
     error.value = null
     
     try {
-      const response = await gameAPI.getGames()
+      const response = await gameService.getGames()
       
       if (response.success && response.data) {
         games.value = response.data
@@ -38,7 +39,7 @@ export const useGameStore = defineStore('game', () => {
   }
   
   // 获取游戏详情
-  async function getGameDetail(id: string): Promise<Game | null> {
+  async function getGameDetail(id: string): Promise<IGame | null> {
     // 先从缓存中查找
     const cachedGame = games.value.find(game => game.id === id)
     if (cachedGame) {
@@ -51,7 +52,7 @@ export const useGameStore = defineStore('game', () => {
     error.value = null
     
     try {
-      const response = await gameAPI.getGameDetail(id)
+      const response = await gameService.getGameDetail(id)
       
       if (response.success && response.data) {
         currentGame.value = response.data
@@ -72,7 +73,7 @@ export const useGameStore = defineStore('game', () => {
   }
   
   // 设置当前游戏
-  function setCurrentGame(game: Game | null) {
+  function setCurrentGame(game: IGame | null) {
     currentGame.value = game
   }
   
@@ -89,6 +90,8 @@ export const useGameStore = defineStore('game', () => {
     currentGame,
     loading,
     error,
+
+    // Getters
     hotGames,
     newGames,
     
@@ -98,7 +101,4 @@ export const useGameStore = defineStore('game', () => {
     setCurrentGame,
     clearGames
   }
-})
-
-// 重新导出Game类型,方便使用
-export type { Game } from '@/services/api/game'
+})

+ 3 - 3
src/stores/games/turtlesoup.ts

@@ -1,6 +1,6 @@
 //海龟汤游戏特定的状态管理
 import { defineStore } from 'pinia'
-import { turtleSoupAPI } from '@/services/api/games'
+import { turtleSoupService } from '@/services/api/games'
 
 // 海龟汤游戏状态
 export interface TurtleSoupGame {
@@ -34,7 +34,7 @@ export const useTurtleSoupStore = defineStore('turtlesoup', {
     async loadGame(gameId: string, role: string) {
       this.loading = true
       try {
-        const result = await turtleSoupAPI.getGameData(gameId, role)
+        const result = await turtleSoupService.getGameData(gameId, role)
         if (result && result.game) {
           this.currentGame = result.game
           return true
@@ -53,7 +53,7 @@ export const useTurtleSoupStore = defineStore('turtlesoup', {
       if (!this.currentGame) return false
       
       try {
-        const result = await turtleSoupAPI.submitQuestion(this.currentGame.id, content)
+        const result = await turtleSoupService.submitQuestion(this.currentGame.id, content)
         if (result && result.questionId) {
           // 更新本地状态...
           return true

+ 6 - 6
src/stores/games/werewolf.ts

@@ -1,5 +1,5 @@
 import { defineStore } from 'pinia'
-import { werewolfAPI } from '@/services/api/games/werewolf'
+import { werewolfService } from '@/services/api/games'
 
 // 玩家角色类型
 export type WerewolfRole = 
@@ -238,7 +238,7 @@ export const useWerewolfStore = defineStore('werewolf', {
       this.error = null;
       
       try {
-        const result = await werewolfAPI.getGameData(gameId, role);
+        const result = await werewolfService.getGameData(gameId, role);
         if (result && result.game) {
           this.currentGame = result.game;
           
@@ -269,7 +269,7 @@ export const useWerewolfStore = defineStore('werewolf', {
       if (!this.currentGame || !this.currentPlayer) return false;
       
       try {
-        await werewolfAPI.playerAction(
+        await werewolfService.playerAction(
           this.currentGame.id,
           this.currentPlayer.id,
           'ready'
@@ -286,7 +286,7 @@ export const useWerewolfStore = defineStore('werewolf', {
       if (!this.currentGame || !this.currentPlayer) return false;
       
       try {
-        await werewolfAPI.playerAction(
+        await werewolfService.playerAction(
           this.currentGame.id,
           this.currentPlayer.id,
           action,
@@ -337,7 +337,7 @@ export const useWerewolfStore = defineStore('werewolf', {
     // 丘比特连线
     async couplePlayer(player1Id: string, player2Id: string) {
       try {
-        await werewolfAPI.playerAction(
+        await werewolfService.playerAction(
           this.currentGame!.id,
           this.currentPlayer!.id,
           'pair',
@@ -355,7 +355,7 @@ export const useWerewolfStore = defineStore('werewolf', {
       if (!this.currentGame || !this.currentPlayer) return false;
       
       try {
-        await werewolfAPI.playerAction(
+        await werewolfService.playerAction(
           this.currentGame.id,
           this.currentPlayer.id,
           'lastWords',

+ 22 - 24
src/stores/room.ts

@@ -1,32 +1,30 @@
 // stores/room.ts - 管理房间状态
 import { defineStore } from 'pinia'
 import { ref, reactive, computed } from 'vue'
-import { roomAPI, type Room, type RoomUser, type RecentRoom } from '@/services/api/room'
+import { roomService } from '@/services/api/room'
+import { type IRoom, type IRoomUser, type IRecentRoom } from '@/types/room'
 import { useUserStore } from '@/stores/user'
 import Taro from '@tarojs/taro'
 
-// 重新导出类型以便其他组件使用
-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 currentRoom = ref<IRoom | null>(null)
   
   // 最近加入的房间
-  const recentRooms = ref<RecentRoom[]>([])
+  const recentRooms = ref<IRecentRoom[]>([])
   
   // 历史记录状态
   const historyState = reactive({
-    activeRooms: [] as Room[],
+    activeRooms: [] as IRoom[],
     endedGames: [] as any[],
     isLoading: false
   })
   
   // 设置当前房间
-  function setCurrentRoom(room: Room | null) {
+  function setCurrentRoom(room: IRoom | null) {
     currentRoom.value = room
   }
   
@@ -38,7 +36,7 @@ export const useRoomStore = defineStore('room', () => {
     }
     
     // 从已加载的活跃房间和历史记录构建最近房间列表
-    const recentList: RecentRoom[] = [
+    const recentList: IRecentRoom[] = [
       // 活跃房间
       ...historyState.activeRooms.map(room => ({
         id: room.id,
@@ -69,7 +67,7 @@ export const useRoomStore = defineStore('room', () => {
   // 加载活跃房间 - 统一数据源
   async function loadActiveRooms() {
     try {
-      historyState.activeRooms = await roomAPI.getActiveRooms(userStore.openid)
+      historyState.activeRooms = await roomService.getActiveRooms(userStore.openid)
       return historyState.activeRooms
     } catch (error) {
       console.error('加载活跃房间失败:', error)
@@ -80,7 +78,7 @@ export const useRoomStore = defineStore('room', () => {
   // 加载已结束游戏 - 统一数据源
   async function loadEndedGames() {
     try {
-      historyState.endedGames = await roomAPI.getEndedGames(userStore.openid)
+      historyState.endedGames = await roomService.getEndedGames(userStore.openid)
       return historyState.endedGames
     } catch (error) {
       console.error('加载已结束游戏失败:', error)
@@ -100,7 +98,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 创建房间 - 调用API并更新store
-  async function createRoom(roomData: Room) {
+  async function createRoom(roomData: IRoom) {
     try {
       // 检查用户是否已注册
       const userCheck = await checkUserRegistered()
@@ -108,7 +106,7 @@ export const useRoomStore = defineStore('room', () => {
         return { roomId: null, success: false, message: userCheck.message }
       }
       
-      const result = await roomAPI.createRoom(roomData, {
+      const result = await roomService.createRoom(roomData, {
         openid: userStore.openid,
         nickname: userStore.nickname,
         avatar: userStore.avatar
@@ -116,7 +114,7 @@ export const useRoomStore = defineStore('room', () => {
       
       if (result.success && result.roomId) {
         // 获取完整房间信息
-        const room = await roomAPI.getRoomInfo(result.roomId)
+        const room = await roomService.getRoomInfo(result.roomId)
         if (room) {
           setCurrentRoom(room)
           
@@ -142,7 +140,7 @@ export const useRoomStore = defineStore('room', () => {
       }
       
       // 构建用户数据
-      const userData: RoomUser = {
+      const userData: IRoomUser = {
         id: userStore.openid,
         name: userStore.nickname,
         avatar: userStore.avatar,
@@ -151,7 +149,7 @@ export const useRoomStore = defineStore('room', () => {
       }
       
       // 调用API加入房间
-      const result = await roomAPI.joinRoom(roomId, userData)
+      const result = await roomService.joinRoom(roomId, userData)
       
       if (result.success && result.roomData) {
         // 更新当前房间
@@ -175,7 +173,7 @@ export const useRoomStore = defineStore('room', () => {
   // 退出房间 - 调用API并更新store
   async function leaveRoom(roomId: string) {
     try {
-      const result = await roomAPI.leaveRoom(roomId, userStore.openid)
+      const result = await roomService.leaveRoom(roomId, userStore.openid)
       
       if (result.success) {
         // 清除当前房间信息
@@ -195,7 +193,7 @@ export const useRoomStore = defineStore('room', () => {
   // 加载房间信息 - 通过API获取
   async function loadRoomInfo(roomId: string) {
     try {
-      const room = await roomAPI.getRoomInfo(roomId)
+      const room = await roomService.getRoomInfo(roomId)
       
       if (room) {
         setCurrentRoom(room)
@@ -210,7 +208,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 添加用户到当前房间
-  function addUserToRoom(user: RoomUser) {
+  function addUserToRoom(user: IRoomUser) {
     if (currentRoom.value) {
       // 检查是否已存在
       const existingUserIndex = currentRoom.value.users.findIndex(u => u.id === user.id)
@@ -235,7 +233,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 更新用户信息
-  function updateUserInRoom(userId: string, data: Partial<RoomUser>) {
+  function updateUserInRoom(userId: string, data: Partial<IRoomUser>) {
     if (currentRoom.value) {
       const userIndex = currentRoom.value.users.findIndex(u => u.id === userId)
       if (userIndex !== -1) {
@@ -296,7 +294,7 @@ export const useRoomStore = defineStore('room', () => {
   }
   
   // 更新房间设置
-  function updateRoomSettings(settings: Partial<Room>) {
+  function updateRoomSettings(settings: Partial<IRoom>) {
     if (currentRoom.value) {
       currentRoom.value = {
         ...currentRoom.value,
@@ -365,7 +363,7 @@ export const useRoomStore = defineStore('room', () => {
     success: boolean, 
     message?: string, 
     needPassword?: boolean,
-    roomData?: Room  // 添加房间数据
+    roomData?: IRoom  // 添加房间数据
   }> {
     try {
       // 1. 检查用户是否注册
@@ -375,7 +373,7 @@ export const useRoomStore = defineStore('room', () => {
       }
       
       // 2. 验证房间
-      const checkResult = await roomAPI.checkRoomExist(roomId)
+      const checkResult = await roomService.checkRoomExist(roomId)
       if (!checkResult || !checkResult.exists) {
         return { success: false, message: '房间不存在' }
       }
@@ -386,7 +384,7 @@ export const useRoomStore = defineStore('room', () => {
       }
       
       if (checkResult.needPassword && password) {
-        const pwdResult = await roomAPI.verifyRoomPassword(roomId, password)
+        const pwdResult = await roomService.verifyRoomPassword(roomId, password)
         if (!pwdResult || !pwdResult.valid) {
           return { success: false, message: '房间密码错误' }
         }

+ 5 - 4
src/stores/user.ts

@@ -1,7 +1,8 @@
 // stores/user.ts - 管理用户信息、登录状态、角色权限等
 import { defineStore } from 'pinia'
 import Taro from '@tarojs/taro'
-import { userAPI, type UserInfo} from '@/services/api/user'
+import { userService} from '@/services/api/user'
+import { type IUserInfo } from '@/types/user'
 import { ref, computed } from 'vue'
 
 export type UserRole = 'player' | 'hoster'
@@ -29,7 +30,7 @@ export const useUserStore = defineStore('user', () => {
   const roomCount = ref(0)   // 创建房间数量
   
   // 计算属性
-  const userInfo = computed<UserInfo>(() => ({
+  const userInfo = computed<IUserInfo>(() => ({
     openid: openid.value,
     nickname: nickname.value,
     avatar: avatar.value,
@@ -137,7 +138,7 @@ export const useUserStore = defineStore('user', () => {
         return false
       }
       
-      const result = await userAPI.getOpenId(loginRes.code)
+      const result = await userService.getOpenId(loginRes.code)
       if (!result.success || !result.data) {
         error.value = result.message || '获取OpenID失败'
         return false
@@ -188,7 +189,7 @@ export const useUserStore = defineStore('user', () => {
         saveUserToStorage()
         
         // 同步到云端 - 使用Service层处理
-        await userAPI.saveUserToCloud({
+        await userService.saveUserToCloud({
           openid: openid.value,
           nickname: nickname.value,
           avatar: avatar.value,

+ 27 - 56
src/types/game.ts

@@ -1,57 +1,28 @@
-// 游戏状态枚举
-export enum GameStatus {
-    PREPARING = 'preparing', // 准备中
-    ONGOING = 'ongoing',     // 进行中
-    SOLVED = 'solved',       // 已解决
-    EXPIRED = 'expired'      // 已过期
-}
+// 游戏数据接口
+export interface IGame {
+    id: string; // 游戏ID
+    title: string; // 游戏标题
+    image: string; // 游戏图片
+    players: string; // 游戏人数
+    duration: string; // 游戏时长
+    rating: number; // 游戏评分
+    isNew: boolean; // 是否为新游戏
+    isHot: boolean; // 是否为热门游戏
+    description: string; // 游戏描述
+    rules: string; // 游戏规则
+    category: string; // 游戏分类
+    tips: string[]; // 游戏提示
+    playCount: number; // 游戏参与人数
+    completionRate: number; // 游戏完成率
+    examples: {
+      question: string; // 游戏示例问题
+      answer: string; // 游戏示例答案 
+    }[];
+  }
   
-// 问题模型
-export interface Question {
-    id: string;            // 问题ID
-    playerId: string;      // 提问玩家ID
-    playerName: string;    // 提问玩家名称
-    content: string;       // 问题内容
-    answer?: string;       // 回答 (是/否/不相关)
-    createdAt: Date;       // 创建时间
-    answeredAt?: Date;     // 回答时间
-}
-  
-// 基础游戏模型
-export interface BaseGame {
-    id: string;            // 游戏ID
-    roomId: string;        // 关联房间ID
-    type: string;          // 游戏类型
-    title: string;         // 标题
-    startTime: Date;       // 开始时间
-    endTime?: Date;        // 结束时间
-    status: GameStatus;    // 游戏状态
-}
-  
-// 海龟汤游戏模型
-export interface TurtleSoupGame extends BaseGame {
-    initialStory: string;  // 初始故事
-    hints: string[];       // 提示列表
-    solution: string;      // 解答
-    revealedHints: number[]; // 已公开提示索引
-    questions: Question[]; // 问题列表
-}
-  
-// 游戏结果模型
-export interface GameResult {
-    gameId: string;
-    solved: boolean;
-    solvedBy?: string;
-    solvedAt?: Date;
-    duration: number;      // 游戏持续时间(秒)
-    questionsCount: number;
-    hintsRevealed: number;
-}
-  
-// 游戏配置选项
-export interface GameOptions {
-    gameType: string;
-    difficulty: string;
-    timeLimit?: number;
-    customSettings?: Record<string, any>;
-}
+  // API返回结果接口
+  export interface IApiResult<T> {
+    success: boolean;
+    data?: T;
+    message?: string;
+  }

+ 26 - 0
src/types/games/turtlesoup.ts

@@ -0,0 +1,26 @@
+// 海龟汤游戏数据类型
+export interface ITurtleSoupGame {
+    id: string;
+    title: string;
+    story: string;
+    solution: string;
+    hints: string[];
+    questions: ITurtleSoupQuestion[];
+    // 其他海龟汤特有属性
+  }
+  
+  export interface ITurtleSoupQuestion {
+    id: string;
+    content: string;
+    answer: string;
+    askedBy: string;
+    timestamp: number;
+  }
+  
+  export interface ITurtleSoupGameResult {
+    solved: boolean;
+    solvedBy?: string;
+    timeUsed: number;
+    questionsCount: number;
+    hintsUsed: number[];
+  }

+ 32 - 0
src/types/room.ts

@@ -0,0 +1,32 @@
+// 导出类型定义以便共享
+export interface IRoomUser {
+    id: string
+    name: string
+    avatar: string
+    role: 'hoster' | 'player'
+    joinTime: number
+  }
+  
+  export interface IRoom {
+    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: IRoomUser[]
+  }
+  
+  export interface IRecentRoom {
+    id: string
+    name: string
+    game: string
+    time: number
+    needPassword?: boolean
+    status?: string
+  }

+ 18 - 0
src/types/user.ts

@@ -0,0 +1,18 @@
+// 明确定义接口类型
+export interface IUserInfo {
+    openid: string;
+    nickname?: string;
+    avatar?: string;
+    role?: string;
+  }
+  
+  export interface IOpenIdResult {
+    openid: string;
+    [key: string]: any;
+  }
+  
+  export interface IApiResult<T> {
+    success: boolean;
+    data?: T;
+    message?: string;
+  }