From d3ca8498fbde838348935c26f7f85e0eab316cca Mon Sep 17 00:00:00 2001 From: Mars <3361409208a@gmail.com> Date: Wed, 17 Jun 2026 11:00:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(player/admin/api):=20=E7=8E=A9=E5=AE=B6?= =?UTF-8?q?=E7=AB=AF=E6=9A=97=E8=89=B2=E6=9E=81=E7=AE=80=E4=B8=BB=E9=A2=98?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=B8=8E=E7=9B=98=E5=8F=A3=E5=8D=95=E4=B8=B2?= =?UTF-8?q?=E5=85=B3=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 管理端 / 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 --- apps/admin/src/views/match-form.ts | 2 + .../src/views/matches/MatchMarketsPanel.vue | 4 +- .../src/domains/catalog/matches.service.ts | 2 + apps/player/index.html | 39 +- apps/player/src/assets/images/banner.svg | 10 +- .../src/assets/images/empty-matches.svg | 12 +- apps/player/src/assets/images/vs.png | Bin 114184 -> 0 bytes .../src/components/AnnouncementMarquee.vue | 14 +- .../player/src/components/BackToTopButton.vue | 10 +- apps/player/src/components/BannerCarousel.vue | 75 +-- apps/player/src/components/BetGuideHelp.vue | 8 +- apps/player/src/components/BetHistoryCard.vue | 35 +- apps/player/src/components/BetSlipDrawer.vue | 175 ++++--- apps/player/src/components/BetStatsPanel.vue | 142 ++++-- .../src/components/BetSuccessOverlay.vue | 7 +- .../player/src/components/CashBalanceChip.vue | 46 +- .../src/components/CustomerServiceModal.vue | 22 +- apps/player/src/components/GoldSpinner.vue | 106 ++-- .../src/components/LeagueAccordionItem.vue | 48 +- apps/player/src/components/LocaleFlag.vue | 2 +- apps/player/src/components/LocaleSwitcher.vue | 10 +- .../src/components/LoginPromptModal.vue | 31 +- apps/player/src/components/MatchBetCard.vue | 115 ++--- .../src/components/PhoneCountrySelect.vue | 16 +- .../src/components/PlayerAvatarModal.vue | 8 +- .../src/components/PlayerAvatarPicker.vue | 8 +- apps/player/src/components/RobotVerify.vue | 8 +- .../player/src/components/StatusWatermark.vue | 12 +- apps/player/src/components/UserAvatarMenu.vue | 10 +- apps/player/src/components/VsBadge.vue | 56 +++ .../src/components/WalletBalanceCard.vue | 171 +++++++ .../src/components/WalletStatsPanel.vue | 214 ++++++-- .../match-detail/CorrectScoreConfirmModal.vue | 15 +- .../match-detail/CorrectScorePanel.vue | 15 +- .../match-detail/MarketSelectionsPanel.vue | 17 +- .../match-detail/MarketTypeTile.vue | 76 +-- .../components/outright/OutrightBetModal.vue | 43 +- .../outright/OutrightEventSection.vue | 18 +- .../outright/OutrightOptionCard.vue | 8 +- .../src/components/outright/OutrightPanel.vue | 2 +- apps/player/src/i18n/en-US.ts | 3 + apps/player/src/i18n/ms-MY.ts | 3 + apps/player/src/i18n/zh-CN.ts | 3 + apps/player/src/layouts/MainLayout.vue | 80 +-- apps/player/src/stores/betSlip.ts | 1 + apps/player/src/styles.css | 418 +++++++++------- apps/player/src/utils/walletStats.ts | 18 + apps/player/src/views/BetDetailView.vue | 188 +++---- apps/player/src/views/CashbackRecordsView.vue | 38 +- apps/player/src/views/FootballView.vue | 113 +++-- apps/player/src/views/ForgotPasswordView.vue | 45 +- apps/player/src/views/HomeView.vue | 233 +-------- apps/player/src/views/LoginView.vue | 108 ++-- apps/player/src/views/MatchDetailView.vue | 338 +++++-------- apps/player/src/views/MyBetsView.vue | 37 +- apps/player/src/views/ProfileEditView.vue | 20 +- apps/player/src/views/ProfileView.vue | 379 +++----------- apps/player/src/views/RechargeHistoryView.vue | 132 ++--- apps/player/src/views/RechargeView.vue | 93 ++-- apps/player/src/views/RegisterView.vue | 54 +- apps/player/src/views/WalletDetailView.vue | 100 ++-- .../src/views/WalletTransactionDetailView.vue | 58 +-- apps/player/src/views/WalletView.vue | 472 +++++++++++++++--- packages/shared/public/site.webmanifest | 4 +- 64 files changed, 2466 insertions(+), 2084 deletions(-) delete mode 100644 apps/player/src/assets/images/vs.png create mode 100644 apps/player/src/components/VsBadge.vue create mode 100644 apps/player/src/components/WalletBalanceCard.vue create mode 100644 apps/player/src/utils/walletStats.ts diff --git a/apps/admin/src/views/match-form.ts b/apps/admin/src/views/match-form.ts index 8a5d055..b406955 100644 --- a/apps/admin/src/views/match-form.ts +++ b/apps/admin/src/views/match-form.ts @@ -81,6 +81,8 @@ export interface AdminMarket { lineValue: number | null; paramsJson?: Record | null; status: string; + allowSingle?: boolean; + allowParlay?: boolean; showOnPlayer: boolean; promoLabel: string; promoLabelI18n?: Record; diff --git a/apps/admin/src/views/matches/MatchMarketsPanel.vue b/apps/admin/src/views/matches/MatchMarketsPanel.vue index afa8c0d..1c20c00 100644 --- a/apps/admin/src/views/matches/MatchMarketsPanel.vue +++ b/apps/admin/src/views/matches/MatchMarketsPanel.vue @@ -198,8 +198,8 @@ function mapMarkets(detail: AdminMatchDetail) { lineValue: m.lineValue, paramsJson: m.paramsJson ?? null, status: m.status, - allowSingle: true, - allowParlay: true, + allowSingle: m.allowSingle ?? true, + allowParlay: m.allowParlay ?? true, showOnPlayer: m.showOnPlayer ?? true, sortOrder: m.sortOrder ?? index, promoLabel: m.promoLabel ?? '', diff --git a/apps/api/src/domains/catalog/matches.service.ts b/apps/api/src/domains/catalog/matches.service.ts index 9b24b97..179a8c1 100644 --- a/apps/api/src/domains/catalog/matches.service.ts +++ b/apps/api/src/domains/catalog/matches.service.ts @@ -871,6 +871,8 @@ export class MatchesService { lineValue: m.lineValue != null ? Number(m.lineValue) : null, paramsJson: m.paramsJson ?? null, status: m.status, + allowSingle: m.allowSingle, + allowParlay: m.allowParlay, showOnPlayer: m.showOnPlayer, promoLabel: m.promoLabel ?? '', promoLabelI18n: sanitizeLocalizedText(m.promoLabelI18n), diff --git a/apps/player/index.html b/apps/player/index.html index c799e95..0499639 100644 --- a/apps/player/index.html +++ b/apps/player/index.html @@ -3,13 +3,13 @@ - + TheBet365 diff --git a/apps/player/src/components/BetSuccessOverlay.vue b/apps/player/src/components/BetSuccessOverlay.vue index 8c82f30..3eacebe 100644 --- a/apps/player/src/components/BetSuccessOverlay.vue +++ b/apps/player/src/components/BetSuccessOverlay.vue @@ -16,7 +16,7 @@ interface Particle { color: string; shape: number; } -const COLORS = ['#D4AF37', '#FFD700', '#F0C040', '#C8A02E', '#E6C84C', '#B8960C']; +const COLORS = ['#FFFFFF', '#FFD700', '#F0C040', '#C8A02E', '#E6C84C', '#B8960C']; let w = 0; let h = 0; @@ -111,8 +111,8 @@ onUnmounted(stop);
- - + +

{{ t('bet.place_success') }}

@@ -151,6 +151,7 @@ onUnmounted(stop); .check { width: 72px; height: 72px; + color: var(--primary); } .check-circle { diff --git a/apps/player/src/components/CashBalanceChip.vue b/apps/player/src/components/CashBalanceChip.vue index d104b25..b048b63 100644 --- a/apps/player/src/components/CashBalanceChip.vue +++ b/apps/player/src/components/CashBalanceChip.vue @@ -112,12 +112,12 @@ onUnmounted(() => { align-items: center; gap: 4px; height: 36px; - padding: 0 8px 0 8px; + padding: 0 10px; min-width: 0; cursor: pointer; border: 1px solid var(--border); - border-radius: 6px; - background: #141414; + border-radius: var(--radius-sm); + background: var(--bg-card); box-sizing: border-box; } @@ -130,8 +130,8 @@ onUnmounted(() => { .chip-label { font-size: 8px; - color: var(--text-muted); - font-weight: 600; + color: var(--text-dim); + font-weight: 500; letter-spacing: 0.02em; white-space: nowrap; overflow: hidden; @@ -142,15 +142,16 @@ onUnmounted(() => { .chip-amount { font-size: 11px; - font-weight: 800; - color: var(--primary-light); + font-weight: 600; + color: var(--text); white-space: nowrap; line-height: 1.15; + font-variant-numeric: tabular-nums; } .chevron { font-size: 10px; - color: var(--primary-light); + color: var(--text-muted); transition: transform 0.2s; margin-left: 0; } @@ -166,9 +167,11 @@ onUnmounted(() => { width: min(260px, 78vw); padding: 12px 14px; z-index: 130; - border: 1px solid var(--border); + border: 1px solid var(--border-strong); border-radius: var(--radius-sm); - background: #141414; + background: var(--dropdown-bg); + backdrop-filter: none; + -webkit-backdrop-filter: none; box-shadow: var(--shadow); } @@ -181,8 +184,9 @@ onUnmounted(() => { justify-content: space-between; gap: 12px; font-size: 13px; - font-weight: 600; + font-weight: 500; padding: 6px 0; + font-variant-numeric: tabular-nums; } .panel-row.muted { @@ -190,14 +194,14 @@ onUnmounted(() => { } .panel-row.highlight { - color: var(--primary-light); - font-weight: 800; + color: var(--text); + font-weight: 600; font-size: 14px; } .panel-divider { height: 1px; - background: linear-gradient(90deg, transparent, var(--border-gold-soft), transparent); + background: var(--border); margin: 4px 0; } @@ -205,18 +209,18 @@ onUnmounted(() => { width: 100%; padding: 8px 0; margin-top: 4px; - border-radius: 6px; - border: 1px solid var(--primary, #c8a84e); - background: rgba(200, 168, 78, 0.12); - color: var(--primary-light, #c8a84e); + border-radius: var(--radius-sm); + border: none; + background: var(--primary); + color: var(--on-primary); font-size: 13px; - font-weight: 700; + font-weight: 600; cursor: pointer; - transition: background 0.2s; + transition: opacity 0.15s ease; } .panel-recharge-btn:active { - background: rgba(200, 168, 78, 0.24); + opacity: 0.88; } .backdrop { diff --git a/apps/player/src/components/CustomerServiceModal.vue b/apps/player/src/components/CustomerServiceModal.vue index fb1886d..f00bd7f 100644 --- a/apps/player/src/components/CustomerServiceModal.vue +++ b/apps/player/src/components/CustomerServiceModal.vue @@ -87,11 +87,11 @@ function close() { flex-direction: column; width: min(100%, 420px); height: min(82vh, 680px); - background: #141414; - border: 1px solid var(--border-gold-soft, rgba(200, 168, 78, 0.25)); - border-radius: 12px; + background: var(--bg-card); + border: 1px solid var(--border-gold-soft); + border-radius: var(--radius); overflow: hidden; - box-shadow: 0 16px 48px rgba(0, 0, 0, 0.55); + box-shadow: var(--shadow); } .cs-header { @@ -101,21 +101,21 @@ function close() { justify-content: space-between; gap: 12px; padding: 14px 16px; - border-bottom: 1px solid var(--border, #2a2a2a); - background: rgba(26, 26, 26, 0.98); + border-bottom: 1px solid var(--border); + background: var(--bg-elevated); } .cs-title { margin: 0; font-size: 16px; font-weight: 800; - color: var(--primary-light, #c8a84e); + color: var(--primary-light); } .close-btn { background: none; border: none; - color: #666; + color: var(--text-muted); font-size: 18px; cursor: pointer; padding: 4px; @@ -123,13 +123,13 @@ function close() { } .close-btn:hover { - color: #aaa; + color: var(--text); } .cs-body { flex: 1; min-height: 0; - background: #0d0d0d; + background: var(--bg-body); } .cs-frame { @@ -150,7 +150,7 @@ function close() { text-align: center; font-size: 14px; line-height: 1.6; - color: #888; + color: var(--text-muted); } .fade-enter-active, diff --git a/apps/player/src/components/GoldSpinner.vue b/apps/player/src/components/GoldSpinner.vue index cf6186e..29cb828 100644 --- a/apps/player/src/components/GoldSpinner.vue +++ b/apps/player/src/components/GoldSpinner.vue @@ -1,102 +1,114 @@ + + + + + diff --git a/apps/player/src/components/LeagueAccordionItem.vue b/apps/player/src/components/LeagueAccordionItem.vue index a3e1526..909bb94 100644 --- a/apps/player/src/components/LeagueAccordionItem.vue +++ b/apps/player/src/components/LeagueAccordionItem.vue @@ -49,7 +49,7 @@ const openCount = computed(() => props.matches.filter(m => m.matchPhase === 'ope