feat(player): 完善 H5 投注端与 API 演示数据
- 球赛/串关/优胜冠军、赛事详情、历史投注与个人资料编辑 - 固定顶栏、公告与底栏,仅内容区滚动 - 底部导航与站点 favicon 使用 logo,登录页精简 - API 种子、冠军盘与历史注单增强 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,62 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import api from '../api';
|
||||
import BetHistoryCard, { type BetHistoryItem } from '../components/BetHistoryCard.vue';
|
||||
|
||||
const bets = ref<{ items: Bet[]; total: number }>({ items: [], total: 0 });
|
||||
const { t } = useI18n();
|
||||
|
||||
interface Bet {
|
||||
betNo: string;
|
||||
betType: string;
|
||||
stake: string;
|
||||
totalOdds: string;
|
||||
potentialReturn: string;
|
||||
actualReturn: string;
|
||||
status: string;
|
||||
placedAt: string;
|
||||
selections: Array<{ selectionNameSnapshot: string; odds: string; resultStatus?: string }>;
|
||||
}
|
||||
const bets = ref<{ items: BetHistoryItem[]; total: number }>({ items: [], total: 0 });
|
||||
const loading = ref(true);
|
||||
|
||||
onMounted(load);
|
||||
|
||||
async function load() {
|
||||
const { data } = await api.get('/player/bets');
|
||||
bets.value = data.data;
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await api.get('/player/bets');
|
||||
bets.value = data.data ?? { items: [], total: 0 };
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h2>我的投注</h2>
|
||||
<div v-for="bet in bets.items" :key="bet.betNo" class="card bet-card">
|
||||
<div class="bet-header">
|
||||
<span class="bet-no">{{ bet.betNo }}</span>
|
||||
<span :class="['status', bet.status.toLowerCase()]">{{ bet.status }}</span>
|
||||
</div>
|
||||
<div v-for="(sel, i) in bet.selections" :key="i" class="sel">
|
||||
{{ sel.selectionNameSnapshot }} @ {{ sel.odds }}
|
||||
<span v-if="sel.resultStatus"> → {{ sel.resultStatus }}</span>
|
||||
</div>
|
||||
<div class="bet-footer">
|
||||
<span>{{ bet.betType }} · 投注 {{ bet.stake }}</span>
|
||||
<span v-if="bet.status === 'WON'">返还 {{ bet.actualReturn }}</span>
|
||||
<span v-else-if="bet.status === 'PENDING'">预计 {{ bet.potentialReturn }}</span>
|
||||
</div>
|
||||
<div class="time">{{ new Date(bet.placedAt).toLocaleString() }}</div>
|
||||
</div>
|
||||
<div v-if="!bets.items.length" class="empty">暂无投注</div>
|
||||
<div class="history-page">
|
||||
<h2 class="page-title">{{ t('nav.bet_history') }}</h2>
|
||||
|
||||
<div v-if="loading" class="state">{{ t('bet.loading') }}</div>
|
||||
|
||||
<template v-else-if="bets.items.length">
|
||||
<BetHistoryCard v-for="bet in bets.items" :key="bet.betNo" :bet="bet" />
|
||||
</template>
|
||||
|
||||
<div v-else class="state">{{ t('history.empty') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
h2 { margin-bottom: 16px; }
|
||||
.bet-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
|
||||
.bet-no { font-size: 12px; color: var(--text-muted); }
|
||||
.status { font-size: 12px; font-weight: 600; }
|
||||
.status.pending { color: #ff9800; }
|
||||
.status.won { color: var(--primary); }
|
||||
.status.lost { color: var(--danger); }
|
||||
.sel { font-size: 13px; margin-bottom: 4px; }
|
||||
.bet-footer { font-size: 13px; margin-top: 8px; display: flex; justify-content: space-between; }
|
||||
.time { font-size: 11px; color: var(--text-muted); margin-top: 4px; }
|
||||
.empty { text-align: center; color: var(--text-muted); padding: 40px; }
|
||||
.history-page {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
color: var(--text);
|
||||
margin-bottom: 16px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.state {
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
padding: 56px 20px;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user