feat(game): 结算倒计时展示+自动下一局
- 解析后端settlement_deadline_ms,展示5秒结算倒计时 - 下一局按钮显示倒计时秒数(如"下一局 (3s)") - 倒计时归零自动发送next_round(可提前点击) - 新局开始时清除结算状态 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {computed, onBeforeUnmount, onMounted, ref} from 'vue'
|
||||
import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import deskImage from '../assets/images/desk/desk_01_1920_945.png'
|
||||
import robotIcon from '../assets/images/icons/robot.svg'
|
||||
@@ -98,6 +98,7 @@ const discardPending = ref(false)
|
||||
const claimActionPending = ref(false)
|
||||
const turnActionPending = ref(false)
|
||||
const nextRoundPending = ref(false)
|
||||
const settlementDeadlineMs = ref<number | null>(null)
|
||||
const selectedDiscardTileId = ref<number | null>(null)
|
||||
let clockTimer: number | null = null
|
||||
let discardPendingTimer: number | null = null
|
||||
@@ -352,6 +353,14 @@ const settlementPlayers = computed(() => {
|
||||
.sort((a, b) => b.score - a.score)
|
||||
})
|
||||
|
||||
const settlementCountdown = computed(() => {
|
||||
if (!showSettlementOverlay.value || !settlementDeadlineMs.value) {
|
||||
return null
|
||||
}
|
||||
const remaining = Math.max(0, Math.ceil((settlementDeadlineMs.value - now.value) / 1000))
|
||||
return remaining
|
||||
})
|
||||
|
||||
const myReadyState = computed(() => {
|
||||
return Boolean(myPlayer.value?.isReady)
|
||||
})
|
||||
@@ -1241,6 +1250,12 @@ function handleRoomStateResponse(message: unknown): void {
|
||||
if (typeof totalRounds === 'number') {
|
||||
gameStore.totalRounds = totalRounds
|
||||
}
|
||||
const sdMs = readNumber(payload, 'settlement_deadline_ms', 'settlementDeadlineMs')
|
||||
if (typeof sdMs === 'number' && sdMs > 0) {
|
||||
settlementDeadlineMs.value = sdMs
|
||||
} else if (phase !== 'settlement') {
|
||||
settlementDeadlineMs.value = null
|
||||
}
|
||||
gameStore.pendingClaim = normalizePendingClaim(payload)
|
||||
if (!gameStore.pendingClaim) {
|
||||
claimActionPending.value = false
|
||||
@@ -1295,6 +1310,7 @@ function handleRoomStateResponse(message: unknown): void {
|
||||
}
|
||||
if (phase !== 'settlement') {
|
||||
nextRoundPending.value = false
|
||||
settlementDeadlineMs.value = null
|
||||
}
|
||||
if (currentTurnPlayerId && currentTurnPlayerId !== loggedInUserId.value) {
|
||||
markDiscardCompleted()
|
||||
@@ -1583,6 +1599,10 @@ function handleRoomInfoResponse(message: unknown): void {
|
||||
if (typeof infoTotalRounds === 'number') {
|
||||
gameStore.totalRounds = infoTotalRounds
|
||||
}
|
||||
const infoSdMs = readNumber(gameState ?? {}, 'settlement_deadline_ms', 'settlementDeadlineMs')
|
||||
if (typeof infoSdMs === 'number' && infoSdMs > 0) {
|
||||
settlementDeadlineMs.value = infoSdMs
|
||||
}
|
||||
|
||||
setActiveRoom({
|
||||
roomId,
|
||||
@@ -2753,6 +2773,12 @@ function hydrateFromActiveRoom(routeRoomId: string): void {
|
||||
}
|
||||
|
||||
|
||||
watch(settlementCountdown, (remaining) => {
|
||||
if (remaining !== null && remaining <= 0 && !nextRoundPending.value && !isLastRound.value) {
|
||||
nextRound()
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
const routeRoomId = typeof route.params.roomId === 'string' ? route.params.roomId : ''
|
||||
needsInitialRoomInfo = true
|
||||
@@ -3161,7 +3187,9 @@ onBeforeUnmount(() => {
|
||||
:disabled="nextRoundPending"
|
||||
@click="nextRound"
|
||||
>
|
||||
<span class="ready-toggle-label">{{ nextRoundPending ? '准备中...' : '下一局' }}</span>
|
||||
<span class="ready-toggle-label">
|
||||
{{ nextRoundPending ? '准备中...' : settlementCountdown != null && settlementCountdown > 0 ? `下一局 (${settlementCountdown}s)` : '下一局' }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
|
||||
Reference in New Issue
Block a user