feat: 添加房间页面全屏容器和 WS 中枢
This commit is contained in:
151
src/views/game/RoomPage.vue
Normal file
151
src/views/game/RoomPage.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<template>
|
||||
<div class="room-page">
|
||||
<!-- 顶部信息栏 -->
|
||||
<RoomTopBar />
|
||||
|
||||
<!-- 主体区域 -->
|
||||
<div class="room-page__main">
|
||||
<!-- 左侧面板 -->
|
||||
<RoomSidePanel />
|
||||
|
||||
<!-- 中央游戏画布 -->
|
||||
<div class="room-page__canvas">
|
||||
<GameCanvas />
|
||||
</div>
|
||||
|
||||
<!-- 右侧面板 -->
|
||||
<RoomRightPanel />
|
||||
</div>
|
||||
|
||||
<!-- 底部观战栏 -->
|
||||
<SpectatorBar v-if="roomStore.allowSpectate" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, provide } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { useRoomStore } from '@/stores'
|
||||
import { useAccountStore } from '@/stores'
|
||||
import { getRoomDetail } from '@/api/modules/game'
|
||||
import { createRoomSocket } from '@/common/websocket/roomSocket'
|
||||
import type { RoomSocket } from '@/common/websocket/roomSocket'
|
||||
import type { WsMessage } from '@/api/modules/game'
|
||||
|
||||
import RoomTopBar from './components/RoomTopBar.vue'
|
||||
import RoomSidePanel from './components/RoomSidePanel.vue'
|
||||
import GameCanvas from './components/GameCanvas.vue'
|
||||
import RoomRightPanel from './components/RoomRightPanel.vue'
|
||||
import SpectatorBar from './components/SpectatorBar.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const roomStore = useRoomStore()
|
||||
const accountStore = useAccountStore()
|
||||
|
||||
let socket: RoomSocket | null = null
|
||||
|
||||
onMounted(async () => {
|
||||
const roomId = route.params.roomId as string
|
||||
const userId = accountStore.accountInfo?.userId ?? ''
|
||||
|
||||
try {
|
||||
// 1. 获取房间详情(初始快照)
|
||||
const res = await getRoomDetail(roomId)
|
||||
roomStore.initFromDetail(res.data, userId)
|
||||
|
||||
// 2. 建立 WebSocket 连接
|
||||
const token = accountStore.accessToken
|
||||
socket = createRoomSocket(roomId, token, {
|
||||
onMessage: handleWsMessage,
|
||||
onClose: () => {
|
||||
// 连接关闭,提示并返回大厅
|
||||
router.push('/')
|
||||
}
|
||||
})
|
||||
|
||||
// 3. 向子组件暴露 WebSocket 控制接口
|
||||
provide('roomSocket', socket)
|
||||
} catch {
|
||||
// 房间不存在或加载失败,返回大厅
|
||||
router.push('/')
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
socket?.close()
|
||||
roomStore.reset()
|
||||
})
|
||||
|
||||
/** 处理 WebSocket 下行消息 */
|
||||
function handleWsMessage(msg: WsMessage) {
|
||||
switch (msg.type) {
|
||||
case 'board_sync': {
|
||||
const data = msg.data as { board: number[][]; currentTurn: string }
|
||||
roomStore.boardState = data.board
|
||||
roomStore.setCurrentTurn(data.currentTurn)
|
||||
break
|
||||
}
|
||||
case 'move': {
|
||||
const data = msg.data as { row: number; col: number; playerId: string; moveIndex: number }
|
||||
roomStore.applyMove(data.row, data.col, data.playerId, data.moveIndex)
|
||||
break
|
||||
}
|
||||
case 'turn_change': {
|
||||
const data = msg.data as { playerId: string }
|
||||
roomStore.setCurrentTurn(data.playerId)
|
||||
break
|
||||
}
|
||||
case 'chat_broadcast':
|
||||
roomStore.addMessage(msg.data as { userId: string; content: string; timestamp: number })
|
||||
break
|
||||
case 'game_over': {
|
||||
const data = msg.data as { winnerId: string | null; resultType: string }
|
||||
roomStore.setGameOver(data.winnerId, data.resultType)
|
||||
break
|
||||
}
|
||||
case 'player_join': {
|
||||
const data = msg.data as { userId: string; avatar: string; nickname: string; seatNumber: number; ready: boolean }
|
||||
roomStore.addPlayer(data)
|
||||
break
|
||||
}
|
||||
case 'player_leave': {
|
||||
const data = msg.data as { userId: string }
|
||||
roomStore.removePlayer(data.userId)
|
||||
break
|
||||
}
|
||||
case 'ready_change': {
|
||||
const data = msg.data as { userId: string; ready: boolean }
|
||||
roomStore.updateReady(data.userId, data.ready)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.room-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100lvh;
|
||||
width: 100lvw;
|
||||
overflow: hidden;
|
||||
background-color: var(--bg-body);
|
||||
}
|
||||
|
||||
.room-page__main {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.room-page__canvas {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user