feat: replace watermarks with corner tags, improve parlay layout and admin polish
- MatchDetailView: replace StatusWatermark with top-right solid status tags, remove match-hero background image - ParlayPanel: replace StatusWatermark with corner tags, improve toggle button style and layout spacing, fix overflow clipping - Admin: wallet ledger dialog, agent manager, and player page refinements 🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
@@ -133,7 +133,7 @@ watch(
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
:title="dialogTitle"
|
||||
width="960px"
|
||||
width="980px"
|
||||
destroy-on-close
|
||||
class="player-wallet-ledger-dialog"
|
||||
append-to-body
|
||||
@@ -172,10 +172,10 @@ watch(
|
||||
<el-table-column :label="t('finance.col.tx_id')" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.transactionId }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('agent.col.credit_type')" width="92">
|
||||
<el-table-column :label="t('agent.col.credit_type')" min-width="88">
|
||||
<template #default="{ row }">{{ walletTypeLabel(row.transactionType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('finance.col.balance_change')" width="100" align="right">
|
||||
<el-table-column :label="t('finance.col.balance_change')" min-width="96" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="formatAmountFull(row.amount)" placement="top">
|
||||
<span :class="parseFloat(row.amount) >= 0 ? 'amt-pos' : 'amt-neg'">
|
||||
@@ -184,28 +184,28 @@ watch(
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('finance.col.balance_before')" width="96" align="right">
|
||||
<el-table-column :label="t('finance.col.balance_before')" min-width="92" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="formatAmountFull(row.balanceBefore)" placement="top">
|
||||
<span>{{ formatAmount(row.balanceBefore) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('finance.col.balance_after')" width="96" align="right">
|
||||
<el-table-column :label="t('finance.col.balance_after')" min-width="92" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="formatAmountFull(row.balanceAfter)" placement="top">
|
||||
<span>{{ formatAmount(row.balanceAfter) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('finance.col.frozen_before')" width="96" align="right">
|
||||
<el-table-column :label="t('finance.col.frozen_before')" min-width="92" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="formatAmountFull(row.frozenBefore)" placement="top">
|
||||
<span>{{ formatAmount(row.frozenBefore) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('finance.col.frozen_after')" width="96" align="right">
|
||||
<el-table-column :label="t('finance.col.frozen_after')" min-width="92" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="formatAmountFull(row.frozenAfter)" placement="top">
|
||||
<span>{{ formatAmount(row.frozenAfter) }}</span>
|
||||
@@ -250,18 +250,22 @@ watch(
|
||||
|
||||
<style scoped>
|
||||
.ledger-filter {
|
||||
margin-bottom: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.ledger-filter :deep(.el-form-item) {
|
||||
margin-bottom: 0;
|
||||
margin-right: 12px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.table-wrap {
|
||||
margin: 0 -2px;
|
||||
}
|
||||
|
||||
.pager {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 12px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.amt-pos {
|
||||
@@ -283,3 +287,18 @@ watch(
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.player-wallet-ledger-dialog .el-dialog__header {
|
||||
padding: 12px 16px 6px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.player-wallet-ledger-dialog .el-dialog__body {
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.player-wallet-ledger-dialog .el-dialog__headerbtn {
|
||||
top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -543,6 +543,9 @@ export const adminPagesMs: Record<string, string> = {
|
||||
'agent_portal.no_players': 'Tiada pemain langsung. Klik butang di atas untuk cipta.',
|
||||
'agent_portal.search_sub_agent_ph': 'Nama pengguna atau ID',
|
||||
'agent_portal.no_sub_agents': 'Tiada ejen peringkat 2. Klik butang di atas untuk cipta.',
|
||||
'agent_portal.no_sub_agents_level': 'Tiada ejen peringkat {level}. Klik butang di atas untuk cipta.',
|
||||
'agent_portal.sub_agent_players_readonly': 'Pemain langsung di bawah sub-ejen ini hanya boleh dilihat. Pembukaan akaun dan tambah baki diurus oleh sub-ejen.',
|
||||
'agent_portal.sub_agent_players_readonly_level': 'Pemain langsung di bawah ejen peringkat {level} ini hanya boleh dilihat. Pembukaan akaun dan tambah baki diurus oleh ejen tersebut.',
|
||||
'agent_portal.create_sub_agent_dialog': 'Ejen peringkat 2 baharu',
|
||||
'agent_portal.sub_agent_credit_hint': 'Kredit awal diperuntukkan dari had tersedia anda',
|
||||
'agent_portal.adjust_credit_dialog': 'Laraskan kredit {name}',
|
||||
|
||||
@@ -608,6 +608,8 @@ export const adminPagesZh: Record<string, string> = {
|
||||
'agent_portal.no_players': '暂无直属玩家,点击右上角创建',
|
||||
'agent_portal.search_sub_agent_ph': '用户名或 ID',
|
||||
'agent_portal.no_sub_agents': '暂无二级代理,点击右上角创建',
|
||||
'agent_portal.no_sub_agents_level': '暂无{level}级代理,点击右上角创建',
|
||||
'agent_portal.sub_agent_players_readonly_level': '以下为该{level}级代理直属玩家,仅可查看;开户、上分等操作由该代理自行处理。',
|
||||
'agent_portal.create_sub_agent_dialog': '新建二级代理',
|
||||
'agent_portal.sub_agent_credit_hint': '初始授信从您的可用额度中划拨,不能超过可用授信',
|
||||
'agent_portal.adjust_credit_dialog': '调整 {name} 授信',
|
||||
@@ -1478,6 +1480,8 @@ export const adminPagesEn: Record<string, string> = {
|
||||
'agent_portal.no_players': 'No direct players yet. Use the button above to create one.',
|
||||
'agent_portal.search_sub_agent_ph': 'Username or ID',
|
||||
'agent_portal.no_sub_agents': 'No tier-2 agents yet. Use the button above to create one.',
|
||||
'agent_portal.no_sub_agents_level': 'No L{level} agents yet. Use the button above to create one.',
|
||||
'agent_portal.sub_agent_players_readonly_level': 'Direct players under this L{level} agent are read-only here. Account opening and top-ups are handled by that agent.',
|
||||
'agent_portal.create_sub_agent_dialog': 'New tier-2 agent',
|
||||
'agent_portal.sub_agent_credit_hint': 'Initial credit is allocated from your available limit',
|
||||
'agent_portal.adjust_credit_dialog': 'Adjust credit for {name}',
|
||||
|
||||
@@ -1452,26 +1452,26 @@ function creditTypeLabel(type: string) {
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="userId" label="ID" width="64" />
|
||||
<el-table-column prop="username" :label="t('user.col.username')" width="100" show-overflow-tooltip />
|
||||
<el-table-column :label="t('common.status')" width="72">
|
||||
<el-table-column prop="userId" label="ID" min-width="64" />
|
||||
<el-table-column prop="username" :label="t('user.col.username')" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column :label="t('common.status')" min-width="72">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusTagType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="level" :label="t('agent.col.level')" width="52" align="center">
|
||||
<el-table-column prop="level" :label="t('agent.col.level')" min-width="52" align="center">
|
||||
<template #default="{ row }">L{{ row.level }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('agent.col.credit')" width="132" align="right">
|
||||
<el-table-column :label="t('agent.col.credit')" min-width="148" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="creditLineFull(row)" placement="top">
|
||||
<span class="amount-compact">{{ creditLine(row) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="directPlayerCount" :label="t('agent.col.direct_players')" width="72" align="center" />
|
||||
<el-table-column prop="childAgentCount" :label="t('agent.col.sub_agents')" width="72" align="center" />
|
||||
<el-table-column :label="t('agent.col.cashback')" width="80" align="right">
|
||||
<el-table-column prop="directPlayerCount" :label="t('agent.col.direct_players')" min-width="72" align="center" />
|
||||
<el-table-column prop="childAgentCount" :label="t('agent.col.sub_agents')" min-width="72" align="center" />
|
||||
<el-table-column :label="t('agent.col.cashback')" min-width="80" align="right">
|
||||
<template #default="{ row }">{{ row.cashbackRate }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.actions')" width="400" fixed="right" align="center">
|
||||
@@ -1595,24 +1595,24 @@ function creditTypeLabel(type: string) {
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userId" label="ID" width="64" />
|
||||
<el-table-column prop="username" :label="t('user.col.username')" width="100" show-overflow-tooltip />
|
||||
<el-table-column :label="t('agent.col.parent_chain')" width="120" show-overflow-tooltip>
|
||||
<el-table-column prop="userId" label="ID" min-width="64" />
|
||||
<el-table-column prop="username" :label="t('user.col.username')" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column :label="t('agent.col.parent_chain')" min-width="120" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ parentChainLabel(row) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.status')" width="72">
|
||||
<el-table-column :label="t('common.status')" min-width="72">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="statusTagType(subAgentAccountStatus(row))" size="small">{{ statusLabel(subAgentAccountStatus(row)) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('agent.col.credit')" width="132" align="right">
|
||||
<el-table-column :label="t('agent.col.credit')" min-width="148" align="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip :content="creditLineFull(row)" placement="top">
|
||||
<span class="amount-compact">{{ creditLine(row) }}</span>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="directPlayerCount" :label="t('agent.col.direct_players')" width="72" align="center" />
|
||||
<el-table-column prop="directPlayerCount" :label="t('agent.col.direct_players')" min-width="72" align="center" />
|
||||
<el-table-column :label="t('common.actions')" width="400" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="action-btns" @click.stop>
|
||||
@@ -2248,7 +2248,9 @@ function creditTypeLabel(type: string) {
|
||||
.expandable-table :deep(.row-expandable) {
|
||||
cursor: pointer;
|
||||
}
|
||||
.compact-agent-table :deep(.el-table__header .el-table__cell),
|
||||
.compact-agent-table :deep(.el-table__header .el-table__cell) {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.compact-agent-table :deep(.el-table__body .el-table__cell) {
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
@@ -42,11 +42,13 @@ import {
|
||||
snapshotFromAgentRow,
|
||||
type AgentCreditAdjustContext,
|
||||
} from '../../utils/agent-credit-context';
|
||||
import { formatAgentLevelNumeral } from '../../utils/agent-level-label';
|
||||
|
||||
const { t, localeTag } = useAdminLocale();
|
||||
const auth = useAuthStore();
|
||||
|
||||
const profile = ref<{
|
||||
level?: number;
|
||||
creditLimit?: string;
|
||||
usedCredit?: string;
|
||||
availableCredit?: string;
|
||||
@@ -57,6 +59,41 @@ const canManageSubAgents = computed(
|
||||
() => profile.value.canManageSubAgents === true || auth.canManageSubAgents.value,
|
||||
);
|
||||
|
||||
const myAgentLevel = computed(() => profile.value.level ?? auth.user.value?.agentLevel ?? 1);
|
||||
const childAgentLevel = computed(() => myAgentLevel.value + 1);
|
||||
|
||||
function lvlLabel(level: number) {
|
||||
return formatAgentLevelNumeral(level, localeTag.value);
|
||||
}
|
||||
|
||||
const childAgentTierName = computed(() =>
|
||||
t('agent.level_name', { level: lvlLabel(childAgentLevel.value) }),
|
||||
);
|
||||
|
||||
const subAgentsTabLabel = computed(() =>
|
||||
`${childAgentTierName.value} (${subAgents.value.length})`,
|
||||
);
|
||||
|
||||
const createSubBtnLabel = computed(() => {
|
||||
if (childAgentLevel.value === 2) return t('agent.create_sub_btn');
|
||||
return t('agent.create_level_agent_btn', { level: lvlLabel(childAgentLevel.value) });
|
||||
});
|
||||
|
||||
const createSubDialogTitle = computed(() => {
|
||||
if (childAgentLevel.value === 2) return t('agent_portal.create_sub_agent_dialog');
|
||||
return t('agent.dialog.create_level_agent', { level: lvlLabel(childAgentLevel.value) });
|
||||
});
|
||||
|
||||
const noSubAgentsHint = computed(() => {
|
||||
if (childAgentLevel.value === 2) return t('agent_portal.no_sub_agents');
|
||||
return t('agent_portal.no_sub_agents_level', { level: lvlLabel(childAgentLevel.value) });
|
||||
});
|
||||
|
||||
const subAgentPlayersReadonlyHint = computed(() => {
|
||||
if (childAgentLevel.value === 2) return t('agent_portal.sub_agent_players_readonly');
|
||||
return t('agent_portal.sub_agent_players_readonly_level', { level: lvlLabel(childAgentLevel.value) });
|
||||
});
|
||||
|
||||
/* ─── Top-level tab: players | subAgents ─── */
|
||||
const activeTab = ref('players');
|
||||
|
||||
@@ -654,7 +691,7 @@ function statusTagType(s: string) {
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- ══════ Tab: 下级代理 (仅一级代理可见) ══════ -->
|
||||
<el-tab-pane v-if="canManageSubAgents" :label="`${t('nav.subAgents')} (${subAgents.length})`" name="subAgents">
|
||||
<el-tab-pane v-if="canManageSubAgents" :label="subAgentsTabLabel" name="subAgents">
|
||||
<div class="inner-toolbar">
|
||||
<el-form inline size="small" style="flex: 1">
|
||||
<el-form-item :label="t('common.search')">
|
||||
@@ -662,7 +699,7 @@ function statusTagType(s: string) {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button type="primary" size="small" @click="openCreateSub">
|
||||
+ {{ t('agent_portal.create_tier2_btn') }}
|
||||
{{ createSubBtnLabel }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@@ -677,7 +714,7 @@ function statusTagType(s: string) {
|
||||
@expand-change="onSubAgentExpand"
|
||||
@row-click="onSubAgentRowClick"
|
||||
>
|
||||
<template #empty><div class="empty-hint">{{ t('agent_portal.no_sub_agents') || '暂无下级代理' }}</div></template>
|
||||
<template #empty><div class="empty-hint">{{ noSubAgentsHint }}</div></template>
|
||||
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
@@ -687,7 +724,7 @@ function statusTagType(s: string) {
|
||||
</div>
|
||||
<template v-else>
|
||||
<div class="expand-section-title">{{ t('nav.players') }} ({{ getSubAgentPlayers(row.userId).length }})</div>
|
||||
<p class="expand-readonly-hint">{{ t('agent_portal.sub_agent_players_readonly') }}</p>
|
||||
<p class="expand-readonly-hint">{{ subAgentPlayersReadonlyHint }}</p>
|
||||
<el-table :data="getSubAgentPlayers(row.userId)" stripe size="small" class="inner-table nested-table">
|
||||
<template #empty><div class="empty-hint">暂无数据</div></template>
|
||||
<el-table-column prop="id" :label="t('common.col_id')" width="60" />
|
||||
@@ -869,7 +906,7 @@ function statusTagType(s: string) {
|
||||
</el-dialog>
|
||||
|
||||
<!-- ── Create Sub-Agent ── -->
|
||||
<el-dialog v-model="createSubVisible" :title="t('agent_portal.create_tier2_btn')" width="480px" destroy-on-close>
|
||||
<el-dialog v-model="createSubVisible" :title="createSubDialogTitle" width="480px" destroy-on-close>
|
||||
<el-form label-width="100px">
|
||||
<el-form-item :label="t('user.col.username')" required>
|
||||
<el-input v-model="createSubForm.username" :placeholder="t('agent_portal.agent_username_ph')" />
|
||||
|
||||
Reference in New Issue
Block a user