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