1.优化实时游戏对局页面样式

This commit is contained in:
2026-05-28 17:39:26 +08:00
parent 3e5858bcce
commit 1ba6ddd24e

View File

@@ -283,9 +283,16 @@ let snapshotLoadPromise: Promise<void> | null = null
const wsLoading = ref(false)
const wsReady = ref(false)
const wsConnected = ref(false)
/** 同一期只提示一次“中大奖”,避免 bet.win/jackpot.hit 连续触发刷屏 */
const lastJackpotTipPeriodNo = ref<string>('')
const lastJackpotTipAtMs = ref<number>(0)
const wsUrl = ref('')
const wsTopics = ref<string[]>([])
const wsClient = ref<WebSocket | null>(null)
let wsReconnectTimer: number | null = null
let wsReconnectAttempt = 0
let wsLastConfigReloadAtMs = 0
let wsManualClosing = false
const isMobile = ref(false)
const candidateSort = ref<{ prop: string; order: 'ascending' | 'descending' | null }>({ prop: 'estimated_loss', order: 'ascending' })
@@ -376,7 +383,7 @@ function handleWsPayload(raw: unknown): void {
if (event === 'bet.win' && parsed.data && typeof parsed.data === 'object') {
const winData = parsed.data as anyObj
if (winData.is_jackpot === true) {
ElMessage.success(t('game.live.jackpot_hit_tip'))
showJackpotTipOnce(readString(winData.period_no) || readString(snapshot.record?.period_no))
}
return
}
@@ -384,7 +391,7 @@ function handleWsPayload(raw: unknown): void {
const jackpotData = parsed.data as anyObj
const hits = Array.isArray(jackpotData.hits) ? jackpotData.hits : []
if (hits.length > 0) {
ElMessage.success(t('game.live.jackpot_hit_tip'))
showJackpotTipOnce(readString(jackpotData.period_no) || readString(snapshot.record?.period_no))
}
return
}
@@ -397,6 +404,27 @@ function handleWsPayload(raw: unknown): void {
}
}
function readString(v: any): string {
return typeof v === 'string' ? v : ''
}
function showJackpotTipOnce(periodNo: string): void {
const key = String(periodNo || '')
const now = Date.now()
// 同一期只提示一次;同时加 2s 防抖,避免同一瞬间 bet.win + jackpot.hit 连续触发刷屏
if (key !== '' && lastJackpotTipPeriodNo.value === key) {
return
}
if (now - lastJackpotTipAtMs.value < 2000) {
return
}
if (key !== '') {
lastJackpotTipPeriodNo.value = key
}
lastJackpotTipAtMs.value = now
ElMessage.success(t('game.live.jackpot_hit_tip'))
}
/** 用 period.tick 轻量字段刷新倒计时(不触发 HTTP避免与 WS 每秒 snapshot 冲突) */
function mergePeriodTickFields(periodData: anyObj): void {
if (typeof periodData.server_time === 'number') {
@@ -445,11 +473,17 @@ function connectWs(): void {
if (!wsReady.value || !wsUrl.value) {
return
}
if (wsReconnectTimer !== null) {
window.clearTimeout(wsReconnectTimer)
wsReconnectTimer = null
}
wsManualClosing = false
disconnectWs()
const socket = new WebSocket(wsUrl.value)
wsClient.value = socket
socket.onopen = () => {
wsConnected.value = true
wsReconnectAttempt = 0
const topics = wsTopics.value
const payload = JSON.stringify({ action: 'subscribe', topics })
socket.send(payload)
@@ -463,24 +497,42 @@ function connectWs(): void {
socket.onclose = () => {
wsConnected.value = false
wsClient.value = null
if (wsManualClosing) {
return
}
// 断线后:先刷新 wsConfig 拿新的 admin_ws_token避免握手 token 已过期反复失败),再重连
window.setTimeout(async () => {
const attempt = wsReconnectAttempt
wsReconnectAttempt = Math.min(wsReconnectAttempt + 1, 10)
const baseDelay = Math.min(30_000, 1200 * Math.pow(2, attempt))
const delay = Math.max(1200, Math.floor(baseDelay + Math.random() * 400))
wsReconnectTimer = window.setTimeout(async () => {
wsReconnectTimer = null
if (wsConnected.value) {
return
}
try {
await reloadWsConfig()
const now = Date.now()
// 避免断线风暴下疯狂请求 wsConfig后台会报错且浏览器控制台刷屏
if (!wsLoading.value && now - wsLastConfigReloadAtMs > 8000) {
wsLastConfigReloadAtMs = now
await reloadWsConfig()
}
} catch {
/* ignore下次重连时再试 */
}
if (!wsConnected.value) {
connectWs()
}
}, 1200)
}, delay)
}
}
function disconnectWs(): void {
wsManualClosing = true
if (wsReconnectTimer !== null) {
window.clearTimeout(wsReconnectTimer)
wsReconnectTimer = null
}
if (wsClient.value) {
wsClient.value.close()
wsClient.value = null