@@ -4,10 +4,14 @@
< el-alert : type = "pushConnected ? 'success' : 'error'" : title = "pushConnected ? t('game.live.push_connected') : t('game.live.push_disconnected')" show -icon class = "mb-12" / >
< el-card shadow = "never" class = "mb-12" >
< el-alert v-if = "snapshot.is_payout_phase" type="warning" :title="t('game.live.payout_phase')" show-icon class="mb-12" / >
< div class = "header-row" >
< div >
< div > { { t ( 'game.live.current_record' ) } } : { { snapshot . record ? . period _no || '-' } } < / div >
< div > { { t ( 'game.live.ai_default_number' ) } } : { { snapshot . ai _default _number ? ? '-' } } < / div >
< div v-if = "snapshot.pending_draw_number != null" >
{{ t ( ' game.live.pending_draw ' ) }} : {{ snapshot.pending_draw_number }}
< / div >
< div > { { t ( 'game.live.countdown' ) } } : { { countdownText } } < / div >
< / div >
< div class = "header-actions" >
@@ -15,7 +19,7 @@
< el-button :loading = "calcLoading" :disabled = "!snapshot.can_calculate" @click ="onCalculate" >
{{ t ( ' game.live.btn_calc ' ) }}
< / el -button >
< el-button type = "primary" :loading = "drawLoading" :disabled = "!snapshot.can_draw" @click ="onDraw" >
< el-button type = "primary" :loading = "drawLoading" :disabled = "!snapshot.can_schedule_ draw" @click ="onDraw" >
{{ t ( ' game.live.btn_draw ' ) }}
< / el -button >
< el-button :loading = "loading" @click ="loadSnapshot" > {{ t ( ' Refresh ' ) }} < / el -button >
@@ -67,6 +71,7 @@ interface Snapshot {
bets : anyObj [ ]
candidate _numbers : anyObj [ ]
ai _default _number : number | null
pending _draw _number : number | null
period _seconds ? : number
bet _seconds ? : number
pick _max _number _count ? : number
@@ -74,8 +79,12 @@ interface Snapshot {
draw _number _max ? : number
remaining _seconds ? : number
bet _remaining _seconds ? : number
payout _remaining _seconds ? : number
is _payout _phase ? : boolean
can _calculate ? : boolean
can _draw ? : boolean
can _schedule _draw ? : boolean
server _time ? : number
}
const { t } = useI18n ( )
@@ -87,14 +96,18 @@ const snapshot = reactive<Snapshot>({
bets : [ ] ,
candidate _numbers : [ ] ,
ai _default _number : null ,
pending _draw _number : null ,
period _seconds : 30 ,
bet _seconds : 20 ,
pick _max _number _count : 10 ,
draw _number _max : 36 ,
remaining _seconds : 0 ,
bet _remaining _seconds : 0 ,
payout _remaining _seconds : 0 ,
is _payout _phase : false ,
can _calculate : false ,
can _draw : false ,
can _schedule _draw : false ,
} )
const calcLoading = ref ( false )
const drawLoading = ref ( false )
@@ -102,6 +115,12 @@ const manualNumber = ref<number | null>(1)
const calcResultNumber = ref < number | null > ( null )
const calcEstimatedLoss = ref < string > ( '0.0000' )
/** 服务端 Unix 秒 − 本地 Unix 秒,用于派彩倒计时与服务器对齐 */
const serverSkewSeconds = ref ( 0 )
/** 每秒递增,驱动派彩剩余秒本地刷新 */
const clockTick = ref ( 0 )
let clockTimer : number | null = null
let pushClient : any = null
let pushChannel : any = null
let pollTimer : number | null = null
@@ -113,6 +132,45 @@ function formatPicks(v: unknown): string {
return '-'
}
function syncServerClock ( serverTime : unknown ) : void {
if ( typeof serverTime === 'number' && Number . isFinite ( serverTime ) ) {
serverSkewSeconds . value = serverTime - Math . floor ( Date . now ( ) / 1000 )
snapshot . server _time = serverTime
}
}
function readPayoutUntilUnix ( rec : anyObj | null ) : number | null {
if ( ! rec ) {
return null
}
const v = rec . payout _until
if ( v === null || v === undefined || v === '' ) {
return null
}
if ( typeof v === 'number' && Number . isFinite ( v ) ) {
return v
}
if ( typeof v === 'string' && /^\d+$/ . test ( v ) ) {
return parseInt ( v , 10 )
}
return null
}
/** 派彩剩余秒:优先用 payout_until 与对时后的「服务器当前秒」计算,便于每秒递减 */
const payoutRemainingLive = computed ( ( ) => {
clockTick . value
if ( ! snapshot . is _payout _phase ) {
return null
}
const until = readPayoutUntilUnix ( snapshot . record )
if ( until !== null ) {
const serverNow = Math . floor ( Date . now ( ) / 1000 ) + serverSkewSeconds . value
const diff = until - serverNow
return diff > 0 ? diff : 0
}
return snapshot . payout _remaining _seconds ? ? 0
} )
async function loadSnapshot ( ) {
loading . value = true
try {
@@ -122,14 +180,20 @@ async function loadSnapshot() {
snapshot . bets = res . data . bets || [ ]
snapshot . candidate _numbers = res . data . candidate _numbers || [ ]
snapshot . ai _default _number = res . data . ai _default _number
snapshot . pending _draw _number =
typeof res . data . pending _draw _number === 'number' ? res . data . pending _draw _number : null
snapshot . period _seconds = res . data . period _seconds ? ? 30
snapshot . bet _seconds = res . data . bet _seconds ? ? 20
snapshot . pick _max _number _count = res . data . pick _max _number _count ? ? 10
snapshot . draw _number _max = res . data . draw _number _max ? ? 36
snapshot . remaining _seconds = res . data . remaining _seconds ? ? 0
snapshot . bet _remaining _seconds = res . data . bet _remaining _seconds ? ? 0
snapshot . payout _remaining _seconds = res . data . payout _remaining _seconds ? ? 0
snapshot . is _payout _phase = ! ! res . data . is _payout _phase
snapshot . can _calculate = ! ! res . data . can _calculate
snapshot . can _draw = ! ! res . data . can _draw
snapshot . can _schedule _draw = ! ! res . data . can _schedule _draw || ! ! res . data . can _draw
syncServerClock ( res . data . server _time )
const dmax = res . data . draw _number _max ? ? 36
if ( manualNumber . value === null || manualNumber . value < 1 || manualNumber . value > dmax ) manualNumber . value = 1
}
@@ -172,14 +236,20 @@ async function initPush() {
snapshot . bets = payload . bets || [ ]
snapshot . candidate _numbers = payload . candidate _numbers || [ ]
snapshot . ai _default _number = payload . ai _default _number ? ? null
snapshot . pending _draw _number =
typeof payload . pending _draw _number === 'number' ? payload . pending _draw _number : null
snapshot . period _seconds = payload . period _seconds ? ? 30
snapshot . bet _seconds = payload . bet _seconds ? ? 20
snapshot . pick _max _number _count = payload . pick _max _number _count ? ? 10
snapshot . draw _number _max = payload . draw _number _max ? ? 36
snapshot . remaining _seconds = payload . remaining _seconds ? ? 0
snapshot . bet _remaining _seconds = payload . bet _remaining _seconds ? ? 0
snapshot . payout _remaining _seconds = payload . payout _remaining _seconds ? ? 0
snapshot . is _payout _phase = ! ! payload . is _payout _phase
snapshot . can _calculate = ! ! payload . can _calculate
snapshot . can _draw = ! ! payload . can _draw
snapshot . can _schedule _draw = ! ! payload . can _schedule _draw || ! ! payload . can _draw
syncServerClock ( payload . server _time )
} )
} catch {
pushConnected . value = false
@@ -244,12 +314,19 @@ async function onDraw() {
}
const countdownText = computed ( ( ) => {
const total = snapshot . remaining _seconds ? ? 0
const bet = snapshot . bet _remaining _seconds ? ? 0
return ` ${ t ( 'game.live.bet_countdown' ) } ${ bet } s / ${ t ( 'game.live.draw_countdown' ) } ${ total } s `
const draw = snapshot . remaining _seconds ? ? 0
let payoutPart = t ( 'game.live.payout_na' )
if ( snapshot . is _payout _phase && payoutRemainingLive . value !== null ) {
payoutPart = ` ${ payoutRemainingLive . value } s `
}
return ` ${ t ( 'game.live.bet_countdown' ) } ${ bet } s / ${ t ( 'game.live.draw_countdown' ) } ${ draw } s / ${ t ( 'game.live.payout_countdown' ) } ${ payoutPart } `
} )
onMounted ( async ( ) => {
clockTimer = window . setInterval ( ( ) => {
clockTick . value ++
} , 1000 )
await loadSnapshot ( )
try {
await initPush ( )
@@ -269,6 +346,10 @@ onUnmounted(() => {
}
stopPolling ( )
stopPushWatchdog ( )
if ( clockTimer !== null ) {
window . clearInterval ( clockTimer )
clockTimer = null
}
} )
function startPolling ( ) {