From ecfea51545f98b541a4cdcd981e18cf3512db046 Mon Sep 17 00:00:00 2001 From: mars <3361409208@qq.com> Date: Thu, 9 Jul 2026 18:03:57 +0800 Subject: [PATCH] feat(theme-4): fix dark backgrounds, quick bet, inbox spacing, customer service theme --- .../applications/player/player.controller.ts | 4 +- .../src/domains/catalog/matches.service.ts | 7 +- apps/player/src/App.vue | 2 + apps/player/src/components/AppToast.vue | 47 + .../player/src/components/CashBalanceChip.vue | 6 +- .../src/components/CustomerServicePanel.vue | 3 +- .../player/src/components/FloatingMailbox.vue | 471 ++++ .../src/components/HomeAnnouncementCard.vue | 250 ++ apps/player/src/components/MatchBetCard.vue | 407 +++- .../src/components/MessageListPanel.vue | 8 +- .../player/src/components/MouseRippleGrid.vue | 284 +++ apps/player/src/components/TeamEmblem.vue | 16 +- .../src/components/desktop/AccountSideNav.vue | 121 + .../src/components/desktop/BetSlipPanel.vue | 2033 +++++++++++++++++ .../src/components/desktop/DataTable.vue | 123 + .../components/desktop/DesktopAuthLayout.vue | 233 ++ .../desktop/DesktopBanner3DCarousel.vue | 471 ++++ .../components/desktop/DesktopBetSlipRail.vue | 163 ++ .../desktop/DesktopOddsBetPopover.vue | 462 ++++ .../desktop/DesktopOutrightEventCard.vue | 330 +++ .../src/components/desktop/DesktopShell.vue | 169 ++ .../src/components/desktop/DesktopTopNav.vue | 243 ++ .../desktop/DesktopWalletSubNav.vue | 46 + .../src/components/desktop/LeagueSidebar.vue | 363 +++ .../src/components/desktop/MatchSidebar.vue | 343 +++ .../components/desktop/OutrightSidebar.vue | 265 +++ .../src/components/desktop/Pagination.vue | 172 ++ .../components/desktop/SportsCategoryBar.vue | 108 + .../match-detail/CorrectScorePanel.vue | 82 +- .../match-detail/MarketSelectionsPanel.vue | 152 +- apps/player/src/composables/useAppToast.ts | 23 + .../src/composables/useDesktopBetLocate.ts | 42 + .../src/composables/useDesktopBetPopover.ts | 101 + .../composables/useDesktopParlayMatches.ts | 67 + .../src/composables/useFloatingMailbox.ts | 24 + .../src/composables/useHoverDropdown.ts | 42 + .../player/src/composables/useMatchFilters.ts | 33 + .../src/composables/useOutrightEvents.ts | 68 + apps/player/src/composables/usePlayerHome.ts | 90 +- apps/player/src/composables/useViewport.ts | 28 + apps/player/src/i18n/en-US.ts | 139 +- apps/player/src/i18n/ms-MY.ts | 145 +- apps/player/src/i18n/zh-CN.ts | 143 +- apps/player/src/layouts/MainLayout.vue | 16 +- apps/player/src/main.ts | 6 + apps/player/src/router/index.ts | 31 +- apps/player/src/stores/betSlip.ts | 266 ++- apps/player/src/styles.css | 3 +- apps/player/src/styles/desktop/index.css | 4 + .../player/src/styles/desktop/interaction.css | 102 + apps/player/src/styles/desktop/layout.css | 200 ++ apps/player/src/styles/desktop/records.css | 312 +++ apps/player/src/styles/desktop/tokens.css | 46 + .../src/utils/betPlaceConfirmMessage.ts | 49 + .../src/views/AnnouncementDetailView.vue | 319 +-- .../player/src/views/AnnouncementListView.vue | 179 +- .../player/src/views/AnnouncementsHubView.vue | 13 + apps/player/src/views/BetDetailView.vue | 642 +----- apps/player/src/views/CashbackRecordsView.vue | 413 +--- apps/player/src/views/FootballView.vue | 666 +----- apps/player/src/views/ForgotPasswordView.vue | 306 ++- .../src/views/ForgotPasswordWrapperView.vue | 10 + apps/player/src/views/HomeView.vue | 450 +--- apps/player/src/views/InboxHubView.vue | 295 +-- apps/player/src/views/LoginView.vue | 374 ++- apps/player/src/views/LoginWrapperView.vue | 10 + apps/player/src/views/MatchDetailView.vue | 872 +------ apps/player/src/views/MessageDetailView.vue | 391 +--- apps/player/src/views/MessagesHubView.vue | 13 + .../views/MobileAnnouncementDetailView.vue | 320 +++ .../src/views/MobileAnnouncementListView.vue | 178 ++ apps/player/src/views/MobileBetDetailView.vue | 640 ++++++ .../src/views/MobileCashbackRecordsView.vue | 411 ++++ apps/player/src/views/MobileFootballView.vue | 666 ++++++ apps/player/src/views/MobileHomeView.vue | 450 ++++ apps/player/src/views/MobileInboxHubView.vue | 297 +++ .../src/views/MobileMatchDetailView.vue | 872 +++++++ .../src/views/MobileMessageDetailView.vue | 393 ++++ apps/player/src/views/MobileMyBetsView.vue | 238 ++ .../src/views/MobileOutrightDetailView.vue | 166 ++ .../src/views/MobileProfileEditView.vue | 564 +++++ apps/player/src/views/MobileProfileView.vue | 381 +++ .../src/views/MobileRechargeDetailView.vue | 343 +++ .../src/views/MobileRechargeHistoryView.vue | 847 +++++++ apps/player/src/views/MobileRechargeView.vue | 701 ++++++ .../src/views/MobileWalletDetailView.vue | 391 ++++ .../MobileWalletTransactionDetailView.vue | 388 ++++ apps/player/src/views/MobileWalletView.vue | 588 +++++ apps/player/src/views/MyBetsView.vue | 240 +- apps/player/src/views/OutrightDetailView.vue | 12 + apps/player/src/views/ProfileEditView.vue | 566 +---- apps/player/src/views/ProfileView.vue | 383 +--- apps/player/src/views/RechargeDetailView.vue | 12 + apps/player/src/views/RechargeHistoryView.vue | 849 +------ apps/player/src/views/RechargeView.vue | 703 +----- apps/player/src/views/RegisterView.vue | 410 +++- apps/player/src/views/RegisterWrapperView.vue | 10 + apps/player/src/views/WalletDetailView.vue | 393 +--- .../src/views/WalletTransactionDetailView.vue | 390 +--- apps/player/src/views/WalletView.vue | 590 +---- .../desktop/DesktopAnnouncementDetailView.vue | 31 + .../desktop/DesktopAnnouncementListView.vue | 327 +++ .../desktop/DesktopAnnouncementsHubShell.vue | 44 + .../views/desktop/DesktopBetDetailView.vue | 215 ++ .../desktop/DesktopCashbackRecordsView.vue | 183 ++ .../src/views/desktop/DesktopFootballView.vue | 465 ++++ .../desktop/DesktopForgotPasswordView.vue | 10 + .../src/views/desktop/DesktopHomeView.vue | 1593 +++++++++++++ .../src/views/desktop/DesktopInboxHubView.vue | 265 +++ .../src/views/desktop/DesktopLoginView.vue | 10 + .../views/desktop/DesktopMatchDetailView.vue | 839 +++++++ .../views/desktop/DesktopMessagesHubShell.vue | 182 ++ .../src/views/desktop/DesktopMyBetsView.vue | 198 ++ .../desktop/DesktopOutrightDetailView.vue | 334 +++ .../views/desktop/DesktopProfileEditView.vue | 395 ++++ .../src/views/desktop/DesktopProfileView.vue | 439 ++++ .../desktop/DesktopRechargeDetailView.vue | 429 ++++ .../desktop/DesktopRechargeHistoryView.vue | 143 ++ .../src/views/desktop/DesktopRechargeView.vue | 912 ++++++++ .../src/views/desktop/DesktopRegisterView.vue | 10 + .../views/desktop/DesktopWalletDetailView.vue | 192 ++ .../DesktopWalletTransactionDetailView.vue | 201 ++ .../src/views/desktop/DesktopWalletView.vue | 154 ++ packages/shared/public/tawk.html | 8 +- 124 files changed, 26710 insertions(+), 8578 deletions(-) create mode 100644 apps/player/src/components/AppToast.vue create mode 100644 apps/player/src/components/FloatingMailbox.vue create mode 100644 apps/player/src/components/HomeAnnouncementCard.vue create mode 100644 apps/player/src/components/MouseRippleGrid.vue create mode 100644 apps/player/src/components/desktop/AccountSideNav.vue create mode 100644 apps/player/src/components/desktop/BetSlipPanel.vue create mode 100644 apps/player/src/components/desktop/DataTable.vue create mode 100644 apps/player/src/components/desktop/DesktopAuthLayout.vue create mode 100644 apps/player/src/components/desktop/DesktopBanner3DCarousel.vue create mode 100644 apps/player/src/components/desktop/DesktopBetSlipRail.vue create mode 100644 apps/player/src/components/desktop/DesktopOddsBetPopover.vue create mode 100644 apps/player/src/components/desktop/DesktopOutrightEventCard.vue create mode 100644 apps/player/src/components/desktop/DesktopShell.vue create mode 100644 apps/player/src/components/desktop/DesktopTopNav.vue create mode 100644 apps/player/src/components/desktop/DesktopWalletSubNav.vue create mode 100644 apps/player/src/components/desktop/LeagueSidebar.vue create mode 100644 apps/player/src/components/desktop/MatchSidebar.vue create mode 100644 apps/player/src/components/desktop/OutrightSidebar.vue create mode 100644 apps/player/src/components/desktop/Pagination.vue create mode 100644 apps/player/src/components/desktop/SportsCategoryBar.vue create mode 100644 apps/player/src/composables/useAppToast.ts create mode 100644 apps/player/src/composables/useDesktopBetLocate.ts create mode 100644 apps/player/src/composables/useDesktopBetPopover.ts create mode 100644 apps/player/src/composables/useDesktopParlayMatches.ts create mode 100644 apps/player/src/composables/useFloatingMailbox.ts create mode 100644 apps/player/src/composables/useHoverDropdown.ts create mode 100644 apps/player/src/composables/useMatchFilters.ts create mode 100644 apps/player/src/composables/useOutrightEvents.ts create mode 100644 apps/player/src/composables/useViewport.ts create mode 100644 apps/player/src/styles/desktop/index.css create mode 100644 apps/player/src/styles/desktop/interaction.css create mode 100644 apps/player/src/styles/desktop/layout.css create mode 100644 apps/player/src/styles/desktop/records.css create mode 100644 apps/player/src/styles/desktop/tokens.css create mode 100644 apps/player/src/utils/betPlaceConfirmMessage.ts create mode 100644 apps/player/src/views/AnnouncementsHubView.vue create mode 100644 apps/player/src/views/ForgotPasswordWrapperView.vue create mode 100644 apps/player/src/views/LoginWrapperView.vue create mode 100644 apps/player/src/views/MessagesHubView.vue create mode 100644 apps/player/src/views/MobileAnnouncementDetailView.vue create mode 100644 apps/player/src/views/MobileAnnouncementListView.vue create mode 100644 apps/player/src/views/MobileBetDetailView.vue create mode 100644 apps/player/src/views/MobileCashbackRecordsView.vue create mode 100644 apps/player/src/views/MobileFootballView.vue create mode 100644 apps/player/src/views/MobileHomeView.vue create mode 100644 apps/player/src/views/MobileInboxHubView.vue create mode 100644 apps/player/src/views/MobileMatchDetailView.vue create mode 100644 apps/player/src/views/MobileMessageDetailView.vue create mode 100644 apps/player/src/views/MobileMyBetsView.vue create mode 100644 apps/player/src/views/MobileOutrightDetailView.vue create mode 100644 apps/player/src/views/MobileProfileEditView.vue create mode 100644 apps/player/src/views/MobileProfileView.vue create mode 100644 apps/player/src/views/MobileRechargeDetailView.vue create mode 100644 apps/player/src/views/MobileRechargeHistoryView.vue create mode 100644 apps/player/src/views/MobileRechargeView.vue create mode 100644 apps/player/src/views/MobileWalletDetailView.vue create mode 100644 apps/player/src/views/MobileWalletTransactionDetailView.vue create mode 100644 apps/player/src/views/MobileWalletView.vue create mode 100644 apps/player/src/views/OutrightDetailView.vue create mode 100644 apps/player/src/views/RechargeDetailView.vue create mode 100644 apps/player/src/views/RegisterWrapperView.vue create mode 100644 apps/player/src/views/desktop/DesktopAnnouncementDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopAnnouncementListView.vue create mode 100644 apps/player/src/views/desktop/DesktopAnnouncementsHubShell.vue create mode 100644 apps/player/src/views/desktop/DesktopBetDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopCashbackRecordsView.vue create mode 100644 apps/player/src/views/desktop/DesktopFootballView.vue create mode 100644 apps/player/src/views/desktop/DesktopForgotPasswordView.vue create mode 100644 apps/player/src/views/desktop/DesktopHomeView.vue create mode 100644 apps/player/src/views/desktop/DesktopInboxHubView.vue create mode 100644 apps/player/src/views/desktop/DesktopLoginView.vue create mode 100644 apps/player/src/views/desktop/DesktopMatchDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopMessagesHubShell.vue create mode 100644 apps/player/src/views/desktop/DesktopMyBetsView.vue create mode 100644 apps/player/src/views/desktop/DesktopOutrightDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopProfileEditView.vue create mode 100644 apps/player/src/views/desktop/DesktopProfileView.vue create mode 100644 apps/player/src/views/desktop/DesktopRechargeDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopRechargeHistoryView.vue create mode 100644 apps/player/src/views/desktop/DesktopRechargeView.vue create mode 100644 apps/player/src/views/desktop/DesktopRegisterView.vue create mode 100644 apps/player/src/views/desktop/DesktopWalletDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopWalletTransactionDetailView.vue create mode 100644 apps/player/src/views/desktop/DesktopWalletView.vue diff --git a/apps/api/src/applications/player/player.controller.ts b/apps/api/src/applications/player/player.controller.ts index f2f52c4..0a20eb0 100644 --- a/apps/api/src/applications/player/player.controller.ts +++ b/apps/api/src/applications/player/player.controller.ts @@ -189,8 +189,8 @@ export class PlayerController { const [banners, announcements, allMatches, upcomingMatches, inboxEnabled] = await Promise.all([ this.content.listActive('BANNER', locale), this.content.listActiveAnnouncements(locale), - this.matches.listPublished(locale, undefined, { includeMarkets: false }), - this.matches.listUpcomingPublished(locale), + this.matches.listPublished(locale, undefined, { includeMarkets: true }), + this.matches.listUpcomingPublished(locale, { includeMarkets: true }), this.systemConfig.getInboxFeatureEnabled(), ]); const timeZone = safeTimeZone(headerTimeZone); diff --git a/apps/api/src/domains/catalog/matches.service.ts b/apps/api/src/domains/catalog/matches.service.ts index e0139e1..e048cfb 100644 --- a/apps/api/src/domains/catalog/matches.service.ts +++ b/apps/api/src/domains/catalog/matches.service.ts @@ -1595,10 +1595,11 @@ export class MatchesService { /** 未来 N 天内开赛的已发布赛事(按开赛时间升序,不限 isHot) */ async listUpcomingPublished( locale = 'en-US', - options?: { limit?: number; days?: number }, + options?: { limit?: number; days?: number; includeMarkets?: boolean }, ) { const limit = options?.limit ?? 50; const days = options?.days ?? 3; + const includeMarkets = options?.includeMarkets ?? false; const now = new Date(); const end = new Date(now); end.setDate(end.getDate() + days); @@ -1625,14 +1626,14 @@ export class MatchesService { homeTeam: true, awayTeam: true, score: true, - markets: this.playerMarketStatusInclude, + markets: includeMarkets ? this.playerMarketInclude : this.playerMarketStatusInclude, }, orderBy: [{ startTime: 'asc' }, { displayOrder: 'asc' }], take: limit, }); return Promise.all( - matches.map((m) => this.enrichMatch(m, locale, { omitMarkets: true })), + matches.map((m) => this.enrichMatch(m, locale, { omitMarkets: !includeMarkets })), ); } diff --git a/apps/player/src/App.vue b/apps/player/src/App.vue index f4ac569..4acb453 100644 --- a/apps/player/src/App.vue +++ b/apps/player/src/App.vue @@ -1,8 +1,10 @@ diff --git a/apps/player/src/components/AppToast.vue b/apps/player/src/components/AppToast.vue new file mode 100644 index 0000000..6b68d8c --- /dev/null +++ b/apps/player/src/components/AppToast.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/apps/player/src/components/CashBalanceChip.vue b/apps/player/src/components/CashBalanceChip.vue index 4951456..e37a983 100644 --- a/apps/player/src/components/CashBalanceChip.vue +++ b/apps/player/src/components/CashBalanceChip.vue @@ -7,6 +7,8 @@ import { useAuthStore } from '../stores/auth'; import { formatMoney } from '../utils/localeDisplay'; import { usePlayerProfile } from '../composables/usePlayerProfile'; +const props = withDefaults(defineProps<{ hideAvatar?: boolean }>(), { hideAvatar: false }); + const { locale, t } = useI18n(); const { profileRaw, refreshProfile, avatarUrl } = usePlayerProfile(); const auth = useAuthStore(); @@ -84,8 +86,8 @@ onUnmounted(() => { {{ available }} - - + + diff --git a/apps/player/src/components/CustomerServicePanel.vue b/apps/player/src/components/CustomerServicePanel.vue index 93404df..b3c2701 100644 --- a/apps/player/src/components/CustomerServicePanel.vue +++ b/apps/player/src/components/CustomerServicePanel.vue @@ -46,7 +46,6 @@ const iframeSrc = computed(() => { min-height: 0; display: flex; flex-direction: column; - margin: 0 -16px; background: var(--bg-body); } @@ -55,6 +54,6 @@ const iframeSrc = computed(() => { width: 100%; min-height: calc(100dvh - 140px); border: 0; - background: #fff; + background: var(--bg-body); } diff --git a/apps/player/src/components/FloatingMailbox.vue b/apps/player/src/components/FloatingMailbox.vue new file mode 100644 index 0000000..f5d0b6c --- /dev/null +++ b/apps/player/src/components/FloatingMailbox.vue @@ -0,0 +1,471 @@ + + + + + diff --git a/apps/player/src/components/HomeAnnouncementCard.vue b/apps/player/src/components/HomeAnnouncementCard.vue new file mode 100644 index 0000000..650816a --- /dev/null +++ b/apps/player/src/components/HomeAnnouncementCard.vue @@ -0,0 +1,250 @@ + + + + + diff --git a/apps/player/src/components/MatchBetCard.vue b/apps/player/src/components/MatchBetCard.vue index c66cc72..828e5a3 100644 --- a/apps/player/src/components/MatchBetCard.vue +++ b/apps/player/src/components/MatchBetCard.vue @@ -1,10 +1,14 @@ @@ -132,7 +290,7 @@ const liveScoreText = computed(() => { .match-card { position: relative; background: var(--gradient-card); - border: 1px solid rgba(255, 255, 255, 0.11); + border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 12px 10px 10px; display: flex; @@ -140,6 +298,7 @@ const liveScoreText = computed(() => { align-items: center; gap: 10px; min-width: 0; + min-height: 130px; overflow: hidden; cursor: pointer; box-shadow: var(--shadow-card-sm); @@ -173,6 +332,7 @@ const liveScoreText = computed(() => { align-items: center; width: 100%; gap: 4px; + transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), padding 0.3s ease; } .team { @@ -183,6 +343,7 @@ const liveScoreText = computed(() => { gap: 6px; min-width: 0; transform: translateY(8px); + transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); } .team-name { @@ -199,6 +360,7 @@ const liveScoreText = computed(() => { padding: 0 6px; align-self: stretch; letter-spacing: -0.02em; + transition: font-size 0.25s ease; } .match-card--phase .team-name { @@ -211,12 +373,14 @@ const liveScoreText = computed(() => { object-fit: cover; border-radius: 3px; display: block; + transition: transform 0.25s ease, opacity 0.2s ease, height 0.25s ease, width 0.25s ease, margin 0.25s ease; } .team-flag.flag-logo { width: 48px; height: 48px; object-fit: contain; + transition: transform 0.25s ease, opacity 0.2s ease, height 0.25s ease, width 0.25s ease, margin 0.25s ease; } .center-col { @@ -226,7 +390,7 @@ const liveScoreText = computed(() => { align-items: center; justify-content: center; gap: 5px; - min-width: 72px; + min-width: 56px; } .kickoff { @@ -238,6 +402,7 @@ const liveScoreText = computed(() => { text-align: center; max-width: 96px; overflow-wrap: anywhere; + transition: opacity 0.2s ease, height 0.2s ease, margin 0.2s ease; } .live-score { @@ -246,35 +411,43 @@ const liveScoreText = computed(() => { color: var(--text); line-height: 1; letter-spacing: 0.02em; + transition: font-size 0.25s ease; +} + +.vs { + font-size: 22px; + font-weight: 900; + color: var(--text); + letter-spacing: 0.08em; + line-height: 1; + transition: font-size 0.25s ease, transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } .status-tag { position: absolute; top: 0; - left: 0; - right: auto; + right: 0; z-index: 3; font-size: 10px; - font-weight: 600; - padding: 4px 10px 4px 8px; - border-radius: 0 0 10px 0; + font-weight: 700; + padding: 3px 10px; + border-radius: 0 6px 0 8px; line-height: 1.3; white-space: nowrap; - letter-spacing: 0; + letter-spacing: 0.02em; +} + +.status-tag--open { + background: rgba(255, 255, 255, 0.12); + color: var(--primary-light); + border: 1px solid var(--border-gold-soft); } .status-tag--settled { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, rgba(0, 20, 40, 0.7) 100%); + background: rgba(255, 255, 255, 0.06); color: var(--text-muted); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - border-right: 1px solid rgba(255, 255, 255, 0.1); -} - -.status-tag--pending { - background: linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, rgba(10, 53, 88, 0.55) 100%); - color: var(--text-muted); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - border-right: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid var(--border); + border-left: 1px solid var(--border); } .bet-btn { @@ -287,6 +460,8 @@ const liveScoreText = computed(() => { font-size: 13px; letter-spacing: 0.04em; line-height: 1.2; + overflow: hidden; + transition: opacity 0.2s ease, transform 0.2s ease, height 0.25s ease, padding 0.25s ease, min-width 0.25s ease, min-height 0.25s ease, margin 0.25s ease; } .bet-btn--view { @@ -294,4 +469,184 @@ const liveScoreText = computed(() => { border: 1px solid var(--border); color: var(--text-muted); } + +/* --- Morphing Quick Bet panel --- */ +.card-quick-bet { + position: absolute; + left: 8px; + right: 8px; + bottom: 8px; + display: flex; + flex-direction: column; + gap: 4px; + opacity: 0; + pointer-events: none; + transform: translateY(12px); + transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); + z-index: 5; +} + +@media (hover: hover) { + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) { + border-color: var(--border-gold); + box-shadow: var(--shadow-sm); + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .teams-row { + transform: translateY(0); + padding: 0 40px; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .team { + transform: translateY(0); + gap: 3px; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .team-flag { + width: 40px; + height: 28px; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .team-flag.flag-logo { + width: 28px; + height: 28px; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .team-name { + font-size: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .kickoff { + opacity: 0; + height: 0; + margin: 0; + overflow: hidden; + pointer-events: none; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .vs, + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .live-score { + font-size: 16px; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .bet-btn { + opacity: 0; + pointer-events: none; + } + + .match-card:not(.no-quick-bet):is(:hover, .match-card--engaged) .card-quick-bet { + opacity: 1; + pointer-events: auto; + transform: translateY(0); + } + + .match-card.no-quick-bet:hover { + border-color: var(--border-gold); + box-shadow: var(--shadow-sm); + } +} + +.quick-bet-tabs { + display: flex; + align-items: center; + border-bottom: 1px solid var(--border); + padding-bottom: 4px; + margin-bottom: 2px; +} + +.quick-bet-tab-btn { + padding: 2px 6px; + font-size: 9px; + font-weight: 700; + color: var(--text-muted); + background: transparent; + border: none; + cursor: pointer; + border-radius: 3px; + transition: all 0.15s; +} + +.quick-bet-tab-btn:hover { + color: var(--primary-light); +} + +.quick-bet-tab-btn.active { + color: var(--primary-light); + background: var(--border-gold-soft); +} + +.quick-bet-all-link { + margin-left: auto; + font-size: 9px; + font-weight: 700; + color: var(--primary-light); + cursor: pointer; + padding: 2px 4px; + transition: opacity 0.15s; +} + +.quick-bet-all-link:hover { + opacity: 0.7; +} + +.quick-bet-odds { + display: flex; + align-items: center; + justify-content: center; + width: 100%; +} + +.quick-bet-odds :deep(.odds-btn) { + min-height: 26px !important; + padding: 1px 4px !important; + gap: 1px !important; +} + +.quick-bet-odds :deep(.odds) { + font-size: 11px !important; +} + +.quick-bet-odds :deep(.label) { + font-size: 8px !important; +} + +.quick-bet-no-odds { + font-size: 10px; + color: var(--text-muted); +} + +/* --- VS Selection Highlight: scale selected team --- */ +.teams-row .team { + transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease, filter 0.3s ease; +} +.teams-row.home .team:first-child { + transform: scale(1.08); + filter: brightness(1.3) saturate(1.2); + z-index: 2; +} +.teams-row.home .team:last-child { + opacity: 0.35 !important; + transform: scale(0.94); + filter: grayscale(0.6) brightness(0.7); +} + +.teams-row.away .team:last-child { + transform: scale(1.08); + filter: brightness(1.3) saturate(1.2); + z-index: 2; +} +.teams-row.away .team:first-child { + opacity: 0.35 !important; + transform: scale(0.94); + filter: grayscale(0.6) brightness(0.7); +} + +.teams-row.draw .team { + transform: scale(1.05); + filter: brightness(1.15) saturate(1.1); +} diff --git a/apps/player/src/components/MessageListPanel.vue b/apps/player/src/components/MessageListPanel.vue index 0fca05b..c84cf4d 100644 --- a/apps/player/src/components/MessageListPanel.vue +++ b/apps/player/src/components/MessageListPanel.vue @@ -251,7 +251,7 @@ onActivated(tryLoad); flex: 1; min-width: 0; font-size: 15px; - color: #d8d8d8; + color: var(--text-muted); line-height: 1.35; overflow: hidden; text-overflow: ellipsis; @@ -264,7 +264,7 @@ onActivated(tryLoad); border-radius: 4px; font-size: 10px; font-weight: 600; - color: #888; + color: var(--text-muted); background: rgba(255, 255, 255, 0.06); } @@ -283,7 +283,7 @@ onActivated(tryLoad); .date { font-size: 12px; - color: #777; + color: var(--text-muted); } .chevron { @@ -301,7 +301,7 @@ onActivated(tryLoad); border: none; border-radius: 8px; background: transparent; - color: #666; + color: var(--text-muted); cursor: pointer; } diff --git a/apps/player/src/components/MouseRippleGrid.vue b/apps/player/src/components/MouseRippleGrid.vue new file mode 100644 index 0000000..9a6990f --- /dev/null +++ b/apps/player/src/components/MouseRippleGrid.vue @@ -0,0 +1,284 @@ + + + + + diff --git a/apps/player/src/components/TeamEmblem.vue b/apps/player/src/components/TeamEmblem.vue index 06d5b70..d7b8690 100644 --- a/apps/player/src/components/TeamEmblem.vue +++ b/apps/player/src/components/TeamEmblem.vue @@ -7,7 +7,7 @@ const props = withDefaults( teamCode?: string; teamName?: string; logoUrl?: string | null; - size?: 'sm' | 'md' | 'lg'; + size?: 'sm' | 'md' | 'lg' | 'xl'; }>(), { size: 'md' }, ); @@ -80,6 +80,11 @@ watch( height: 52px; } +.team-emblem--xl { + width: 72px; + height: 72px; +} + /* 国旗:横向比例 + 铺满 */ .team-emblem:not(.team-emblem--logo) { object-fit: cover; @@ -102,6 +107,11 @@ watch( height: 36px; } +.team-emblem--xl:not(.team-emblem--logo) { + width: 80px; + height: 54px; +} + /* 队徽:正方形容器 + 完整显示 */ .team-emblem--logo { object-fit: contain; @@ -129,4 +139,8 @@ watch( .team-emblem--lg.team-emblem--placeholder { font-size: 22px; } + +.team-emblem--xl.team-emblem--placeholder { + font-size: 30px; +} diff --git a/apps/player/src/components/desktop/AccountSideNav.vue b/apps/player/src/components/desktop/AccountSideNav.vue new file mode 100644 index 0000000..fb68313 --- /dev/null +++ b/apps/player/src/components/desktop/AccountSideNav.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/apps/player/src/components/desktop/BetSlipPanel.vue b/apps/player/src/components/desktop/BetSlipPanel.vue new file mode 100644 index 0000000..5fba4cd --- /dev/null +++ b/apps/player/src/components/desktop/BetSlipPanel.vue @@ -0,0 +1,2033 @@ + + + + + diff --git a/apps/player/src/components/desktop/DataTable.vue b/apps/player/src/components/desktop/DataTable.vue new file mode 100644 index 0000000..e39e1e2 --- /dev/null +++ b/apps/player/src/components/desktop/DataTable.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopAuthLayout.vue b/apps/player/src/components/desktop/DesktopAuthLayout.vue new file mode 100644 index 0000000..29db87a --- /dev/null +++ b/apps/player/src/components/desktop/DesktopAuthLayout.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopBanner3DCarousel.vue b/apps/player/src/components/desktop/DesktopBanner3DCarousel.vue new file mode 100644 index 0000000..75c1c04 --- /dev/null +++ b/apps/player/src/components/desktop/DesktopBanner3DCarousel.vue @@ -0,0 +1,471 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopBetSlipRail.vue b/apps/player/src/components/desktop/DesktopBetSlipRail.vue new file mode 100644 index 0000000..b8ecb88 --- /dev/null +++ b/apps/player/src/components/desktop/DesktopBetSlipRail.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopOddsBetPopover.vue b/apps/player/src/components/desktop/DesktopOddsBetPopover.vue new file mode 100644 index 0000000..0d7bba9 --- /dev/null +++ b/apps/player/src/components/desktop/DesktopOddsBetPopover.vue @@ -0,0 +1,462 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopOutrightEventCard.vue b/apps/player/src/components/desktop/DesktopOutrightEventCard.vue new file mode 100644 index 0000000..53c8d32 --- /dev/null +++ b/apps/player/src/components/desktop/DesktopOutrightEventCard.vue @@ -0,0 +1,330 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopShell.vue b/apps/player/src/components/desktop/DesktopShell.vue new file mode 100644 index 0000000..d9c592f --- /dev/null +++ b/apps/player/src/components/desktop/DesktopShell.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopTopNav.vue b/apps/player/src/components/desktop/DesktopTopNav.vue new file mode 100644 index 0000000..c38e24c --- /dev/null +++ b/apps/player/src/components/desktop/DesktopTopNav.vue @@ -0,0 +1,243 @@ + + + + + diff --git a/apps/player/src/components/desktop/DesktopWalletSubNav.vue b/apps/player/src/components/desktop/DesktopWalletSubNav.vue new file mode 100644 index 0000000..db241ef --- /dev/null +++ b/apps/player/src/components/desktop/DesktopWalletSubNav.vue @@ -0,0 +1,46 @@ + + + diff --git a/apps/player/src/components/desktop/LeagueSidebar.vue b/apps/player/src/components/desktop/LeagueSidebar.vue new file mode 100644 index 0000000..d81dc01 --- /dev/null +++ b/apps/player/src/components/desktop/LeagueSidebar.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/apps/player/src/components/desktop/MatchSidebar.vue b/apps/player/src/components/desktop/MatchSidebar.vue new file mode 100644 index 0000000..263a723 --- /dev/null +++ b/apps/player/src/components/desktop/MatchSidebar.vue @@ -0,0 +1,343 @@ + + + + + diff --git a/apps/player/src/components/desktop/OutrightSidebar.vue b/apps/player/src/components/desktop/OutrightSidebar.vue new file mode 100644 index 0000000..2a40a56 --- /dev/null +++ b/apps/player/src/components/desktop/OutrightSidebar.vue @@ -0,0 +1,265 @@ + + + + + diff --git a/apps/player/src/components/desktop/Pagination.vue b/apps/player/src/components/desktop/Pagination.vue new file mode 100644 index 0000000..53163cc --- /dev/null +++ b/apps/player/src/components/desktop/Pagination.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/apps/player/src/components/desktop/SportsCategoryBar.vue b/apps/player/src/components/desktop/SportsCategoryBar.vue new file mode 100644 index 0000000..1d9d24f --- /dev/null +++ b/apps/player/src/components/desktop/SportsCategoryBar.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/apps/player/src/components/match-detail/CorrectScorePanel.vue b/apps/player/src/components/match-detail/CorrectScorePanel.vue index eca0bff..aff7c4b 100644 --- a/apps/player/src/components/match-detail/CorrectScorePanel.vue +++ b/apps/player/src/components/match-detail/CorrectScorePanel.vue @@ -2,6 +2,8 @@ import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { groupCorrectScoreSelections, type CsSelection } from '../../utils/correctScoreLayout'; +import { useDesktopBetPopover } from '../../composables/useDesktopBetPopover'; + const props = defineProps<{ marketType: string; @@ -15,13 +17,16 @@ const props = defineProps<{ }>; isSelected: (id: string) => boolean; locked?: boolean; + /** PC 全宽紧凑:更小单元格、三列均分 */ + dense?: boolean; }>(); const emit = defineEmits<{ - pick: [id: string]; + pick: [id: string, event?: MouseEvent]; }>(); const { t } = useI18n(); +const { cancelClose } = useDesktopBetPopover(); const columns = computed(() => groupCorrectScoreSelections( @@ -34,9 +39,9 @@ const columns = computed(() => ), ); -function onPick(sel: CsSelection) { +function onPick(sel: CsSelection, event?: MouseEvent) { if (props.locked) return; - emit('pick', sel.id); + emit('pick', sel.id, event); } function formatOdds(odds: string) { @@ -46,7 +51,11 @@ function formatOdds(odds: string) {