[游戏管理]游戏实时对局

This commit is contained in:
2026-04-16 15:10:12 +08:00
parent 15fdd3ba57
commit c7149e7058
29 changed files with 1158 additions and 157 deletions

View File

@@ -0,0 +1,140 @@
<template>
<div class="default-main">
<el-alert type="info" :title="t('game.live.tip')" show-icon class="mb-12" />
<el-card shadow="never" 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>
<el-button type="primary" :loading="loading" @click="loadSnapshot">{{ t('Refresh') }}</el-button>
</div>
</el-card>
<el-row :gutter="12">
<el-col :span="12">
<el-card shadow="never">
<template #header>{{ t('game.live.candidate_title') }}</template>
<el-table :data="snapshot.candidate_numbers" height="420">
<el-table-column prop="number" :label="t('game.live.number')" width="100" />
<el-table-column prop="estimated_loss" :label="t('game.live.estimated_loss')" />
</el-table>
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="never">
<template #header>{{ t('game.live.bet_stream_title') }}</template>
<el-table :data="snapshot.bets" height="420">
<el-table-column prop="id" :label="t('game.live.bet_id')" width="90" />
<el-table-column prop="user_id" :label="t('game.live.user_id')" width="90" />
<el-table-column prop="pick_numbers" :label="t('game.live.pick_numbers')">
<template #default="scope">
{{ formatPicks(scope.row.pick_numbers) }}
</template>
</el-table-column>
<el-table-column prop="unit_amount" :label="t('game.live.unit_amount')" width="120" />
<el-table-column prop="streak_at_bet" :label="t('game.live.streak_at_bet')" width="90" />
</el-table>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import createAxios from '/@/utils/axios'
interface Snapshot {
record: anyObj | null
bets: anyObj[]
candidate_numbers: anyObj[]
ai_default_number: number | null
}
const { t } = useI18n()
const loading = ref(false)
const snapshot = reactive<Snapshot>({
record: null,
bets: [],
candidate_numbers: [],
ai_default_number: null,
})
let pushClient: any = null
let pushChannel: any = null
function formatPicks(v: unknown): string {
if (Array.isArray(v)) return JSON.stringify(v)
if (typeof v === 'string') return v
return '-'
}
async function loadSnapshot() {
loading.value = true
try {
const res = await createAxios({ url: '/admin/game.Live/snapshot', method: 'get', showCodeMessage: false })
if (res.code === 1 && res.data) {
snapshot.record = res.data.record
snapshot.bets = res.data.bets || []
snapshot.candidate_numbers = res.data.candidate_numbers || []
snapshot.ai_default_number = res.data.ai_default_number
}
} finally {
loading.value = false
}
}
async function initPush() {
const cfgRes = await createAxios({ url: '/admin/game.Live/pushConfig', method: 'get', showCodeMessage: false })
if (cfgRes.code !== 1 || !cfgRes.data) {
return
}
const { url, app_key, channel, event } = cfgRes.data
await loadPushJs()
const PushCtor = (window as any).Push
if (!PushCtor) {
return
}
pushClient = new PushCtor({ url, app_key })
pushChannel = pushClient.subscribe(channel)
pushChannel.on(event, (payload: anyObj) => {
snapshot.record = payload.record || null
snapshot.bets = payload.bets || []
snapshot.candidate_numbers = payload.candidate_numbers || []
snapshot.ai_default_number = payload.ai_default_number ?? null
})
}
async function loadPushJs() {
if ((window as any).Push) {
return
}
await new Promise<void>((resolve, reject) => {
const script = document.createElement('script')
script.src = '/plugin/webman/push/push.js'
script.onload = () => resolve()
script.onerror = () => reject(new Error('load push.js failed'))
document.head.appendChild(script)
})
}
onMounted(async () => {
await loadSnapshot()
await initPush()
})
</script>
<style scoped lang="scss">
.mb-12 {
margin-bottom: 12px;
}
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>