feat(admin): 管理端列表分页、控制台图表与赛事导入

- 玩家/代理/赛事/注单/审计列表分页,默认每页 10 条,无页面滚动条布局

- ECharts 控制台概览、注单管理中文化与列宽优化

- zhibo 赛事字段迁移与导入,玩家编辑可改所属代理

- 管理端 API 分页与 dashboard 统计接口

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-03 13:49:31 +08:00
parent 2c356b2048
commit 80adc0e928
45 changed files with 6564 additions and 499 deletions

View File

@@ -1,25 +1,105 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import api from '../../api';
import { formatAmount } from '../../utils/format-amount';
import { betStatusLabel, betStatusTagType, betTypeLabel } from '../../utils/bet-labels';
const bets = ref<unknown[]>([]);
interface BetRow {
id: string;
betNo: string;
betType?: string;
stake: string | number;
status: string;
placedAt?: string;
user?: { username?: string };
}
onMounted(async () => {
const { data } = await api.get('/agent/bets');
bets.value = data.data.items;
});
const bets = ref<BetRow[]>([]);
const total = ref(0);
const page = ref(1);
const pageSize = ref(10);
onMounted(load);
async function load() {
const { data } = await api.get('/agent/bets', {
params: { page: page.value, pageSize: pageSize.value },
});
bets.value = (data.data.items ?? []) as BetRow[];
total.value = data.data.total ?? 0;
}
function onPageChange(p: number) {
page.value = p;
load();
}
function onSizeChange(size: number) {
pageSize.value = size;
page.value = 1;
load();
}
</script>
<template>
<h2>下级注单</h2>
<el-table :data="bets">
<el-table-column prop="betNo" label="注单号" />
<el-table-column label="玩家">
<template #default="{ row }">
{{ (row as { user?: { username: string } }).user?.username }}
</template>
</el-table-column>
<el-table-column prop="stake" label="投注额" />
<el-table-column prop="status" label="状态" />
</el-table>
<div class="admin-list-page">
<div class="page-header">
<h2 class="page-title">注单查询</h2>
<span class="page-desc">下级玩家的全部投注记录</span>
</div>
<el-card class="data-card" shadow="never">
<div class="table-wrap">
<el-table :data="bets" stripe>
<el-table-column prop="id" label="单号" width="56" align="center" />
<el-table-column prop="betNo" label="流水编号" width="168" show-overflow-tooltip>
<template #default="{ row }">
<span class="bet-no">{{ row.betNo }}</span>
</template>
</el-table-column>
<el-table-column label="玩家" min-width="100">
<template #default="{ row }">{{ row.user?.username ?? '—' }}</template>
</el-table-column>
<el-table-column label="类型" width="88" align="center">
<template #default="{ row }">
<el-tag v-if="row.betType" type="info" size="small" effect="plain">
{{ betTypeLabel(row.betType) }}
</el-tag>
<span v-else></span>
</template>
</el-table-column>
<el-table-column label="投注额" width="110" align="right">
<template #default="{ row }">{{ formatAmount(row.stake) }}</template>
</el-table-column>
<el-table-column label="状态" width="96" align="center">
<template #default="{ row }">
<el-tag :type="betStatusTagType(row.status)" size="small">
{{ betStatusLabel(row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
<div class="pager">
<el-pagination
v-model:current-page="page"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
background
@current-change="onPageChange"
@size-change="onSizeChange"
/>
</div>
</el-card>
</div>
</template>
<style scoped>
.page-header { display: flex; align-items: baseline; gap: 12px; }
.page-title { font-size: 20px; font-weight: 700; color: #e0e0e0; }
.page-desc { font-size: 13px; color: #3a3a3a; }
.data-card { border-radius: 12px; }
.bet-no { font-size: 12px; color: #ccc; font-family: ui-monospace, monospace; word-break: break-all; }
</style>