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,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import api from '../api';
import { ElMessage } from 'element-plus';
@@ -22,25 +22,105 @@ async function confirm() {
if (!preview.value?.batch) return;
await api.post(`/admin/settlement/${(preview.value.batch as { id: string }).id}/confirm`);
ElMessage.success('结算已确认');
preview.value = null;
}
</script>
<template>
<h2>赛事结算 #{{ route.params.id }}</h2>
<el-form inline style="margin: 16px 0">
<el-input-number v-model="score.htHome" :min="0" />
<el-input-number v-model="score.htAway" :min="0" />
<span>半场</span>
<el-input-number v-model="score.ftHome" :min="0" />
<el-input-number v-model="score.ftAway" :min="0" />
<span>全场</span>
<el-button @click="recordScore">录入比分</el-button>
<el-button type="primary" @click="previewSettlement">生成预览</el-button>
</el-form>
<el-card v-if="preview">
<p>单关注单: {{ preview.singleBetCount }}</p>
<p>预计派彩: {{ preview.totalPayout }}</p>
<p>退款: {{ preview.totalRefund }}</p>
<el-button type="success" @click="confirm">确认结算</el-button>
<div class="page-header">
<h2 class="page-title">赛事结算</h2>
<span class="page-id"># {{ route.params.id }}</span>
</div>
<el-card class="tool-card" shadow="never">
<div class="score-section">
<div class="score-block">
<div class="score-title">半场比分</div>
<div class="score-inputs">
<el-input-number v-model="score.htHome" :min="0" controls-position="right" style="width: 100px" />
<span class="score-sep"></span>
<el-input-number v-model="score.htAway" :min="0" controls-position="right" style="width: 100px" />
</div>
</div>
<div class="score-block">
<div class="score-title">全场比分</div>
<div class="score-inputs">
<el-input-number v-model="score.ftHome" :min="0" controls-position="right" style="width: 100px" />
<span class="score-sep"></span>
<el-input-number v-model="score.ftAway" :min="0" controls-position="right" style="width: 100px" />
</div>
</div>
</div>
<div class="action-row">
<el-button @click="recordScore">录入比分</el-button>
<el-button type="primary" @click="previewSettlement">生成结算预览</el-button>
</div>
</el-card>
<el-card v-if="preview" class="preview-card" shadow="never">
<div class="preview-title">结算预览</div>
<el-row :gutter="20">
<el-col :span="8">
<div class="pstat">
<div class="pstat-value">{{ preview.singleBetCount }}</div>
<div class="pstat-label">单关注单数</div>
</div>
</el-col>
<el-col :span="8">
<div class="pstat">
<div class="pstat-value pstat-green">{{ preview.totalPayout }}</div>
<div class="pstat-label">预计派彩</div>
</div>
</el-col>
<el-col :span="8">
<div class="pstat">
<div class="pstat-value pstat-orange">{{ preview.totalRefund }}</div>
<div class="pstat-label">退款金额</div>
</div>
</el-col>
</el-row>
<el-button type="success" @click="confirm" style="margin-top: 24px">确认结算</el-button>
</el-card>
</template>
<style scoped>
.page-header { display: flex; align-items: baseline; gap: 12px; margin-bottom: 20px; }
.page-title { font-size: 20px; font-weight: 700; color: #e0e0e0; }
.page-id { font-size: 14px; color: #3a3a3a; font-family: monospace; }
.tool-card { margin-bottom: 16px; border-radius: 12px; }
.preview-card { border-radius: 12px; }
.score-section {
display: flex;
gap: 40px;
margin-bottom: 20px;
}
.score-block { }
.score-title {
font-size: 13px;
color: #3a3a3a;
margin-bottom: 8px;
font-weight: 500;
}
.score-inputs {
display: flex;
align-items: center;
gap: 10px;
}
.score-sep {
font-size: 18px;
color: #ccc;
font-weight: 300;
}
.action-row {
display: flex;
gap: 10px;
}
.preview-title { font-size: 15px; font-weight: 600; color: #e0e0e0; margin-bottom: 16px; }
.pstat { padding: 16px; background: #f9f9fb; border-radius: 10px; text-align: center; }
.pstat-value { font-size: 26px; font-weight: 700; color: #e0e0e0; }
.pstat-green { color: var(--green-glow); text-shadow: 0 0 20px rgba(47, 181, 106, 0.35); }
.pstat-orange { color: #c85a00; }
.pstat-label { font-size: 12px; color: #3a3a3a; margin-top: 4px; }
</style>