|
|
|
|
@@ -33,6 +33,9 @@ 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 InviteManageDialog from '../../components/InviteManageDialog.vue';
|
|
|
|
|
import AdminPlayerRowActions from '../../components/AdminPlayerRowActions.vue';
|
|
|
|
|
import AdminRowActionsDropdown from '../../components/AdminRowActionsDropdown.vue';
|
|
|
|
|
import {
|
|
|
|
|
depositAmountCap,
|
|
|
|
|
parsePlayerAvailable,
|
|
|
|
|
@@ -46,6 +49,7 @@ import { formatAgentLevelNumeral } from '../../utils/agent-level-label';
|
|
|
|
|
|
|
|
|
|
const { t, localeTag } = useAdminLocale();
|
|
|
|
|
const auth = useAuthStore();
|
|
|
|
|
const inviteDialogOpen = ref(false);
|
|
|
|
|
|
|
|
|
|
const profile = ref<{
|
|
|
|
|
level?: number;
|
|
|
|
|
@@ -638,8 +642,14 @@ function statusTagType(s: string) {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- ─── Top-level tabs ─── -->
|
|
|
|
|
<el-tabs v-model="activeTab" class="portal-top-tabs">
|
|
|
|
|
<InviteManageDialog v-model="inviteDialogOpen" />
|
|
|
|
|
|
|
|
|
|
<div class="portal-tabs-shell">
|
|
|
|
|
<el-button type="primary" class="invite-prominent-btn" @click="inviteDialogOpen = true">
|
|
|
|
|
{{ t('invite.menu_btn') }}
|
|
|
|
|
</el-button>
|
|
|
|
|
<!-- ─── Top-level tabs ─── -->
|
|
|
|
|
<el-tabs v-model="activeTab" class="portal-top-tabs portal-top-tabs--with-invite">
|
|
|
|
|
<!-- ══════ Tab: 直属玩家 ══════ -->
|
|
|
|
|
<el-tab-pane :label="`${t('nav.players')} (${players.length})`" name="players">
|
|
|
|
|
<div class="inner-toolbar">
|
|
|
|
|
@@ -657,6 +667,12 @@ function statusTagType(s: string) {
|
|
|
|
|
<template #empty><div class="empty-hint">{{ t('agent_portal.no_players') }}</div></template>
|
|
|
|
|
<el-table-column prop="id" :label="t('common.col_id')" width="60" />
|
|
|
|
|
<el-table-column prop="username" :label="t('user.col.username')" min-width="100" />
|
|
|
|
|
<el-table-column :label="t('user.col.invite_code')" min-width="96" show-overflow-tooltip>
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<code v-if="row.inviteCode" class="invite-code-cell">{{ row.inviteCode }}</code>
|
|
|
|
|
<span v-else>—</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column :label="t('common.status')" width="80" align="center">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<el-tag :type="statusTagType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
|
|
|
|
|
@@ -672,19 +688,17 @@ function statusTagType(s: string) {
|
|
|
|
|
<span v-else>—</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<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="380" align="center" fixed="right">
|
|
|
|
|
<el-table-column :label="t('common.actions')" min-width="300" 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>
|
|
|
|
|
<el-button v-else size="small" link type="primary" @click="toggleFreeze(row)">{{ t('common.unfreeze') }}</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<AdminPlayerRowActions
|
|
|
|
|
:row="row"
|
|
|
|
|
:show-detail="false"
|
|
|
|
|
@ledger="openPlayerWalletLedger(row.id, row.username)"
|
|
|
|
|
@edit="openEdit(row)"
|
|
|
|
|
@deposit="openTransfer('deposit', row)"
|
|
|
|
|
@withdraw="openTransfer('withdraw', row)"
|
|
|
|
|
@freeze="toggleFreeze(row)"
|
|
|
|
|
/>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
@@ -729,6 +743,12 @@ function statusTagType(s: string) {
|
|
|
|
|
<template #empty><div class="empty-hint">暂无数据</div></template>
|
|
|
|
|
<el-table-column prop="id" :label="t('common.col_id')" width="60" />
|
|
|
|
|
<el-table-column prop="username" :label="t('user.col.username')" min-width="100" />
|
|
|
|
|
<el-table-column :label="t('user.col.invite_code')" min-width="96" show-overflow-tooltip>
|
|
|
|
|
<template #default="{ row: p }">
|
|
|
|
|
<code v-if="p.inviteCode" class="invite-code-cell">{{ p.inviteCode }}</code>
|
|
|
|
|
<span v-else>—</span>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column :label="t('common.status')" width="80" align="center">
|
|
|
|
|
<template #default="{ row: p }">
|
|
|
|
|
<el-tag :type="statusTagType(p.status)" size="small">{{ statusLabel(p.status) }}</el-tag>
|
|
|
|
|
@@ -771,25 +791,22 @@ 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="280" align="center" fixed="right">
|
|
|
|
|
<el-table-column :label="t('common.actions')" width="96" align="center" fixed="right">
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
<div class="action-btns" @click.stop>
|
|
|
|
|
<el-button size="small" type="primary" link @click="openEditSub(row)">{{ t('common.edit') }}</el-button>
|
|
|
|
|
<el-button size="small" type="primary" link @click="openCreditSub(row)">{{ t('common.adjust_credit') }}</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="subAgentAccountStatus(row) === 'ACTIVE'"
|
|
|
|
|
size="small"
|
|
|
|
|
link
|
|
|
|
|
type="warning"
|
|
|
|
|
@click="toggleFreezeSub(row)"
|
|
|
|
|
>{{ t('common.freeze') }}</el-button>
|
|
|
|
|
<el-button v-else size="small" link type="primary" @click="toggleFreezeSub(row)">{{ t('common.unfreeze') }}</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<AdminRowActionsDropdown>
|
|
|
|
|
<el-dropdown-item @click="openEditSub(row)">{{ t('common.edit') }}</el-dropdown-item>
|
|
|
|
|
<el-dropdown-item @click="openCreditSub(row)">{{ t('common.adjust_credit') }}</el-dropdown-item>
|
|
|
|
|
<el-dropdown-item v-if="subAgentAccountStatus(row) === 'ACTIVE'" divided @click="toggleFreezeSub(row)">
|
|
|
|
|
<span class="action-warning">{{ t('common.freeze') }}</span>
|
|
|
|
|
</el-dropdown-item>
|
|
|
|
|
<el-dropdown-item v-else divided @click="toggleFreezeSub(row)">{{ t('common.unfreeze') }}</el-dropdown-item>
|
|
|
|
|
</AdminRowActionsDropdown>
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
</el-tabs>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- ═══════════ DIALOGS ═══════════ -->
|
|
|
|
|
|
|
|
|
|
@@ -921,9 +938,6 @@ function statusTagType(s: string) {
|
|
|
|
|
<el-input-number v-model="createSubForm.creditLimit" :min="0" :step="1000" style="width: 100%" />
|
|
|
|
|
<div class="field-hint">{{ t('agent.hint.credit_limit') }}</div>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item :label="t('agent.field.cashback_rate')">
|
|
|
|
|
<el-input-number v-model="createSubForm.cashbackRate" :min="0" :max="1" :step="0.001" :precision="4" style="width: 100%" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item :label="t('agent.field.max_single_deposit')">
|
|
|
|
|
<el-input-number v-model="createSubForm.maxSingleDeposit" :min="0" :step="100" style="width: 100%" />
|
|
|
|
|
<div class="field-hint">{{ t('agent.hint.deposit_limit_empty') }}</div>
|
|
|
|
|
@@ -1029,20 +1043,70 @@ function statusTagType(s: string) {
|
|
|
|
|
.credit-item { display: flex; flex-direction: column; gap: 4px; }
|
|
|
|
|
.credit-label { font-size: 12px; color: #666; }
|
|
|
|
|
.credit-value { font-size: 18px; font-weight: 700; color: #e0e0e0; font-variant-numeric: tabular-nums; }
|
|
|
|
|
.credit-value.c-green { color: #67c23a; }
|
|
|
|
|
.credit-value.c-green { color: var(--gold-text); }
|
|
|
|
|
.credit-divider { width: 1px; height: 32px; background: #2a2a2a; }
|
|
|
|
|
|
|
|
|
|
/* ─── Tabs ─── */
|
|
|
|
|
.portal-top-tabs { margin-bottom: 8px; }
|
|
|
|
|
.portal-tabs-shell {
|
|
|
|
|
position: relative;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.portal-top-tabs { margin-bottom: 0; }
|
|
|
|
|
.portal-top-tabs--with-invite :deep(.el-tabs__header) {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
padding-right: 108px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.portal-top-tabs :deep(.el-tabs__item) { font-size: 14px; }
|
|
|
|
|
|
|
|
|
|
.invite-prominent-btn {
|
|
|
|
|
position: absolute;
|
|
|
|
|
right: 0;
|
|
|
|
|
top: 0;
|
|
|
|
|
z-index: 2;
|
|
|
|
|
min-width: 96px;
|
|
|
|
|
height: 38px;
|
|
|
|
|
padding: 0 22px;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 10px rgba(201, 162, 39, 0.22);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.invite-code-cell {
|
|
|
|
|
font-family: ui-monospace, monospace;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
color: var(--gold-text);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ─── Inner content ─── */
|
|
|
|
|
.inner-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-bottom: 8px; }
|
|
|
|
|
.inner-table { border-radius: 6px; }
|
|
|
|
|
.expandable-table :deep(.row-expandable) { cursor: pointer; }
|
|
|
|
|
.action-btns {
|
|
|
|
|
display: flex; align-items: center; justify-content: center;
|
|
|
|
|
gap: 4px; flex-wrap: nowrap; white-space: nowrap;
|
|
|
|
|
.agent-portal-mgr :deep(.el-table) {
|
|
|
|
|
min-width: 720px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.inner-toolbar {
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
row-gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 900px) {
|
|
|
|
|
.portal-top-tabs--with-invite :deep(.el-tabs__header) {
|
|
|
|
|
padding-right: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.invite-prominent-btn {
|
|
|
|
|
position: static;
|
|
|
|
|
align-self: flex-end;
|
|
|
|
|
margin: 0 0 8px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ─── Expansion ─── */
|
|
|
|
|
@@ -1072,22 +1136,3 @@ function statusTagType(s: string) {
|
|
|
|
|
.edit-stats { margin-top: 8px; }
|
|
|
|
|
.compact-edit-form :deep(.el-form-item) { margin-bottom: 10px; }
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
/* 代理端冻结按钮:与管理端一致的金黄渐变 */
|
|
|
|
|
.agent-portal-mgr .el-button.is-link.el-button--warning {
|
|
|
|
|
color: #ffffff !important;
|
|
|
|
|
background: linear-gradient(165deg, #e8a84a 0%, #c47a18 42%, #9a5c10 100%) !important;
|
|
|
|
|
border: 1px solid rgba(232, 168, 74, 0.45) !important;
|
|
|
|
|
border-radius: 6px !important;
|
|
|
|
|
padding: 5px 11px !important;
|
|
|
|
|
box-shadow: 0 1px 0 rgba(255,255,255,.12) inset, 0 1px 6px rgba(0,0,0,.35) !important;
|
|
|
|
|
text-shadow: 0 1px 1px rgba(0,0,0,.2);
|
|
|
|
|
}
|
|
|
|
|
.agent-portal-mgr .el-button.is-link.el-button--warning:hover,
|
|
|
|
|
.agent-portal-mgr .el-button.is-link.el-button--warning:focus {
|
|
|
|
|
color: #ffffff !important;
|
|
|
|
|
background: linear-gradient(165deg, #f0bc62 0%, #d48a28 42%, #a86814 100%) !important;
|
|
|
|
|
border-color: rgba(240, 188, 98, 0.55) !important;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|