439 lines
15 KiB
Vue
439 lines
15 KiB
Vue
<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 />
|
|
|
|
<TableHeader
|
|
:buttons="['refresh', 'add', 'edit', 'delete', 'comSearch', 'quickSearch', 'columnDisplay']"
|
|
:quick-search-placeholder="t('Quick search placeholder', { fields: t('user.user.quick Search Fields') })"
|
|
></TableHeader>
|
|
|
|
<Table ref="tableRef"></Table>
|
|
|
|
<PopupForm />
|
|
|
|
<el-dialog v-model="walletDialogVisible" class="ba-operate-dialog wallet-adjust-dialog" :close-on-click-modal="false" width="520px">
|
|
<template #header>
|
|
<div class="title">{{ t('user.user.wallet_adjust_title') }}</div>
|
|
</template>
|
|
<el-scrollbar class="ba-table-form-scrollbar">
|
|
<div class="ba-operate-form wallet-adjust-form">
|
|
<el-form :label-position="config.layout.shrink ? 'top' : 'right'" :label-width="config.layout.shrink ? '' : '110px'">
|
|
<el-form-item :label="t('user.user.username')">
|
|
<el-input :model-value="walletForm.username" readonly />
|
|
</el-form-item>
|
|
<el-form-item :label="t('user.user.coin')">
|
|
<el-tag type="primary">{{ walletForm.current_coin }}</el-tag>
|
|
</el-form-item>
|
|
<el-form-item :label="t('user.user.wallet_adjust_op')">
|
|
<el-radio-group v-model="walletForm.op" class="wallet-adjust-op-group" @change="syncWalletRemark">
|
|
<el-radio label="credit">{{ t('user.user.wallet_adjust_credit') }}</el-radio>
|
|
<el-radio label="deduct">{{ t('user.user.wallet_adjust_deduct') }}</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item :label="t('user.user.wallet_adjust_amount')">
|
|
<el-input-number
|
|
class="wallet-adjust-amount-input"
|
|
v-model="walletForm.amount"
|
|
:min="0.01"
|
|
:step="0.01"
|
|
:precision="2"
|
|
@change="syncWalletRemark"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item :label="t('user.user.remark')">
|
|
<el-input v-model="walletForm.remark" type="textarea" :rows="3" />
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</el-scrollbar>
|
|
<template #footer>
|
|
<div class="wallet-adjust-dialog-footer">
|
|
<el-button @click="walletDialogVisible = false">{{ t('Cancel') }}</el-button>
|
|
<el-button type="primary" :loading="walletSubmitting" @click="submitWalletAdjust">{{ t('Save') }}</el-button>
|
|
</div>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, provide, reactive, ref, useTemplateRef } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { ElMessage } from 'element-plus'
|
|
import PopupForm from './popupForm.vue'
|
|
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'
|
|
import { useConfig } from '/@/stores/config'
|
|
|
|
defineOptions({
|
|
name: 'user/user',
|
|
})
|
|
|
|
const { t } = useI18n()
|
|
const config = useConfig()
|
|
const tableRef = useTemplateRef('tableRef')
|
|
const optButtons: OptButton[] = defaultOptButtons(['edit', 'delete'])
|
|
|
|
function formatCoin(_row: anyObj, _column: any, cellValue: unknown) {
|
|
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
|
return '-'
|
|
}
|
|
const s = String(cellValue).trim().replace(',', '.')
|
|
const n = parseFloat(s)
|
|
if (!Number.isFinite(n)) {
|
|
return String(cellValue)
|
|
}
|
|
return n.toFixed(2)
|
|
}
|
|
|
|
/** 杩斿洖澶氭爣绛炬枃妗堟暟缁勶紝渚?render: tags 浣跨敤 */
|
|
function formatRiskFlags(row: anyObj, _column: any, cellValue: unknown) {
|
|
const raw = cellValue !== undefined && cellValue !== null && cellValue !== '' ? cellValue : row.risk_flags
|
|
const f = typeof raw === 'number' ? raw : parseInt(String(raw ?? '0'), 10) || 0
|
|
const parts: string[] = []
|
|
if (f & 1) parts.push(t('user.user.risk_no_login'))
|
|
if (f & 2) parts.push(t('user.user.risk_no_bet'))
|
|
if (f & 4) parts.push(t('user.user.risk_no_withdraw'))
|
|
if (!parts.length) parts.push(t('user.user.risk_none'))
|
|
return parts
|
|
}
|
|
|
|
function buildDisplayAmount(v: unknown): string {
|
|
if (v === null || v === undefined || v === '') return '0.00'
|
|
const n = parseFloat(String(v).trim().replace(',', '.'))
|
|
if (!Number.isFinite(n)) return '0.00'
|
|
return n.toFixed(2)
|
|
}
|
|
|
|
const walletDialogVisible = ref(false)
|
|
const walletSubmitting = ref(false)
|
|
const walletForm = reactive({
|
|
user_id: 0,
|
|
username: '',
|
|
current_coin: '0.00',
|
|
op: 'credit',
|
|
amount: 100,
|
|
remark: '',
|
|
})
|
|
|
|
function syncWalletRemark() {
|
|
const action = walletForm.op === 'credit' ? t('user.user.wallet_adjust_credit') : t('user.user.wallet_adjust_deduct')
|
|
walletForm.remark = t('user.user.wallet_adjust_default_remark', {
|
|
admin: t('user.user.wallet_adjust_operator_admin'),
|
|
action,
|
|
amount: Number(walletForm.amount || 0).toFixed(2),
|
|
})
|
|
}
|
|
|
|
function openWalletDialog(row: anyObj) {
|
|
walletForm.user_id = Number(row.id || 0)
|
|
walletForm.username = String(row.username ?? '-')
|
|
walletForm.current_coin = buildDisplayAmount(row.coin)
|
|
walletForm.op = 'credit'
|
|
walletForm.amount = 100
|
|
syncWalletRemark()
|
|
walletDialogVisible.value = true
|
|
}
|
|
|
|
async function submitWalletAdjust() {
|
|
if (!walletForm.user_id) return
|
|
if (!(walletForm.amount > 0)) {
|
|
ElMessage.error(t('user.user.wallet_adjust_amount_invalid'))
|
|
return
|
|
}
|
|
walletSubmitting.value = true
|
|
try {
|
|
const res = await createAxios({
|
|
url: '/admin/user.User/walletAdjust',
|
|
method: 'post',
|
|
data: {
|
|
user_id: walletForm.user_id,
|
|
op: walletForm.op,
|
|
amount: Number(walletForm.amount).toFixed(2),
|
|
remark: walletForm.remark,
|
|
},
|
|
})
|
|
if (res.code === 1) {
|
|
ElMessage.success(res.msg || t('Success'))
|
|
walletDialogVisible.value = false
|
|
await baTable.getData()
|
|
} else {
|
|
ElMessage.error(res.msg || t('Unknown error'))
|
|
}
|
|
} finally {
|
|
walletSubmitting.value = false
|
|
}
|
|
}
|
|
|
|
const baTable = new baTableClass(
|
|
new baTableApi('/admin/user.User/'),
|
|
{
|
|
pk: 'id',
|
|
column: [
|
|
{ type: 'selection', align: 'center', operator: false },
|
|
{ label: t('user.user.id'), prop: 'id', align: 'center', width: 70, operator: 'RANGE', sortable: 'custom' },
|
|
{
|
|
label: t('user.user.username'),
|
|
prop: 'username',
|
|
align: 'center',
|
|
operatorPlaceholder: t('Fuzzy query'),
|
|
sortable: false,
|
|
operator: 'LIKE',
|
|
},
|
|
{ label: t('user.user.phone'), prop: 'phone', align: 'center', operatorPlaceholder: t('Fuzzy query'), sortable: false, operator: 'LIKE' },
|
|
{
|
|
label: t('user.user.email'),
|
|
prop: 'email',
|
|
align: 'center',
|
|
minWidth: 120,
|
|
showOverflowTooltip: true,
|
|
operatorPlaceholder: t('Fuzzy query'),
|
|
operator: 'LIKE',
|
|
},
|
|
{
|
|
label: t('user.user.head_image'),
|
|
prop: 'head_image',
|
|
align: 'center',
|
|
width: 72,
|
|
operator: false,
|
|
render: 'image',
|
|
},
|
|
{
|
|
label: t('user.user.uuid'),
|
|
prop: 'uuid',
|
|
align: 'center',
|
|
showOverflowTooltip: true,
|
|
operatorPlaceholder: t('Fuzzy query'),
|
|
sortable: false,
|
|
operator: 'LIKE',
|
|
},
|
|
{
|
|
label: t('user.user.register_invite_code'),
|
|
prop: 'register_invite_code',
|
|
align: 'center',
|
|
minWidth: 100,
|
|
showOverflowTooltip: true,
|
|
operator: 'LIKE',
|
|
},
|
|
{
|
|
label: t('user.user.coin'),
|
|
prop: 'coin',
|
|
align: 'center',
|
|
minWidth: 100,
|
|
sortable: false,
|
|
operator: 'RANGE',
|
|
render: 'tag',
|
|
formatter: formatCoin,
|
|
customRenderAttr: {
|
|
tag: ({ row }) => ({
|
|
class: 'wallet-balance-tag',
|
|
onClick: () => openWalletDialog(row),
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
label: t('user.user.total_deposit_coin'),
|
|
prop: 'total_deposit_coin',
|
|
align: 'center',
|
|
minWidth: 110,
|
|
sortable: false,
|
|
operator: 'RANGE',
|
|
formatter: formatCoin,
|
|
},
|
|
{
|
|
label: t('user.user.total_withdraw_coin'),
|
|
prop: 'total_withdraw_coin',
|
|
align: 'center',
|
|
minWidth: 110,
|
|
sortable: false,
|
|
operator: 'RANGE',
|
|
formatter: formatCoin,
|
|
},
|
|
{
|
|
label: t('user.user.bet_flow_coin'),
|
|
prop: 'bet_flow_coin',
|
|
align: 'center',
|
|
minWidth: 110,
|
|
sortable: false,
|
|
operator: 'RANGE',
|
|
formatter: formatCoin,
|
|
},
|
|
{
|
|
label: t('user.user.risk_flags'),
|
|
prop: 'risk_flags',
|
|
align: 'center',
|
|
render: 'tags',
|
|
minWidth: 200,
|
|
operator: false,
|
|
formatter: formatRiskFlags,
|
|
custom: {
|
|
[t('user.user.risk_none')]: 'primary',
|
|
[t('user.user.risk_no_login')]: 'danger',
|
|
[t('user.user.risk_no_bet')]: 'danger',
|
|
[t('user.user.risk_no_withdraw')]: 'danger',
|
|
},
|
|
},
|
|
{ label: t('user.user.current_streak'), prop: 'current_streak', align: 'center', width: 90, operator: 'RANGE' },
|
|
{
|
|
label: t('user.user.last_bet_period_no'),
|
|
prop: 'last_bet_period_no',
|
|
align: 'center',
|
|
minWidth: 120,
|
|
showOverflowTooltip: true,
|
|
operator: 'LIKE',
|
|
},
|
|
{
|
|
label: t('user.user.status'),
|
|
prop: 'status',
|
|
align: 'center',
|
|
operator: 'eq',
|
|
sortable: false,
|
|
render: 'switch',
|
|
replaceValue: { '0': t('user.user.status 0'), '1': t('user.user.status 1') },
|
|
},
|
|
{
|
|
label: t('user.user.channel__name'),
|
|
prop: 'channel.name',
|
|
align: 'center',
|
|
minWidth: 100,
|
|
operatorPlaceholder: t('Fuzzy query'),
|
|
render: 'tags',
|
|
operator: 'LIKE',
|
|
comSearchRender: 'string',
|
|
},
|
|
{
|
|
label: t('user.user.admin__username'),
|
|
prop: 'admin.username',
|
|
align: 'center',
|
|
minWidth: 90,
|
|
effect: 'plain',
|
|
operatorPlaceholder: t('Fuzzy query'),
|
|
render: 'tags',
|
|
operator: 'LIKE',
|
|
comSearchRender: 'string',
|
|
customRenderAttr: {
|
|
tag: () => ({
|
|
color: '#e8f3ff',
|
|
style: { color: '#1677ff', borderColor: '#91caff' },
|
|
}),
|
|
},
|
|
},
|
|
{
|
|
label: t('user.user.remark'),
|
|
prop: 'remark',
|
|
align: 'center',
|
|
minWidth: 100,
|
|
showOverflowTooltip: true,
|
|
operatorPlaceholder: t('Fuzzy query'),
|
|
},
|
|
{
|
|
label: t('user.user.create_time'),
|
|
prop: 'create_time',
|
|
align: 'center',
|
|
render: 'datetime',
|
|
operator: 'RANGE',
|
|
comSearchRender: 'datetime',
|
|
sortable: 'custom',
|
|
width: 160,
|
|
timeFormat: 'yyyy-mm-dd hh:MM:ss',
|
|
},
|
|
{
|
|
label: t('user.user.update_time'),
|
|
prop: 'update_time',
|
|
align: 'center',
|
|
render: 'datetime',
|
|
operator: 'RANGE',
|
|
comSearchRender: 'datetime',
|
|
sortable: 'custom',
|
|
width: 160,
|
|
timeFormat: 'yyyy-mm-dd hh:MM:ss',
|
|
},
|
|
{ label: t('Operate'), align: 'center', width: 80, render: 'buttons', buttons: optButtons, operator: false, fixed: 'right' },
|
|
],
|
|
dblClickNotEditColumn: [undefined, 'status'],
|
|
},
|
|
{
|
|
defaultItems: {
|
|
status: '1',
|
|
coin: '0.00',
|
|
total_deposit_coin: '0.00',
|
|
total_withdraw_coin: '0.00',
|
|
bet_flow_coin: '0.00',
|
|
risk_flags: 0,
|
|
current_streak: 0,
|
|
last_bet_period_no: '',
|
|
register_invite_code: '',
|
|
email: '',
|
|
head_image: '',
|
|
},
|
|
}
|
|
)
|
|
|
|
provide('baTable', baTable)
|
|
|
|
onMounted(() => {
|
|
baTable.table.ref = tableRef.value
|
|
baTable.mount()
|
|
baTable.getData()?.then(() => {
|
|
baTable.initSort()
|
|
baTable.dragSort()
|
|
})
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.wallet-balance-tag {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.wallet-adjust-form :deep(.el-form-item) {
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.wallet-adjust-op-group {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
|
|
.wallet-adjust-amount-input {
|
|
width: 100%;
|
|
}
|
|
|
|
.wallet-adjust-amount-input :deep(.el-input__wrapper) {
|
|
width: 100%;
|
|
}
|
|
|
|
.wallet-adjust-dialog-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.wallet-adjust-dialog :deep(.el-dialog) {
|
|
width: calc(100vw - 16px) !important;
|
|
margin-top: 4vh !important;
|
|
}
|
|
|
|
.wallet-adjust-op-group {
|
|
gap: 8px 12px;
|
|
}
|
|
|
|
.wallet-adjust-dialog-footer {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.wallet-adjust-dialog-footer .el-button {
|
|
flex: 1;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
|