feat: multi-tier agent hierarchy, wallet ledger, and player UX polish
Add configurable agent max level and default sub-agent credit ratio, per-agent block direct player login on suspend, admin/agent wallet transaction views, and match detail my-bets section with refreshed player card styling. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -32,6 +32,7 @@ import {
|
||||
import type { TableInstance } from 'element-plus';
|
||||
import WalletTransferContext from '../../components/WalletTransferContext.vue';
|
||||
import AgentCreditContext from '../../components/AgentCreditContext.vue';
|
||||
import PlayerWalletLedgerDialog from '../../components/PlayerWalletLedgerDialog.vue';
|
||||
import {
|
||||
depositAmountCap,
|
||||
parsePlayerAvailable,
|
||||
@@ -45,11 +46,16 @@ import {
|
||||
const { t, localeTag } = useAdminLocale();
|
||||
const auth = useAuthStore();
|
||||
|
||||
/* L1 agents can manage sub-agents; L2 cannot */
|
||||
const isTier1 = computed(() => auth.isTier1Agent.value);
|
||||
const profile = ref<{
|
||||
creditLimit?: string;
|
||||
usedCredit?: string;
|
||||
availableCredit?: string;
|
||||
canManageSubAgents?: boolean;
|
||||
}>({});
|
||||
|
||||
/* ─── Credit profile ─── */
|
||||
const profile = ref<{ creditLimit?: string; usedCredit?: string; availableCredit?: string }>({});
|
||||
const canManageSubAgents = computed(
|
||||
() => profile.value.canManageSubAgents === true || auth.canManageSubAgents.value,
|
||||
);
|
||||
|
||||
/* ─── Top-level tab: players | subAgents ─── */
|
||||
const activeTab = ref('players');
|
||||
@@ -161,7 +167,7 @@ const transferAmountCapError = computed(() => {
|
||||
onMounted(async () => {
|
||||
await loadProfile();
|
||||
await loadPlayers();
|
||||
if (isTier1.value) {
|
||||
if (canManageSubAgents.value) {
|
||||
await loadSubAgents();
|
||||
}
|
||||
});
|
||||
@@ -183,6 +189,16 @@ async function loadPlayers() {
|
||||
}
|
||||
}
|
||||
|
||||
const walletLedgerVisible = ref(false);
|
||||
const walletLedgerPlayerId = ref('');
|
||||
const walletLedgerPlayerUsername = ref<string | null>(null);
|
||||
|
||||
function openPlayerWalletLedger(playerId: string, playerUsername?: string | null) {
|
||||
walletLedgerPlayerId.value = playerId;
|
||||
walletLedgerPlayerUsername.value = playerUsername ?? null;
|
||||
walletLedgerVisible.value = true;
|
||||
}
|
||||
|
||||
async function loadSubAgents() {
|
||||
loadingAgents.value = true;
|
||||
try {
|
||||
@@ -622,10 +638,11 @@ function statusTagType(s: string) {
|
||||
<el-table-column :label="t('user.col.created')" min-width="148">
|
||||
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.actions')" width="300" align="center" fixed="right">
|
||||
<el-table-column :label="t('common.actions')" width="380" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="action-btns">
|
||||
<el-button size="small" type="primary" link @click="openEdit(row)">{{ t('common.edit') }}</el-button>
|
||||
<el-button size="small" type="primary" link @click="openPlayerWalletLedger(row.id, row.username)">{{ t('user.action.view_wallet_ledger') }}</el-button>
|
||||
<el-button size="small" type="success" link @click="openTransfer('deposit', row)">{{ t('common.topup') }}</el-button>
|
||||
<el-button size="small" type="warning" link @click="openTransfer('withdraw', row)">{{ t('agent_portal.withdraw_btn_label') }}</el-button>
|
||||
<el-button v-if="row.status === 'ACTIVE'" size="small" link type="warning" @click="toggleFreeze(row)">{{ t('common.freeze') }}</el-button>
|
||||
@@ -637,7 +654,7 @@ function statusTagType(s: string) {
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ══════ Tab: 下级代理 (仅一级代理可见) ══════ -->
|
||||
<el-tab-pane v-if="isTier1" :label="`${t('nav.subAgents')} (${subAgents.length})`" name="subAgents">
|
||||
<el-tab-pane v-if="canManageSubAgents" :label="`${t('nav.subAgents')} (${subAgents.length})`" name="subAgents">
|
||||
<div class="inner-toolbar">
|
||||
<el-form inline size="small" style="flex: 1">
|
||||
<el-form-item :label="t('common.search')">
|
||||
@@ -955,6 +972,12 @@ function statusTagType(s: string) {
|
||||
<el-button type="primary" :loading="creditLoading" @click="submitCreditSub">{{ t('common.confirm') }}</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<PlayerWalletLedgerDialog
|
||||
v-model="walletLedgerVisible"
|
||||
:player-id="walletLedgerPlayerId"
|
||||
:player-username="walletLedgerPlayerUsername"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user