fix: 代码审查问题修复 — socket 代理提供 + inject 空安全 + 观战人数响应式
This commit is contained in:
@@ -52,6 +52,9 @@ export const useRoomStore = defineStore('room', () => {
|
||||
const drawRequested = ref<boolean>(false)
|
||||
const drawRequestFrom = ref<string | null>(null)
|
||||
|
||||
// ===== 观战者 =====
|
||||
const spectatorCount = ref<number>(0)
|
||||
|
||||
/**
|
||||
* 从房间详情 HTTP 响应初始化 Store
|
||||
* @param detail - 房间详情响应数据
|
||||
@@ -129,6 +132,11 @@ export const useRoomStore = defineStore('room', () => {
|
||||
resultType.value = type
|
||||
}
|
||||
|
||||
/** 更新观战人数 */
|
||||
function setSpectatorCount(count: number) {
|
||||
spectatorCount.value = count
|
||||
}
|
||||
|
||||
/** 重置状态 */
|
||||
function reset() {
|
||||
roomId.value = ''
|
||||
@@ -150,8 +158,8 @@ export const useRoomStore = defineStore('room', () => {
|
||||
boardState, currentTurn, moves, winner, resultType,
|
||||
isPlaying, isFinished,
|
||||
messages,
|
||||
drawRequested, drawRequestFrom,
|
||||
drawRequested, drawRequestFrom, spectatorCount,
|
||||
initFromDetail, applyMove, setCurrentTurn, addMessage,
|
||||
updateReady, addPlayer, removePlayer, setGameOver, reset
|
||||
updateReady, addPlayer, removePlayer, setGameOver, setSpectatorCount, reset
|
||||
}
|
||||
})
|
||||
|
||||
@@ -45,6 +45,28 @@ const accountStore = useAccountStore()
|
||||
|
||||
let socket: RoomSocket | null = null
|
||||
|
||||
/**
|
||||
* WebSocket 代理对象
|
||||
*
|
||||
* 在 WS 未建立之前 provide 给子组件的安全代理。
|
||||
* 子组件调用 send/close/isOpen 时,代理将请求转发到实际 socket。
|
||||
* WS 未连接时 send() 静默丢弃消息,避免子组件因 inject undefined 而崩溃。
|
||||
*/
|
||||
const socketProxy: RoomSocket = {
|
||||
send(type: string, data?: unknown) {
|
||||
socket?.send(type, data)
|
||||
},
|
||||
close() {
|
||||
socket?.close()
|
||||
},
|
||||
isOpen() {
|
||||
return socket?.isOpen() ?? false
|
||||
}
|
||||
}
|
||||
|
||||
// 在 setup 阶段同步 provide,确保子组件不会 inject 到 undefined
|
||||
provide('roomSocket', socketProxy)
|
||||
|
||||
onMounted(async () => {
|
||||
const roomId = route.params.roomId as string
|
||||
const userId = accountStore.accountInfo?.userId ?? ''
|
||||
@@ -64,8 +86,7 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
// 3. 向子组件暴露 WebSocket 控制接口
|
||||
provide('roomSocket', socket)
|
||||
// socketProxy 已在 setup 阶段同步 provide,子组件现在调用 send() 即可
|
||||
} catch {
|
||||
// 房间不存在或加载失败,返回大厅
|
||||
router.push('/')
|
||||
@@ -119,6 +140,16 @@ function handleWsMessage(msg: WsMessage) {
|
||||
roomStore.updateReady(data.userId, data.ready)
|
||||
break
|
||||
}
|
||||
case 'spectator_join': {
|
||||
const data = msg.data as { count: number }
|
||||
roomStore.setSpectatorCount(data.count)
|
||||
break
|
||||
}
|
||||
case 'spectator_leave': {
|
||||
const data = msg.data as { count: number }
|
||||
roomStore.setSpectatorCount(data.count)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -29,7 +29,7 @@ const canvasSize = PADDING * 2 + CELL_SIZE * (BOARD_SIZE - 1)
|
||||
|
||||
const store = useRoomStore()
|
||||
const { boardState, currentTurn, currentUserId, isPlayer, isPlaying } = storeToRefs(store)
|
||||
const socket = inject<RoomSocket>('roomSocket')!
|
||||
const socket = inject<RoomSocket>('roomSocket', null)!
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null)
|
||||
const hoverPos = ref<{ row: number; col: number } | null>(null)
|
||||
|
||||
@@ -47,7 +47,7 @@ const { players, currentUserId } = storeToRefs(store)
|
||||
const router = useRouter()
|
||||
|
||||
/** 通过 inject 获取 WS 连接(父组件 provide) */
|
||||
const socket = inject<RoomSocket>('roomSocket')!
|
||||
const socket = inject<RoomSocket>('roomSocket', null)!
|
||||
|
||||
/** 当前用户是否已准备 */
|
||||
const isReady = computed(() => {
|
||||
|
||||
@@ -33,7 +33,12 @@ import type { RoomSocket } from '@/common/websocket/roomSocket'
|
||||
|
||||
const store = useRoomStore()
|
||||
const { messages, players } = storeToRefs(store)
|
||||
const socket = inject<RoomSocket>('roomSocket')!
|
||||
const socket = inject<RoomSocket>('roomSocket', null)
|
||||
|
||||
/** 发送消息(空安全,socket 尚未建立时静默丢弃) */
|
||||
function safeSend(type: string, data?: unknown) {
|
||||
socket?.send(type, data)
|
||||
}
|
||||
|
||||
/** 输入框内容 */
|
||||
const input = ref('')
|
||||
@@ -54,7 +59,7 @@ watch(() => messages.value.length, async () => {
|
||||
function sendMessage() {
|
||||
const content = input.value.trim()
|
||||
if (!content) return
|
||||
socket.send('chat', { content })
|
||||
safeSend('chat', { content })
|
||||
input.value = ''
|
||||
}
|
||||
|
||||
|
||||
@@ -8,22 +8,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useRoomStore } from '@/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const store = useRoomStore()
|
||||
const { players } = storeToRefs(store)
|
||||
|
||||
/**
|
||||
* 观战人数估算:WS 连接数 - 玩家数
|
||||
* 后续由 WS spectator_join/leave 事件维护更准确的数据
|
||||
*/
|
||||
const spectatorCount = computed(() => {
|
||||
// 首期简化处理:后端 spectator 数据通过 WS 事件维护
|
||||
// 这里暂时返回固定值,后续通过 Store 中的 spectators 列表计算
|
||||
return 0
|
||||
})
|
||||
const { spectatorCount } = storeToRefs(store)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
Reference in New Issue
Block a user