初始化足球投注平台 MVP Monorepo
包含 NestJS 后端、三端前端、Prisma 数据模型、结算引擎测试与 PRD 文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
86
apps/player/src/views/ProfileView.vue
Normal file
86
apps/player/src/views/ProfileView.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAuthStore } from '../stores/auth';
|
||||
import api from '../api';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const auth = useAuthStore();
|
||||
const router = useRouter();
|
||||
const profile = ref<{ wallet?: { availableBalance: string; frozenBalance: string } } | null>(null);
|
||||
const transactions = ref<unknown[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
const [prof, txns] = await Promise.all([
|
||||
api.get('/player/profile'),
|
||||
api.get('/player/wallet/transactions'),
|
||||
]);
|
||||
profile.value = prof.data.data;
|
||||
transactions.value = txns.data.data.items;
|
||||
});
|
||||
|
||||
async function changeLocale(code: string) {
|
||||
locale.value = code;
|
||||
localStorage.setItem('locale', code);
|
||||
await api.post('/player/language', { locale: code });
|
||||
}
|
||||
|
||||
function logout() {
|
||||
auth.logout();
|
||||
router.push('/login');
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="card profile-card">
|
||||
<div class="username">{{ auth.user?.username }}</div>
|
||||
<div class="balance-row">
|
||||
<span>{{ t('wallet.balance') }}</span>
|
||||
<span class="amount">{{ profile?.wallet?.availableBalance ?? '0' }}</span>
|
||||
</div>
|
||||
<div class="frozen">冻结: {{ profile?.wallet?.frozenBalance ?? '0' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>语言</h3>
|
||||
<div class="lang-btns">
|
||||
<button @click="changeLocale('zh-CN')" :class="{ active: locale === 'zh-CN' }">中文</button>
|
||||
<button @click="changeLocale('en-US')" :class="{ active: locale === 'en-US' }">English</button>
|
||||
<button @click="changeLocale('ms-MY')" :class="{ active: locale === 'ms-MY' }">BM</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>账变记录</h3>
|
||||
<div v-for="tx in transactions as Array<{ transactionType: string; amount: string; createdAt: string }>" :key="(tx as { transactionId?: string }).transactionId" class="tx-row">
|
||||
<span>{{ tx.transactionType }}</span>
|
||||
<span :class="parseFloat(tx.amount) >= 0 ? 'pos' : 'neg'">{{ tx.amount }}</span>
|
||||
<span class="tx-time">{{ new Date(tx.createdAt).toLocaleString() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn-logout" @click="logout">退出登录</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.profile-card { margin-bottom: 12px; }
|
||||
.username { font-size: 18px; font-weight: 600; margin-bottom: 12px; }
|
||||
.balance-row { display: flex; justify-content: space-between; font-size: 16px; }
|
||||
.amount { color: #ffd700; font-weight: 700; }
|
||||
.frozen { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
|
||||
h3 { font-size: 14px; margin-bottom: 12px; }
|
||||
.lang-btns { display: flex; gap: 8px; }
|
||||
.lang-btns button {
|
||||
flex: 1; padding: 8px; background: var(--bg-hover); color: #fff;
|
||||
border-radius: 6px; border: 1px solid var(--border);
|
||||
}
|
||||
.lang-btns button.active { border-color: var(--primary); color: var(--primary); }
|
||||
.tx-row { display: flex; justify-content: space-between; font-size: 13px; padding: 8px 0; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
|
||||
.pos { color: var(--primary); }
|
||||
.neg { color: var(--danger); }
|
||||
.tx-time { width: 100%; font-size: 11px; color: var(--text-muted); }
|
||||
.btn-logout { width: 100%; margin-top: 16px; padding: 12px; background: var(--bg-card); color: var(--danger); border-radius: 6px; border: 1px solid var(--border); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user