1.优化实时对局页面样式以及自动创建下一局和作废本局的记录

2.新增派彩达到game_config.jackpot_max_amount必须审核才能发放
3.新增游戏对局记录-查看游玩记录btn
3.备份MySQL数据库
This commit is contained in:
2026-04-28 18:25:44 +08:00
parent 4ea83d2818
commit 687257adaa
22 changed files with 945 additions and 208 deletions

View File

@@ -35,12 +35,12 @@ export default {
pick_numbers: 'Pick numbers',
total_amount: 'Total bet amount',
streak_at_bet: 'Streak at bet',
runtime_switch: 'Game runtime',
runtime_switch: 'Auto-create next round',
countdown_maintenance: 'Maintenance',
runtime_draining_banner:
'Game stopped: the current round will run through draw, settlement and payout. Full maintenance UI appears after payout completes.',
runtime_maintenance_banner: 'Maintenance: player betting is disabled. Turn runtime on to resume; a new round is created when idle.',
runtime_off_tip: 'When turning runtime on with no active round, a new period is created immediately.',
runtime_maintenance_banner: 'Maintenance: player betting is disabled. Turn on auto-create to resume; a new round is created when idle.',
runtime_off_tip: 'When enabling auto-create with no active round, a new period is created immediately.',
void_btn: 'Void round',
void_dialog_title: 'Void current round',
void_reason_label: 'Reason',

View File

@@ -17,6 +17,18 @@ export default {
'status 1': 'Pending draw',
'status 2': 'Settled',
'status 3': 'Refunded',
'status 4': 'Returned',
'status 5': 'Pending review',
review_title: 'Win review',
review_open: 'Review',
review_approve: 'Approve',
review_reject: 'Reject',
review_remark: 'Reject remark',
review_remark_placeholder: 'Please enter reject reason',
review_remark_required: 'Remark is required when rejecting',
review_approve_confirm: 'Confirm approve and pay out?',
review_approve_success: 'Approved and paid out',
review_reject_success: 'Rejected',
idempotency_key: 'Idempotency key',
create_time: 'Created',
update_time: 'Updated',

View File

@@ -10,10 +10,10 @@ export default {
'status 3': 'Paying',
'status 4': 'Ended',
'status 5': 'Void',
'status 6': 'Abnormal',
draw_mode: 'Draw mode',
'draw_mode 0': 'Auto AI',
'draw_mode 1': 'Manual preset',
preset_number: 'Preset number',
result_number: 'Result number',
platform_profit_amount: 'Round P/L (platform)',
winner_user_count: 'Winners',
@@ -26,13 +26,23 @@ export default {
manual_create_label: 'Allow manual create next round',
manual_create_tip: 'When enabled, button below can create next round manually',
btn_create_next: 'Create next round (manual)',
view_abnormal_rounds: 'View abnormal rounds',
abnormal_dialog_title: 'Abnormal round recovery logs',
abnormal_dialog_tip: 'Shows rounds auto-recovered after service restart (auto-void + refund).',
abnormal_from_status: 'Status before recovery',
refunded_user_count: 'Refunded users',
refunded_order_count: 'Refunded orders',
refunded_total_amount: 'Total refunded amount',
recovered_at: 'Recovered at',
load_abnormal_failed: 'Failed to load abnormal rounds',
view_play_records: 'View play records',
play_record_dialog_title: 'Play (bet) records',
play_record_col_id: 'ID',
play_record_col_user: 'Username',
play_record_col_channel: 'Channel',
play_record_col_pick_numbers: 'Picks',
play_record_col_total_amount: 'Play amount',
play_record_col_is_auto: 'Auto',
play_record_col_win_amount: 'Payout',
play_record_col_jackpot_extra_amount: 'Jackpot extra',
play_record_col_status: 'Status',
play_record_col_create_time: 'Created',
play_record_is_auto_0: 'Manual',
play_record_is_auto_1: 'Auto bet',
play_record_status_1: 'Pending draw',
play_record_status_2: 'Settled',
play_record_status_3: 'Refunded',
play_record_status_4: 'Returned',
load_play_record_failed: 'Failed to load play records',
}

View File

@@ -35,11 +35,11 @@ export default {
pick_numbers: '下注号码',
total_amount: '下注总额',
streak_at_bet: '下注时连胜',
runtime_switch: '游戏运行',
runtime_switch: '自动创建下一局',
countdown_maintenance: '维护中',
runtime_draining_banner: '已关闭游戏:当前局将正常进行至开奖、结算并完成派彩;全部结束后进入维护模式(倒计时与操作区将切换为维护中)。',
runtime_maintenance_banner: '维护中:玩家端已禁止下注。请开启「游戏运行」恢复;若无进行中的局将自动创建新一期。',
runtime_off_tip: '开启「游戏运行」后,若无进行中的局将立即创建新一期。',
runtime_maintenance_banner: '维护中:玩家端已禁止下注。请开启「自动创建下一局」恢复;若无进行中的局将自动创建新一期。',
runtime_off_tip: '开启「自动创建下一局」后,若无进行中的局将立即创建新一期。',
void_btn: '作废本局',
void_dialog_title: '作废本局',
void_reason_label: '作废原因',

View File

@@ -17,6 +17,18 @@ export default {
'status 1': '待开奖',
'status 2': '已结算',
'status 3': '已退款',
'status 4': '已退回',
'status 5': '待审核',
review_title: '中奖审核',
review_open: '审核',
review_approve: '通过',
review_reject: '拒绝',
review_remark: '拒绝备注',
review_remark_placeholder: '请输入拒绝原因',
review_remark_required: '拒绝时必须填写备注',
review_approve_confirm: '确认审核通过并派彩提现吗?',
review_approve_success: '审核通过,已派彩',
review_reject_success: '已拒绝派彩',
idempotency_key: '幂等键',
create_time: '创建时间',
update_time: '更新时间',

View File

@@ -10,10 +10,10 @@ export default {
'status 3': '派彩中',
'status 4': '已结束',
'status 5': '已作废',
'status 6': '异常',
draw_mode: '开奖方式',
'draw_mode 0': '自动AI',
'draw_mode 1': '手动预设',
preset_number: '预设号码',
result_number: '开奖号码',
platform_profit_amount: '对局盈亏(平台)',
winner_user_count: '中奖人数',
@@ -26,13 +26,23 @@ export default {
manual_create_label: '允许手动创建下一局',
manual_create_tip: '开启后可在本页使用「手动创建下一局」按钮',
btn_create_next: '手动创建下一局',
view_abnormal_rounds: '查看异常对局',
abnormal_dialog_title: '异常对局恢复记录',
abnormal_dialog_tip: '展示服务重启后自动恢复的异常对局(自动作废并退款)。',
abnormal_from_status: '异常前状态',
refunded_user_count: '退款用户数',
refunded_order_count: '退款注单数',
refunded_total_amount: '退款总金额',
recovered_at: '恢复时间',
load_abnormal_failed: '加载异常对局失败',
view_play_records: '查看游玩记录',
play_record_dialog_title: '游玩(压注)记录',
play_record_col_id: 'ID',
play_record_col_user: '用户名',
play_record_col_channel: '渠道',
play_record_col_pick_numbers: '选号',
play_record_col_total_amount: '游玩金额',
play_record_col_is_auto: '托管',
play_record_col_win_amount: '派彩',
play_record_col_jackpot_extra_amount: 'Jackpot',
play_record_col_status: '状态',
play_record_col_create_time: '创建时间',
play_record_is_auto_0: '手动',
play_record_is_auto_1: '托管',
play_record_status_1: '待开奖',
play_record_status_2: '已结算',
play_record_status_3: '已退款',
play_record_status_4: '已退回',
load_play_record_failed: '加载游玩记录失败',
}

View File

@@ -256,6 +256,7 @@ const calcLoading = ref(false)
const drawLoading = ref(false)
const pendingSwitchNumber = ref<number | null>(null)
const runtimeSwitchLoading = ref(false)
const pendingRuntimeTarget = ref<boolean | null>(null)
const voidDialogVisible = ref(false)
const voidReason = ref('')
const voidSubmitting = ref(false)
@@ -534,6 +535,23 @@ const canVoidPeriod = computed(() => {
/** 派彩结束后的完整维护态:操作区除顶部开关外全部锁定 */
const asideOperationLocked = computed(() => snapshot.maintenance_ui === true)
function toBool(v: unknown): boolean | null {
if (typeof v === 'boolean') {
return v
}
if (typeof v === 'number' && Number.isFinite(v)) {
if (v === 1) return true
if (v === 0) return false
return null
}
if (typeof v === 'string') {
const s = v.trim().toLowerCase()
if (s === '1' || s === 'true') return true
if (s === '0' || s === 'false') return false
}
return null
}
function mergeLiveSnapshot(data: anyObj): void {
if (data.record !== undefined) {
snapshot.record = data.record
@@ -553,11 +571,20 @@ function mergeLiveSnapshot(data: anyObj): void {
snapshot.can_calculate = !!data.can_calculate
snapshot.can_draw = !!data.can_draw
snapshot.can_schedule_draw = !!(data.can_schedule_draw || data.can_draw)
if (typeof data.runtime_enabled === 'boolean') {
snapshot.runtime_enabled = data.runtime_enabled
const runtimeEnabled = toBool(data.runtime_enabled)
if (pendingRuntimeTarget.value !== null) {
// 开关请求进行中:避免 ws 的旧快照覆盖用户刚切换的状态,导致 UI 回弹
snapshot.runtime_enabled = pendingRuntimeTarget.value
} else if (runtimeEnabled !== null) {
snapshot.runtime_enabled = runtimeEnabled
}
if (typeof data.maintenance_ui === 'boolean') {
snapshot.maintenance_ui = data.maintenance_ui
if (pendingRuntimeTarget.value !== null) {
// 开启时不应显示维护中;关闭后的维护中应由服务端在对局完全结束后下发
snapshot.maintenance_ui = pendingRuntimeTarget.value ? false : snapshot.maintenance_ui
} else {
snapshot.maintenance_ui = data.maintenance_ui
}
}
syncServerClock(data.server_time)
}
@@ -615,11 +642,18 @@ async function loadSnapshot() {
}
async function onRuntimeSwitch(val: boolean | string | number): void {
const on = val === true || val === 'true' || val === 1
const on = toBool(val) === true
// 防止某些场景下 model-value 变化触发重复 change 事件,造成 runtime 接口循环调用
if (on === !!snapshot.runtime_enabled) {
const current = toBool(snapshot.runtime_enabled) === true
if (on === current) {
return
}
// el-switch 为受控组件model-value 来自 snapshot接口返回前先乐观更新避免点击后立刻回弹
snapshot.runtime_enabled = on
if (on) {
snapshot.maintenance_ui = false
}
pendingRuntimeTarget.value = on
runtimeSwitchLoading.value = true
try {
const res = await createAxios({
@@ -637,6 +671,7 @@ async function onRuntimeSwitch(val: boolean | string | number): void {
await loadSnapshot()
} finally {
runtimeSwitchLoading.value = false
pendingRuntimeTarget.value = null
}
}
@@ -667,6 +702,14 @@ async function submitVoidPeriod(): Promise<void> {
})
if (res.code === 1 && res.data) {
mergeLiveSnapshot(res.data as anyObj)
const refund = (res.data as anyObj).void_refund
if (refund && typeof refund === 'object') {
const orderCount = Number(refund.order_count ?? 0)
const totalAmount = String(refund.total_amount ?? '0.00')
if (orderCount > 0) {
ElMessage.success(`已退款 ${orderCount} 笔注单,合计 ${totalAmount}`)
}
}
}
voidDialogVisible.value = false
} finally {

View File

@@ -8,16 +8,49 @@
></TableHeader>
<Table ref="tableRef"></Table>
<el-dialog class="ba-operate-dialog" :title="t('game.playRecord.review_title')" :model-value="reviewDialog.visible" width="620px" :close-on-click-modal="false" @close="closeReviewDialog">
<div v-loading="reviewDialog.loading">
<el-descriptions :column="2" border class="mb-12">
<el-descriptions-item :label="t('game.playRecord.id')">{{ reviewDialog.row?.id ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.gameRecord_period_no')">{{ reviewDialog.row?.gameRecord?.period_no ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.user_username')">{{ reviewDialog.row?.user?.username ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.channel_name')">{{ reviewDialog.row?.channel?.name ?? '-' }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.pick_numbers')">{{ formatPickNumbers({}, {}, reviewDialog.row?.pick_numbers) }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.total_amount')">{{ formatAmount({}, {}, reviewDialog.row?.total_amount) }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.win_amount')">{{ formatAmount({}, {}, reviewDialog.row?.win_amount) }}</el-descriptions-item>
<el-descriptions-item :label="t('game.playRecord.status')">{{ reviewDialog.row?.status ? t(`game.playRecord.status ${reviewDialog.row.status}`) : '-' }}</el-descriptions-item>
</el-descriptions>
<el-form label-width="90px">
<el-form-item :label="t('game.playRecord.review_remark')">
<el-input
v-model="reviewDialog.remark"
type="textarea"
:rows="3"
:placeholder="t('game.playRecord.review_remark_placeholder')"
/>
</el-form-item>
</el-form>
</div>
<template #footer>
<el-button @click="closeReviewDialog">{{ t('Cancel') }}</el-button>
<el-button type="danger" :loading="reviewDialog.loading" @click="submitReject">{{ t('game.playRecord.review_reject') }}</el-button>
<el-button type="primary" :loading="reviewDialog.loading" @click="submitApprove">{{ t('game.playRecord.review_approve') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, provide, useTemplateRef } from 'vue'
import { onMounted, provide, reactive, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import { baTableApi } from '/@/api/common'
import { defaultOptButtons } from '/@/components/table'
import TableHeader from '/@/components/table/header/index.vue'
import Table from '/@/components/table/index.vue'
import baTableClass from '/@/utils/baTable'
import createAxios from '/@/utils/axios'
defineOptions({
name: 'game/playRecord',
@@ -25,6 +58,14 @@ defineOptions({
const { t } = useI18n()
const tableRef = useTemplateRef('tableRef')
const optButtons: OptButton[] = defaultOptButtons([])
const reviewDialog = reactive({
visible: false,
loading: false,
id: 0,
remark: '',
row: null as any,
})
function formatPickNumbers(_row: anyObj, _column: any, cellValue: unknown) {
if (cellValue === null || cellValue === undefined) {
@@ -119,7 +160,6 @@ const baTable = new baTableClass(
replaceValue: { '0': t('game.playRecord.is_auto 0'), '1': t('game.playRecord.is_auto 1') },
},
{ label: t('game.playRecord.win_amount'), prop: 'win_amount', align: 'center', minWidth: 110, operator: 'RANGE', formatter: formatAmount },
{ label: t('game.playRecord.jackpot_extra_amount'), prop: 'jackpot_extra_amount', align: 'center', minWidth: 120, operator: 'RANGE', formatter: formatAmount },
{
label: t('game.playRecord.status'),
prop: 'status',
@@ -128,11 +168,13 @@ const baTable = new baTableClass(
operator: 'eq',
render: 'tag',
effect: 'dark',
custom: { '1': 'warning', '2': 'success', '3': 'danger' },
custom: { '1': 'warning', '2': 'success', '3': 'danger', '4': 'info', '5': 'warning' },
replaceValue: {
'1': t('game.playRecord.status 1'),
'2': t('game.playRecord.status 2'),
'3': t('game.playRecord.status 3'),
'4': t('game.playRecord.status 4'),
'5': t('game.playRecord.status 5'),
},
},
{
@@ -166,12 +208,110 @@ const baTable = new baTableClass(
width: 170,
timeFormat: 'yyyy-mm-dd hh:MM:ss',
},
{ label: t('Operate'), align: 'center', width: 90, render: 'buttons', buttons: optButtons, operator: false, fixed: 'right' },
],
dblClickNotEditColumn: [undefined],
},
{}
)
const openReviewDialog = (row: any) => {
const idRaw = row?.id
const id = typeof idRaw === 'number' ? idRaw : Number(idRaw)
if (!Number.isFinite(id) || id <= 0) {
ElMessage.error(t('Parameter error'))
return
}
reviewDialog.visible = true
reviewDialog.id = id
reviewDialog.remark = ''
reviewDialog.row = row ?? null
}
const closeReviewDialog = (force = false) => {
if (reviewDialog.loading && !force) return
reviewDialog.visible = false
reviewDialog.id = 0
reviewDialog.remark = ''
reviewDialog.row = null
}
const submitApprove = async () => {
if (!reviewDialog.id) return
reviewDialog.loading = true
try {
await createAxios(
{
url: '/admin/game.PlayRecord/approveJackpot',
method: 'post',
data: { id: reviewDialog.id, remark: reviewDialog.remark },
},
{ showSuccessMessage: false }
)
ElMessage.success(t('game.playRecord.review_approve_success'))
if (reviewDialog.row) {
reviewDialog.row.status = 2
reviewDialog.row.can_jackpot_approve = 0
}
closeReviewDialog(true)
baTable.onTableHeaderAction('refresh', { event: 'review-approve' })
} catch (error: any) {
const message = typeof error?.message === 'string' && error.message !== '' ? error.message : t('Operation failed')
ElMessage.error(message)
} finally {
reviewDialog.loading = false
}
}
const submitReject = async () => {
if (!reviewDialog.id) return
const remark = String(reviewDialog.remark ?? '').trim()
if (!remark) {
ElMessage.error(t('game.playRecord.review_remark_required'))
return
}
reviewDialog.loading = true
try {
await createAxios(
{
url: '/admin/game.PlayRecord/rejectJackpot',
method: 'post',
data: { id: reviewDialog.id, remark },
},
{ showSuccessMessage: false }
)
ElMessage.success(t('game.playRecord.review_reject_success'))
if (reviewDialog.row) {
reviewDialog.row.status = 4
reviewDialog.row.can_jackpot_approve = 0
}
closeReviewDialog(true)
baTable.onTableHeaderAction('refresh', { event: 'review-reject' })
} catch (error: any) {
const message = typeof error?.message === 'string' && error.message !== '' ? error.message : t('Operation failed')
ElMessage.error(message)
} finally {
reviewDialog.loading = false
}
}
optButtons.unshift({
render: 'tipButton',
name: 'approve-jackpot',
title: 'game.playRecord.review_open',
text: '',
type: 'warning',
icon: 'fa fa-check',
disabledTip: true,
display: (row: any) => {
const v = row?.can_jackpot_approve
return String(v ?? '') === '1'
},
click: (row: any) => {
openReviewDialog(row)
},
})
provide('baTable', baTable)
onMounted(() => {

View File

@@ -1,4 +1,4 @@
<template>
<template>
<div class="default-main ba-table-box">
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
@@ -6,50 +6,16 @@
:buttons="['refresh', 'comSearch', 'quickSearch', 'columnDisplay']"
:quick-search-placeholder="t('Quick search placeholder', { fields: t('game.record.quick Search Fields') })"
>
<el-button v-blur class="table-header-operate btns-ml-12" type="warning" @click="openAbnormalDialog">
<Icon name="fa fa-exclamation-triangle" />
<span class="table-header-operate-text">{{ t('game.record.view_abnormal_rounds') }}</span>
</el-button>
</TableHeader>
<Table ref="tableRef"></Table>
<PopupForm />
<el-dialog
class="ba-operate-dialog"
:title="t('game.record.abnormal_dialog_title')"
:model-value="abnormalDialog.visible"
width="900px"
:close-on-click-modal="false"
@close="closeAbnormalDialog"
>
<div v-loading="abnormalDialog.loading">
<el-alert type="info" :closable="false" show-icon class="mb-12">
{{ t('game.record.abnormal_dialog_tip') }}
</el-alert>
<el-table :data="abnormalDialog.list" border size="small" max-height="420">
<el-table-column prop="period_no" :label="t('game.record.period_no')" min-width="180" />
<el-table-column :label="t('game.record.abnormal_from_status')" min-width="120">
<template #default="scope">{{ formatStatusLabel(scope.row.abnormal_from_status) }}</template>
</el-table-column>
<el-table-column prop="refunded_user_count" :label="t('game.record.refunded_user_count')" min-width="120" />
<el-table-column prop="refunded_order_count" :label="t('game.record.refunded_order_count')" min-width="120" />
<el-table-column prop="refunded_total_amount" :label="t('game.record.refunded_total_amount')" min-width="140" />
<el-table-column prop="recovered_at" :label="t('game.record.recovered_at')" min-width="170">
<template #default="scope">{{ formatRecoveredTime(scope.row.recovered_at) }}</template>
</el-table-column>
</el-table>
</div>
<template #footer>
<el-button @click="closeAbnormalDialog">{{ t('Cancel') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, provide, reactive, useTemplateRef } from 'vue'
import { onMounted, provide, useTemplateRef } from 'vue'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus'
import PopupForm from './popupForm.vue'
@@ -58,9 +24,7 @@ import { defaultOptButtons } from '/@/components/table'
import TableHeader from '/@/components/table/header/index.vue'
import Table from '/@/components/table/index.vue'
import baTableClass from '/@/utils/baTable'
import createAxios from '/@/utils/axios'
import { timeFormat } from '/@/utils/common'
import { routePush } from '/@/utils/router'
defineOptions({
name: 'game/record',
})
@@ -68,19 +32,37 @@ defineOptions({
const { t } = useI18n()
const tableRef = useTemplateRef('tableRef')
const optButtons: OptButton[] = defaultOptButtons(['edit'])
const abnormalDialog = reactive({
visible: false,
loading: false,
list: [] as any[],
})
const formatCoin = (_row: any, _column: any, cellValue: number | string | null | undefined) => {
if (cellValue === null || cellValue === undefined || cellValue === '') return ''
if (cellValue === null || cellValue === undefined || cellValue === '') return '-'
const n = Number(cellValue)
if (Number.isNaN(n)) return ''
if (Number.isNaN(n)) return '-'
return n.toFixed(2)
}
optButtons.unshift({
render: 'tipButton',
name: 'view-play-record',
title: 'game.record.view_play_records',
text: '',
type: 'success',
icon: 'fa fa-list',
class: 'table-row-view-play-record',
disabledTip: false,
click: (row: any) => {
const pidRaw = row?.id
const pid = typeof pidRaw === 'number' ? pidRaw : Number(pidRaw)
if (!Number.isFinite(pid) || pid <= 0) {
ElMessage.error(t('Parameter error'))
return
}
void routePush({
path: '/admin/game/playRecord',
query: { period_id: String(pid) },
})
},
})
const baTable = new baTableClass(
new baTableApi('/admin/game.Record/'),
{
@@ -88,8 +70,25 @@ const baTable = new baTableClass(
column: [
{ type: 'selection', align: 'center', operator: false },
{ label: t('game.record.id'), prop: 'id', align: 'center', width: 100, operator: 'RANGE', sortable: 'custom' },
{ label: t('game.record.period_no'), prop: 'period_no', align: 'center', minWidth: 180, operatorPlaceholder: t('Fuzzy query'), operator: 'LIKE' },
{ label: t('game.record.period_start_at'), prop: 'period_start_at', align: 'center', width: 170, render: 'datetime', operator: 'RANGE', comSearchRender: 'datetime', sortable: 'custom', timeFormat: 'yyyy-mm-dd hh:MM:ss' },
{
label: t('game.record.period_no'),
prop: 'period_no',
align: 'center',
minWidth: 180,
operatorPlaceholder: t('Fuzzy query'),
operator: 'LIKE',
},
{
label: t('game.record.period_start_at'),
prop: 'period_start_at',
align: 'center',
width: 170,
render: 'datetime',
operator: 'RANGE',
comSearchRender: 'datetime',
sortable: 'custom',
timeFormat: 'yyyy-mm-dd hh:MM:ss',
},
{
label: t('game.record.status'),
prop: 'status',
@@ -98,8 +97,16 @@ const baTable = new baTableClass(
operator: 'eq',
render: 'tag',
effect: 'dark',
custom: { '0': 'success', '1': 'warning', '2': 'info', '3': 'primary', '4': 'warning', '5': 'danger' },
replaceValue: { '0': t('game.record.status 0'), '1': t('game.record.status 1'), '2': t('game.record.status 2'), '3': t('game.record.status 3'), '4': t('game.record.status 4'), '5': t('game.record.status 5') },
custom: { '0': 'success', '1': 'warning', '2': 'info', '3': 'primary', '4': 'warning', '5': 'danger', '6': 'danger' },
replaceValue: {
'0': t('game.record.status 0'),
'1': t('game.record.status 1'),
'2': t('game.record.status 2'),
'3': t('game.record.status 3'),
'4': t('game.record.status 4'),
'5': t('game.record.status 5'),
'6': t('game.record.status 6'),
},
},
{
label: t('game.record.draw_mode'),
@@ -111,7 +118,6 @@ const baTable = new baTableClass(
custom: { '0': 'info', '1': 'warning' },
replaceValue: { '0': t('game.record.draw_mode 0'), '1': t('game.record.draw_mode 1') },
},
{ label: t('game.record.preset_number'), prop: 'preset_number', align: 'center', width: 100, operator: 'RANGE' },
{ label: t('game.record.result_number'), prop: 'result_number', align: 'center', width: 100, operator: 'RANGE' },
{
label: t('game.record.platform_profit_amount'),
@@ -130,9 +136,37 @@ const baTable = new baTableClass(
operator: 'RANGE',
sortable: false,
},
{ label: t('game.record.void_reason'), prop: 'void_reason', align: 'center', minWidth: 140, operatorPlaceholder: t('Fuzzy query'), operator: 'LIKE', showOverflowTooltip: true },
{ label: t('game.record.create_time'), prop: 'create_time', align: 'center', render: 'datetime', operator: 'RANGE', comSearchRender: 'datetime', sortable: 'custom', width: 170, timeFormat: 'yyyy-mm-dd hh:MM:ss' },
{ label: t('game.record.update_time'), prop: 'update_time', align: 'center', render: 'datetime', operator: 'RANGE', comSearchRender: 'datetime', sortable: 'custom', width: 170, timeFormat: 'yyyy-mm-dd hh:MM:ss' },
{
label: t('game.record.void_reason'),
prop: 'void_reason',
align: 'center',
minWidth: 140,
operatorPlaceholder: t('Fuzzy query'),
operator: 'LIKE',
showOverflowTooltip: true,
},
{
label: t('game.record.create_time'),
prop: 'create_time',
align: 'center',
render: 'datetime',
operator: 'RANGE',
comSearchRender: 'datetime',
sortable: 'custom',
width: 170,
timeFormat: 'yyyy-mm-dd hh:MM:ss',
},
{
label: t('game.record.update_time'),
prop: 'update_time',
align: 'center',
render: 'datetime',
operator: 'RANGE',
comSearchRender: 'datetime',
sortable: 'custom',
width: 170,
timeFormat: 'yyyy-mm-dd hh:MM:ss',
},
{ label: t('Operate'), align: 'center', width: 100, render: 'buttons', buttons: optButtons, operator: false, fixed: 'right' },
],
dblClickNotEditColumn: [undefined],
@@ -142,50 +176,6 @@ const baTable = new baTableClass(
}
)
const formatStatusLabel = (status: number) => {
const key = String(status)
if (!['0', '1', '2', '3', '4', '5'].includes(key)) {
return '-'
}
return t(`game.record.status ${key}`)
}
const openAbnormalDialog = async () => {
abnormalDialog.visible = true
abnormalDialog.loading = true
try {
const response = await createAxios(
{
url: '/admin/game.Record/abnormalList',
method: 'get',
params: { limit: 100 },
},
{
showSuccessMessage: false,
}
)
const data = response?.data ?? {}
abnormalDialog.list = Array.isArray(data.list) ? data.list : []
} catch (error: any) {
const message = typeof error?.message === 'string' && error.message !== '' ? error.message : t('game.record.load_abnormal_failed')
ElMessage.error(message)
abnormalDialog.list = []
} finally {
abnormalDialog.loading = false
}
}
const closeAbnormalDialog = () => {
abnormalDialog.visible = false
}
const formatRecoveredTime = (timestamp: number) => {
if (!timestamp) {
return '-'
}
return timeFormat(timestamp)
}
provide('baTable', baTable)
onMounted(() => {

View File

@@ -24,7 +24,6 @@
prop="draw_mode"
:input-attr="{ content: { '0': t('game.record.draw_mode 0'), '1': t('game.record.draw_mode 1') } }"
/>
<FormItem :label="t('game.record.preset_number')" type="number" v-model="baTable.form.items!.preset_number" prop="preset_number" :input-attr="{ step: 1, min: 1, max: 36 }" />
<FormItem :label="t('game.record.result_number')" type="number" v-model="baTable.form.items!.result_number" prop="result_number" :input-attr="{ step: 1, min: 1, max: 36 }" />
<FormItem :label="t('game.record.platform_profit_amount')" type="string" v-model="baTable.form.items!.platform_profit_amount" prop="platform_profit_amount" />
<FormItem :label="t('game.record.winner_user_count')" type="number" v-model="baTable.form.items!.winner_user_count" prop="winner_user_count" :input-attr="{ step: 1, min: 0 }" />