|
@@ -50,7 +50,11 @@
|
|
|
</view>
|
|
|
<!-- 设置弹窗 -->
|
|
|
<nut-dialog v-model:visible="showSettings" title="更换主题">
|
|
|
- <TurtleSoupSettings @close="showSettings = false"/>
|
|
|
+ <GameSettings
|
|
|
+ :gameType="gameType"
|
|
|
+ @settings-change="handleSettingsChange"
|
|
|
+ @close="showSettings = false"
|
|
|
+ />
|
|
|
</nut-dialog>
|
|
|
</template>
|
|
|
|
|
@@ -81,56 +85,220 @@
|
|
|
<view v-if="revealedCluesList.length === 0" class="no-clues">暂无线索</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
- <!-- 关键词道具解锁(占位) -->
|
|
|
- <view class="card tool-card">
|
|
|
- <view class="card-title">关键词道具</view>
|
|
|
- <nut-button block type="primary" @click="showUnlockTools = true">解锁关键词提示</nut-button>
|
|
|
- </view>
|
|
|
- <!-- 道具解锁弹窗(占位) -->
|
|
|
- <nut-dialog v-model:visible="showUnlockTools" title="解锁道具">
|
|
|
- <view style="padding:24px;text-align:center;color:#888;">功能开发中,敬请期待!</view>
|
|
|
- </nut-dialog>
|
|
|
+
|
|
|
+ <!-- 玩家道具区域 -->
|
|
|
+ <PlayerTools :gameType="gameType" />
|
|
|
</template>
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
-import { ref, computed } from 'vue'
|
|
|
+import Taro from '@tarojs/taro'
|
|
|
+import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
|
import { useTurtleSoupStore } from '@/stores/games/turtlesoup'
|
|
|
-import TurtleSoupSettings from '@/components/room/host/gamesettings/TurtleSoupSettings.vue'
|
|
|
+import { useRoomStore } from '@/stores/room'
|
|
|
+import { GameType } from '@/types/game'
|
|
|
+import { TurtleSoupGameStatus } from '@/types/games/turtlesoup'
|
|
|
+import GameSettings from '@/components/room/host/GameSettings.vue'
|
|
|
+import PlayerTools from '@/components/room/player/PlayerTools.vue'
|
|
|
|
|
|
+// 初始化stores
|
|
|
const turtleSoupStore = useTurtleSoupStore()
|
|
|
+const roomStore = useRoomStore()
|
|
|
+
|
|
|
+// 游戏和房间ID
|
|
|
+const gameId = ref('')
|
|
|
+const roomId = ref('')
|
|
|
+const gameType = ref(GameType.TURTLE_SOUP)
|
|
|
+
|
|
|
+// 判断是否为主持人
|
|
|
const isHost = computed(() => turtleSoupStore.isHost)
|
|
|
+
|
|
|
+// 获取当前视图
|
|
|
const currentView = computed(() => turtleSoupStore.currentView)
|
|
|
+
|
|
|
+// 游戏信息
|
|
|
const gameTitle = computed(() => currentView.value?.title || '海龟汤')
|
|
|
const puzzleTitle = computed(() => currentView.value?.puzzle?.title || '谜题')
|
|
|
const scenario = computed(() => currentView.value?.puzzle?.scenario || '')
|
|
|
const solution = computed(() => isHost.value ? currentView.value?.puzzle?.truth : '')
|
|
|
const gameProgress = computed(() => turtleSoupStore.gameProgress)
|
|
|
const gameDuration = computed(() => turtleSoupStore.gameDuration)
|
|
|
+
|
|
|
+// 游戏状态文本
|
|
|
const statusText = computed(() => {
|
|
|
switch(currentView.value?.status) {
|
|
|
- case 'active': return '游戏进行中'
|
|
|
- case 'waiting': return '准备中'
|
|
|
- case 'completed': return '已完成'
|
|
|
- default: return ''
|
|
|
+ case TurtleSoupGameStatus.ACTIVE: return '游戏进行中'
|
|
|
+ case TurtleSoupGameStatus.WAITING: return '准备中'
|
|
|
+ case TurtleSoupGameStatus.COMPLETED: return '已完成'
|
|
|
+ default: return '游戏进行中'
|
|
|
}
|
|
|
})
|
|
|
|
|
|
-// 关键线索(直接取puzzle.keyClues)
|
|
|
+// 关键线索
|
|
|
const clues = computed(() => currentView.value?.puzzle?.keyClues || [])
|
|
|
-// 主持人已显示线索索引(假设用本地state控制,真实用store/后端同步)
|
|
|
-const revealedClues = ref<number[]>([0, 1]) // 示例,已显示前两条
|
|
|
-function revealClue(idx: number) {
|
|
|
- if (!revealedClues.value.includes(idx)) revealedClues.value.push(idx)
|
|
|
-}
|
|
|
|
|
|
-// 玩家端公开线索(直接取keyClues中已公开的)
|
|
|
-const revealedCluesList = computed(() => clues.value.filter((_, idx) => revealedClues.value.includes(idx)))
|
|
|
+// 已显示的线索
|
|
|
+const revealedClues = ref<number[]>([])
|
|
|
+
|
|
|
+// 玩家端公开线索
|
|
|
+const revealedCluesList = computed(() =>
|
|
|
+ clues.value.filter((_, idx) => revealedClues.value.includes(idx))
|
|
|
+)
|
|
|
|
|
|
// 弹窗控制
|
|
|
const showSettings = ref(false)
|
|
|
-const showUnlockTools = ref(false)
|
|
|
+
|
|
|
+// 初始化页面
|
|
|
+const initPage = async () => {
|
|
|
+ // 获取路由参数中的房间ID和游戏ID
|
|
|
+ const pages = Taro.getCurrentPages()
|
|
|
+ const currentPage = pages[pages.length - 1]
|
|
|
+ const routeParams = currentPage.$taroParams
|
|
|
+
|
|
|
+ if (routeParams && routeParams.gameId && routeParams.roomId) {
|
|
|
+ roomId.value = routeParams.roomId
|
|
|
+ gameId.value = routeParams.gameId
|
|
|
+
|
|
|
+ // 加载游戏信息
|
|
|
+ await loadGameInfo(gameId.value, roomId.value)
|
|
|
+ } else {
|
|
|
+ Taro.showToast({
|
|
|
+ title: '参数错误',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ // 延迟返回
|
|
|
+ setTimeout(() => {
|
|
|
+ Taro.navigateBack()
|
|
|
+ }, 1500)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 加载游戏信息
|
|
|
+const loadGameInfo = async (gameId: string, roomId: string) => {
|
|
|
+ try {
|
|
|
+ Taro.showLoading({ title: '加载中...' })
|
|
|
+
|
|
|
+ // 加载房间信息
|
|
|
+ await roomStore.loadRoomInfo(gameId, roomId)
|
|
|
+
|
|
|
+ // 加载游戏信息
|
|
|
+ const result = await turtleSoupStore.loadGameInfo(gameId, roomId)
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ // 更新已显示线索
|
|
|
+ if (result.revealedClues) {
|
|
|
+ revealedClues.value = result.revealedClues
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new Error(result.message || '加载游戏信息失败')
|
|
|
+ }
|
|
|
+
|
|
|
+ Taro.hideLoading()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载游戏信息失败:', error)
|
|
|
+ Taro.showToast({
|
|
|
+ title: error instanceof Error ? error.message : '加载游戏信息失败',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ Taro.hideLoading()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 显示线索
|
|
|
+const revealClue = async (idx: number) => {
|
|
|
+ if (revealedClues.value.includes(idx)) return
|
|
|
+
|
|
|
+ try {
|
|
|
+ Taro.showLoading({ title: '显示线索中...' })
|
|
|
+
|
|
|
+ // 调用API显示线索
|
|
|
+ const result = await turtleSoupStore.revealClue(gameId.value, roomId.value, idx)
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ revealedClues.value.push(idx)
|
|
|
+ // 重新排序
|
|
|
+ revealedClues.value.sort((a, b) => a - b)
|
|
|
+ } else {
|
|
|
+ throw new Error(result.message || '显示线索失败')
|
|
|
+ }
|
|
|
+
|
|
|
+ Taro.hideLoading()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('显示线索失败:', error)
|
|
|
+ Taro.showToast({
|
|
|
+ title: error instanceof Error ? error.message : '显示线索失败',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ Taro.hideLoading()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 处理设置变更
|
|
|
+const handleSettingsChange = async (settings) => {
|
|
|
+ try {
|
|
|
+ Taro.showLoading({ title: '更新设置中...' })
|
|
|
+
|
|
|
+ // 调用API更新游戏设置
|
|
|
+ const result = await turtleSoupStore.updateGameSettings(gameId.value, roomId.value, settings)
|
|
|
+
|
|
|
+ if (result.success) {
|
|
|
+ Taro.showToast({
|
|
|
+ title: '设置已更新',
|
|
|
+ icon: 'success'
|
|
|
+ })
|
|
|
+
|
|
|
+ // 关闭设置窗口
|
|
|
+ showSettings.value = false
|
|
|
+
|
|
|
+ // 刷新游戏信息
|
|
|
+ await loadGameInfo(gameId.value, roomId.value)
|
|
|
+ } else {
|
|
|
+ throw new Error(result.message || '更新设置失败')
|
|
|
+ }
|
|
|
+
|
|
|
+ Taro.hideLoading()
|
|
|
+ } catch (error) {
|
|
|
+ console.error('更新设置失败:', error)
|
|
|
+ Taro.showToast({
|
|
|
+ title: error instanceof Error ? error.message : '更新设置失败',
|
|
|
+ icon: 'none'
|
|
|
+ })
|
|
|
+ Taro.hideLoading()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 游戏数据监听器
|
|
|
+let gameInterval: NodeJS.Timeout | null = null
|
|
|
+
|
|
|
+// 开始监听游戏数据变化
|
|
|
+const startGameListener = () => {
|
|
|
+ // 定期刷新游戏状态
|
|
|
+ gameInterval = setInterval(async () => {
|
|
|
+ if (gameId.value && roomId.value) {
|
|
|
+ await loadGameInfo(gameId.value, roomId.value)
|
|
|
+ }
|
|
|
+ }, 5000) // 每5秒刷新一次
|
|
|
+}
|
|
|
+
|
|
|
+// 停止监听游戏变化
|
|
|
+const stopGameListener = () => {
|
|
|
+ if (gameInterval) {
|
|
|
+ clearInterval(gameInterval)
|
|
|
+ gameInterval = null
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 页面加载时初始化
|
|
|
+onMounted(() => {
|
|
|
+ initPage()
|
|
|
+ startGameListener()
|
|
|
+})
|
|
|
+
|
|
|
+// 页面卸载时停止监听
|
|
|
+onUnmounted(() => {
|
|
|
+ stopGameListener()
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
@@ -180,7 +348,6 @@ const showUnlockTools = ref(false)
|
|
|
&.scenario-card { }
|
|
|
&.solution-card { background: #fff6e3; }
|
|
|
&.clue-card { }
|
|
|
- &.tool-card { background: #f4faff; }
|
|
|
&.player { background: #f4faff; }
|
|
|
.card-title {
|
|
|
font-size: 16px;
|