1.优化实时对局页面样式以及自动创建下一局和作废本局的记录
2.新增派彩达到game_config.jackpot_max_amount必须审核才能发放 3.新增游戏对局记录-查看游玩记录btn 3.备份MySQL数据库
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
|
||||
@@ -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: '作废原因',
|
||||
|
||||
@@ -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: '更新时间',
|
||||
|
||||
@@ -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: '加载游玩记录失败',
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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 }" />
|
||||
|
||||
Reference in New Issue
Block a user