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">
|
<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 {useRoute, useRouter} from 'vue-router'
|
||||||
import deskImage from '../assets/images/desk/desk_01_1920_945.png'
|
import deskImage from '../assets/images/desk/desk_01_1920_945.png'
|
||||||
import robotIcon from '../assets/images/icons/robot.svg'
|
import robotIcon from '../assets/images/icons/robot.svg'
|
||||||
@@ -98,6 +98,7 @@ const discardPending = ref(false)
|
|||||||
const claimActionPending = ref(false)
|
const claimActionPending = ref(false)
|
||||||
const turnActionPending = ref(false)
|
const turnActionPending = ref(false)
|
||||||
const nextRoundPending = ref(false)
|
const nextRoundPending = ref(false)
|
||||||
|
const settlementDeadlineMs = ref<number | null>(null)
|
||||||
const selectedDiscardTileId = ref<number | null>(null)
|
const selectedDiscardTileId = ref<number | null>(null)
|
||||||
let clockTimer: number | null = null
|
let clockTimer: number | null = null
|
||||||
let discardPendingTimer: number | null = null
|
let discardPendingTimer: number | null = null
|
||||||
@@ -352,6 +353,14 @@ const settlementPlayers = computed(() => {
|
|||||||
.sort((a, b) => b.score - a.score)
|
.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(() => {
|
const myReadyState = computed(() => {
|
||||||
return Boolean(myPlayer.value?.isReady)
|
return Boolean(myPlayer.value?.isReady)
|
||||||
})
|
})
|
||||||
@@ -1241,6 +1250,12 @@ function handleRoomStateResponse(message: unknown): void {
|
|||||||
if (typeof totalRounds === 'number') {
|
if (typeof totalRounds === 'number') {
|
||||||
gameStore.totalRounds = totalRounds
|
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)
|
gameStore.pendingClaim = normalizePendingClaim(payload)
|
||||||
if (!gameStore.pendingClaim) {
|
if (!gameStore.pendingClaim) {
|
||||||
claimActionPending.value = false
|
claimActionPending.value = false
|
||||||
@@ -1295,6 +1310,7 @@ function handleRoomStateResponse(message: unknown): void {
|
|||||||
}
|
}
|
||||||
if (phase !== 'settlement') {
|
if (phase !== 'settlement') {
|
||||||
nextRoundPending.value = false
|
nextRoundPending.value = false
|
||||||
|
settlementDeadlineMs.value = null
|
||||||
}
|
}
|
||||||
if (currentTurnPlayerId && currentTurnPlayerId !== loggedInUserId.value) {
|
if (currentTurnPlayerId && currentTurnPlayerId !== loggedInUserId.value) {
|
||||||
markDiscardCompleted()
|
markDiscardCompleted()
|
||||||
@@ -1583,6 +1599,10 @@ function handleRoomInfoResponse(message: unknown): void {
|
|||||||
if (typeof infoTotalRounds === 'number') {
|
if (typeof infoTotalRounds === 'number') {
|
||||||
gameStore.totalRounds = infoTotalRounds
|
gameStore.totalRounds = infoTotalRounds
|
||||||
}
|
}
|
||||||
|
const infoSdMs = readNumber(gameState ?? {}, 'settlement_deadline_ms', 'settlementDeadlineMs')
|
||||||
|
if (typeof infoSdMs === 'number' && infoSdMs > 0) {
|
||||||
|
settlementDeadlineMs.value = infoSdMs
|
||||||
|
}
|
||||||
|
|
||||||
setActiveRoom({
|
setActiveRoom({
|
||||||
roomId,
|
roomId,
|
||||||
@@ -2753,6 +2773,12 @@ function hydrateFromActiveRoom(routeRoomId: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
watch(settlementCountdown, (remaining) => {
|
||||||
|
if (remaining !== null && remaining <= 0 && !nextRoundPending.value && !isLastRound.value) {
|
||||||
|
nextRound()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const routeRoomId = typeof route.params.roomId === 'string' ? route.params.roomId : ''
|
const routeRoomId = typeof route.params.roomId === 'string' ? route.params.roomId : ''
|
||||||
needsInitialRoomInfo = true
|
needsInitialRoomInfo = true
|
||||||
@@ -3161,7 +3187,9 @@ onBeforeUnmount(() => {
|
|||||||
:disabled="nextRoundPending"
|
:disabled="nextRoundPending"
|
||||||
@click="nextRound"
|
@click="nextRound"
|
||||||
>
|
>
|
||||||
<span class="ready-toggle-label">{{ nextRoundPending ? '准备中...' : '下一局' }}</span>
|
<span class="ready-toggle-label">
|
||||||
|
{{ nextRoundPending ? '准备中...' : settlementCountdown != null && settlementCountdown > 0 ? `下一局 (${settlementCountdown}s)` : '下一局' }}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-else
|
v-else
|
||||||
|
|||||||
Reference in New Issue
Block a user