feat(admin,api,player): 优胜赛配置、赛事管理重构与玩家端投注体验优化

管理端拆分赛事/优胜赛 Tab,新增联赛优胜赔率面板(批量、排序、外侧删除);统一 list-chrome 工具栏对齐与列表页布局;Dashboard 失败重试、Users 操作下拉、小屏侧栏等体验修复。

API 扩展优胜赛与赛事目录接口,完善投注与钱包查询;玩家端重构赛事卡片、串关面板、注单/钱包页,新增注单详情、下注成功动画与下拉刷新。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-08 09:55:56 +08:00
parent efff7c27e6
commit 24fa1b275c
66 changed files with 6289 additions and 1426 deletions

View File

@@ -11,6 +11,13 @@ const i18n = createI18n({
fallbackLocale: ['en-US', 'zh-CN'],
messages: {
'zh-CN': {
common: {
pull_refresh: '下拉刷新',
release_refresh: '释放刷新',
refreshing: '刷新中…',
loading_more: '加载更多…',
no_more: '没有更多了',
},
nav: { home: '主页', bet: '投注', bet_history: '历史投注', wallet: '账单', profile: '我的' },
home: {
hot_matches: '热门赛事',
@@ -25,17 +32,39 @@ const i18n = createI18n({
},
history: {
league_default: '足球',
stake: '投注 Stake',
return: '回报 Return',
est_return: '预计回报 Est. Return',
stake: '投注',
return: '回报',
est_return: '预计回报',
odds: '赔率',
ft: '全场',
ht: '半场',
parlay_title: '串关 · {n} 场',
parlay_league: '串关 Parlay',
empty: '暂无投注记录',
no_more: '没有更多记录了',
status_won: 'WON 赢',
status_pending: 'PENDING 待定',
status_lost: 'LOST 输',
status_push: 'PUSH 走盘',
status_won: '赢',
status_pending: '待定',
status_lost: '输',
status_push: '走盘',
back: '返回',
not_found: '注单不存在',
my_pick: '我的选择',
legs: '串关明细',
summary: '投注摘要',
bet_no: '注单号',
awaiting_result: '等待比赛结果…',
filter_all: '全部',
filter_won: '已赢',
filter_lost: '已输',
filter_pending: '待定',
filter_push: '走盘',
stats_total: '总投注',
stats_won: '赢',
stats_lost: '输',
stats_pending: '待定',
stats_push: '走盘',
stats_stake: '总投注额',
stats_return: '总回报',
},
auth: {
login: '登录',
@@ -65,6 +94,13 @@ const i18n = createI18n({
tx_bet_void: '投注撤销',
tx_cashback: '返水发放',
tx_resettle: '重新结算',
stats_income: '收入',
stats_expense: '支出',
stats_net: '净额',
filter_all: '全部',
filter_deposit: '存款',
filter_withdraw: '提款',
filter_bet: '投注',
},
bet: {
bet_slip: '投注单',
@@ -154,6 +190,7 @@ const i18n = createI18n({
cs_confirm_total_stake: '总投注额',
cs_place_success: '下注成功',
cs_place_failed: '下注失败',
kickoff_time: '开赛时间:',
guide_title: '怎么下注?',
guide_help_aria: '查看下注说明',
guide_got_it: '知道了',
@@ -239,6 +276,13 @@ const i18n = createI18n({
},
},
'en-US': {
common: {
pull_refresh: 'Pull to refresh',
release_refresh: 'Release to refresh',
refreshing: 'Refreshing…',
loading_more: 'Loading more…',
no_more: 'No more',
},
nav: { home: 'Home', bet: 'Bet', bet_history: 'History', wallet: 'Wallet', profile: 'Profile' },
home: {
hot_matches: 'Hot matches',
@@ -253,20 +297,42 @@ const i18n = createI18n({
},
history: {
league_default: 'Football',
stake: 'Stake 投注',
return: 'Return 回报',
est_return: 'Est. Return 预计回报',
stake: 'Stake',
return: 'Return',
est_return: 'Est. Return',
odds: 'Odds',
ft: 'FT',
ht: 'HT',
parlay_title: 'Parlay · {n} legs',
parlay_league: 'Parlay 串关',
parlay_league: 'Parlay',
empty: 'No bets yet',
no_more: 'No more bets',
status_won: 'WON',
status_pending: 'PENDING 待定',
status_lost: 'LOST',
status_push: 'PUSH 走盘',
status_won: 'WON',
status_pending: 'PENDING',
status_lost: 'LOST',
status_push: 'PUSH',
back: 'Back',
not_found: 'Bet not found',
my_pick: 'My pick',
legs: 'Parlay legs',
summary: 'Summary',
bet_no: 'Bet ID',
awaiting_result: 'Awaiting result…',
filter_all: 'All',
filter_won: 'Won',
filter_lost: 'Lost',
filter_pending: 'Pending',
filter_push: 'Push',
stats_total: 'Total',
stats_won: 'Won',
stats_lost: 'Lost',
stats_pending: 'Pending',
stats_push: 'Push',
stats_stake: 'Total Stake',
stats_return: 'Total Return',
},
auth: {
login: 'Login',
auth:
{ login: 'Login',
logout: 'Log out',
username: 'Username',
password: 'Password',
@@ -293,6 +359,13 @@ const i18n = createI18n({
tx_bet_void: 'Bet Voided',
tx_cashback: 'Cashback Distribution',
tx_resettle: 'Resettlement',
stats_income: 'Income',
stats_expense: 'Expense',
stats_net: 'Net',
filter_all: 'All',
filter_deposit: 'Deposit',
filter_withdraw: 'Withdraw',
filter_bet: 'Bet',
},
bet: {
bet_slip: 'Bet Slip',
@@ -382,6 +455,7 @@ const i18n = createI18n({
cs_confirm_total_stake: 'Total stake',
cs_place_success: 'Bet placed',
cs_place_failed: 'Bet failed',
kickoff_time: 'Kickoff: ',
guide_title: 'How to bet',
guide_help_aria: 'Betting help',
guide_got_it: 'Got it',
@@ -467,6 +541,13 @@ const i18n = createI18n({
},
},
'ms-MY': {
common: {
pull_refresh: 'Tarik untuk segar',
release_refresh: 'Lepas untuk segar',
refreshing: 'Menyegarkan…',
loading_more: 'Memuat lagi…',
no_more: 'Tiada lagi',
},
nav: {
home: 'Laman Utama',
bet: 'Pertaruhan',
@@ -487,9 +568,12 @@ const i18n = createI18n({
},
history: {
league_default: 'Bola Sepak',
stake: 'Stake',
stake: 'Jumlah',
return: 'Pulangan',
est_return: 'Anggaran pulangan',
odds: 'Odds',
ft: 'PT',
ht: 'SP',
parlay_title: 'Berganda · {n} perlawanan',
parlay_league: 'Berganda',
empty: 'Tiada rekod pertaruhan',
@@ -498,6 +582,25 @@ const i18n = createI18n({
status_pending: 'MENUNGGU',
status_lost: 'KALAH',
status_push: 'SERI',
back: 'Kembali',
not_found: 'Pertaruhan tidak dijumpai',
my_pick: 'Pilihan saya',
legs: 'Butiran berganda',
summary: 'Ringkasan',
bet_no: 'ID Pertaruhan',
awaiting_result: 'Menunggu keputusan…',
filter_all: 'Semua',
filter_won: 'Menang',
filter_lost: 'Kalah',
filter_pending: 'Menunggu',
filter_push: 'Seri',
stats_total: 'Jumlah',
stats_won: 'Menang',
stats_lost: 'Kalah',
stats_pending: 'Menunggu',
stats_push: 'Seri',
stats_stake: 'Jumlah Taruhan',
stats_return: 'Jumlah Pulangan',
},
auth: {
login: 'Log Masuk',
@@ -527,6 +630,13 @@ const i18n = createI18n({
tx_bet_void: 'Pertaruhan Dibatalkan',
tx_cashback: 'Pembayaran Cashback',
tx_resettle: 'Penyelesaian Semula',
stats_income: 'Pendapatan',
stats_expense: 'Perbelanjaan',
stats_net: 'Bersih',
filter_all: 'Semua',
filter_deposit: 'Deposit',
filter_withdraw: 'Pengeluaran',
filter_bet: 'Pertaruhan',
},
bet: {
bet_slip: 'Slip Pertaruhan',
@@ -616,6 +726,7 @@ const i18n = createI18n({
cs_confirm_total_stake: 'Jumlah pertaruhan',
cs_place_success: 'Pertaruhan berjaya',
cs_place_failed: 'Pertaruhan gagal',
kickoff_time: 'Masa mula: ',
guide_title: 'Cara pertaruhan',
guide_help_aria: 'Bantuan pertaruhan',
guide_got_it: 'Faham',
@@ -704,3 +815,10 @@ const i18n = createI18n({
});
createApp(App).use(createPinia()).use(router).use(i18n).mount('#app');
const loader = document.getElementById('app-loading');
if (loader) {
loader.style.opacity = '0';
loader.style.transition = 'opacity 0.3s ease';
setTimeout(() => loader.remove(), 350);
}