feat(player/admin/api): 玩家端暗色极简主题重构与盘口单串关修复

## 管理端 / API(Bug 修复)
- matches.service:getAdminMatchDetail 返回 markets 时补充 allowSingle、allowParlay 字段
- MatchMarketsPanel:mapMarkets 从接口读取单关/串关开关,不再硬编码为 true
- match-form.ts:AdminMarket 类型补充 allowSingle、allowParlay

## 玩家端 — 全局主题(styles.css / index.html / site.webmanifest)
- 海军暗色 + 白色强调 token,卡片高光渐变(--gradient-card)
- 下拉/弹窗实底不透明(--dropdown-bg: #0A2540)
- 统一 sub-toolbar、status-ribbon、filter-tab 等全局样式
- theme-color 同步为 #001A33

## 玩家端 — 布局与壳层
- MainLayout:详情子页去顶栏/公告,sub-toolbar 全宽铺底
- WalletBalanceCard(新):个人页钱包卡片(反水 + 未结算)
- walletStats.ts(新):钱包统计逻辑抽取

## 玩家端 — 赛事 / 投注
- MatchDetailView:顶栏 8px 顶距、卡片全宽(--detail-gutter-x: 0)
- 盘口状态标签(暂停/已关闭)、去掉 MarketTypeTile 右侧箭头
- VsBadge(新):纯文字 VS,删除 vs.png
- isMarketLocked:仅关单关但允许串关时仍可点击,支持串关流程
- BetSlipDrawer:仅串关盘口禁用单关提交并提示 slip_parlay_only_hint
- betSlip:SlipItem 增加 allowSingle 字段
- MatchBetCard / LeagueAccordionItem:45° 待开赛角标,去掉展开左侧白边
- OutrightEventSection:去掉展开时左侧白色选中条
- FootballView:加大左右边距、筛选栏去 sticky、选中态增强
- BannerCarousel:恢复原始全宽轮播

## 玩家端 — 钱包 / 个人 / 认证 / 其他页面
- WalletView:移除顶部钱包卡片,保留账单列表
- ProfileView:保留 WalletBalanceCard
- 充值/账单/注单/登录注册等页面配色与间距统一
- 公告标签、余额面板、语言切换、区号选择等弹层改为实底

## 国际化
- zh-CN / en-US / ms-MY:新增 slip_parlay_only_hint(仅串关盘口提示)

## 资产
- 更新 banner.svg、empty-matches.svg 配色
- 删除 vs.png

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-17 11:00:25 +08:00
parent f34fe54489
commit d3ca8498fb
64 changed files with 2466 additions and 2084 deletions

View File

@@ -233,11 +233,11 @@ onMounted(fetchMethods);
<template>
<div class="recharge-page">
<div class="page-header">
<header class="page-header sub-toolbar">
<button class="back-btn" @click="goBack"></button>
<h2>{{ t('recharge.title') }}</h2>
<button class="history-btn" @click="goHistory">{{ t('recharge.history') }}</button>
</div>
</header>
<div v-if="loading" class="state">
<GoldSpinner :size="36" />
@@ -362,11 +362,10 @@ onMounted(fetchMethods);
</template>
<style scoped>
.recharge-page { padding: 0 12px 24px; }
.recharge-page { padding: 0 0 24px; }
.page-header {
display: flex; align-items: center; justify-content: space-between;
padding: 8px 0 12px;
}
.page-header h2 { margin: 0; font-size: 16px; font-weight: 700; }
.back-btn { background: none; border: none; color: var(--primary-light); font-size: 22px; cursor: pointer; padding: 0 6px; }
@@ -386,18 +385,28 @@ onMounted(fetchMethods);
}
.type-tabs {
display: flex; margin-bottom: 12px;
border-radius: 6px; overflow: hidden;
border: 1px solid rgba(212, 175, 55, 0.2);
display: flex;
margin-bottom: 12px;
border-radius: var(--radius-sm);
overflow: hidden;
border: 1px solid var(--border);
background: var(--bg-body);
}
.tab {
flex: 1; padding: 8px; border: none;
background: rgba(20, 20, 20, 0.8);
color: var(--text-muted); font-weight: 700; font-size: 13px;
cursor: pointer; transition: all 0.2s;
flex: 1;
padding: 8px;
border: none;
background: var(--bg-card);
color: var(--text-muted);
font-weight: 700;
font-size: 13px;
cursor: pointer;
transition: background 0.2s, color 0.2s;
}
.tab.active {
background: var(--primary-light); color: #000;
background: var(--surface-accent);
color: var(--text);
box-shadow: inset 0 0 0 1px var(--primary);
}
.methods-list {
@@ -405,15 +414,15 @@ onMounted(fetchMethods);
}
.method-pill {
display: flex; flex-direction: column; gap: 1px;
background: rgba(20, 20, 20, 0.8);
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 6px; padding: 6px 12px;
text-align: left; cursor: pointer;
transition: border-color 0.15s;
}
.method-pill.selected {
border-color: var(--primary-light);
background: rgba(212, 175, 55, 0.06);
border-color: var(--primary);
background: var(--surface-accent);
}
.pill-name { font-weight: 700; font-size: 12px; color: var(--text); }
.pill-sub { font-size: 10px; color: var(--text-muted); }
@@ -434,13 +443,13 @@ onMounted(fetchMethods);
justify-content: space-between;
overflow: hidden;
background:
radial-gradient(ellipse 80% 60% at 100% 0%, rgba(212, 175, 55, 0.22), transparent 55%),
radial-gradient(ellipse 50% 50% at 0% 100%, rgba(212, 175, 55, 0.1), transparent 50%),
linear-gradient(135deg, #2a2218 0%, #1a1610 35%, #12100c 70%, #0a0908 100%);
border: 1px solid rgba(212, 175, 55, 0.35);
radial-gradient(ellipse 80% 60% at 100% 0%, rgba(255, 255, 255, 0.18), transparent 55%),
radial-gradient(ellipse 50% 50% at 0% 100%, var(--surface-accent), transparent 50%),
var(--gradient-card);
border: 1px solid var(--border-gold-soft);
box-shadow:
0 8px 24px rgba(0, 0, 0, 0.55),
0 2px 8px rgba(212, 175, 55, 0.12),
0 8px 24px rgba(0, 0, 0, 0.35),
0 2px 8px var(--surface-accent),
inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
@@ -461,7 +470,7 @@ onMounted(fetchMethods);
.bank-card-deco {
position: absolute;
border-radius: 50%;
border: 1px solid rgba(212, 175, 55, 0.12);
border: 1px solid var(--border-gold-soft);
pointer-events: none;
}
@@ -470,7 +479,7 @@ onMounted(fetchMethods);
height: 180px;
right: -60px;
bottom: -80px;
background: radial-gradient(circle, rgba(212, 175, 55, 0.08), transparent 70%);
background: radial-gradient(circle, var(--surface-accent), transparent 70%);
}
.bank-card-deco--2 {
@@ -478,7 +487,7 @@ onMounted(fetchMethods);
height: 100px;
right: 40px;
bottom: 20px;
border-color: rgba(212, 175, 55, 0.08);
border-color: rgba(255, 255, 255, 0.25);
}
.bank-card-top,
@@ -531,8 +540,8 @@ onMounted(fetchMethods);
width: 30px;
height: 30px;
border-radius: 8px;
border: 1px solid rgba(212, 175, 55, 0.35);
background: rgba(0, 0, 0, 0.25);
border: 1px solid var(--border-gold-soft);
background: rgba(0, 34, 68, 0.45);
color: var(--primary-light);
display: inline-flex;
align-items: center;
@@ -548,7 +557,7 @@ onMounted(fetchMethods);
.bank-card-copy:active {
opacity: 0.65;
background: rgba(212, 175, 55, 0.12);
background: var(--surface-accent);
}
.bank-card-bottom {
@@ -587,13 +596,14 @@ onMounted(fetchMethods);
font-size: 11px;
font-weight: 800;
letter-spacing: 0.18em;
color: rgba(212, 175, 55, 0.55);
color: var(--primary-light);
font-style: italic;
}
.method-info {
background: rgba(17, 17, 17, 0.9);
border-radius: 8px; padding: 10px 12px;
background: var(--bg-body);
border-radius: var(--radius-sm);
padding: 10px 12px;
margin-bottom: 12px;
border: 1px solid var(--border);
}
@@ -624,19 +634,20 @@ onMounted(fetchMethods);
color: var(--text-muted); margin-bottom: 4px;
}
.amount-input {
width: 100%; padding: 10px; background: #111;
width: 100%; padding: 10px; background: var(--bg-input);
border: 1px solid var(--border); border-radius: 6px;
color: #fff; font-size: 16px; font-weight: 700;
color: var(--text); font-size: 16px; font-weight: 700;
box-sizing: border-box;
}
.amount-input:focus { border-color: var(--primary-light); outline: none; }
.amount-input:focus { border-color: var(--border-gold-soft); outline: none; }
.upload-area {
border: 1px dashed rgba(212, 175, 55, 0.3);
border: 1px dashed var(--border-gold-soft);
border-radius: 6px; padding: 16px;
text-align: center; position: relative;
display: flex; align-items: center; justify-content: center;
cursor: pointer;
background: var(--bg-body);
}
.upload-area input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.upload-hint { font-size: 12px; color: var(--text-muted); }
@@ -652,22 +663,22 @@ onMounted(fetchMethods);
.btn-submit {
width: 100%; padding: 12px;
background: linear-gradient(135deg, #f0d060, #d4a830);
color: #000; border: none; border-radius: 6px;
background: var(--gradient-accent);
color: #fff; border: none; border-radius: 6px;
font-size: 14px; font-weight: 800;
cursor: pointer; margin-top: 8px;
box-shadow: 0 2px 8px rgba(212, 175, 55, 0.2);
box-shadow: none;
}
.btn-submit:disabled { opacity: 0.4; cursor: not-allowed; }
.success-state { text-align: center; padding: 40px 16px; }
.success-icon { font-size: 40px; color: #67c23a; margin-bottom: 10px; }
.success-state h3 { margin: 0 0 6px; font-size: 16px; }
.success-icon { font-size: 40px; color: var(--success); margin-bottom: 10px; }
.success-state h3 { margin: 0 0 6px; font-size: 16px; color: var(--text); }
.order-no { font-family: monospace; color: var(--primary-light); font-size: 13px; margin: 4px 0; }
.success-hint { font-size: 12px; color: var(--text-muted); margin-bottom: 20px; }
.btn-primary {
background: linear-gradient(135deg, #f0d060, #d4a830);
color: #000; border: none; border-radius: 6px;
background: var(--gradient-accent);
color: #fff; border: none; border-radius: 6px;
padding: 10px 20px; font-weight: 700; font-size: 13px; cursor: pointer;
}
</style>