From 7f563326d8e0b33e1a08f402aac093332f4d7dd2 Mon Sep 17 00:00:00 2001 From: Mars <3361409208a@gmail.com> Date: Tue, 30 Jun 2026 16:11:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(player+api):=20=E6=A1=8C=E9=9D=A2=E7=AB=AF?= =?UTF-8?q?=E9=92=B1=E5=8C=85/=E6=B3=A8=E5=8D=95=E6=A0=8F=E9=87=8D?= =?UTF-8?q?=E6=9E=84=EF=BC=8C=E9=A6=96=E9=A1=B5=E5=BF=AB=E9=80=9F=E4=B8=8B?= =?UTF-8?q?=E6=B3=A8=E4=B8=8E=E7=A7=BB=E5=8A=A8=E7=AB=AF=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 桌面端: - 新增 DesktopWalletPageHeader 统一钱包子导航,移除重复大标题 - 重构我的余额页布局(居中窄卡片 + 余额/统计/账户设置) - 新增 DesktopBetSlipRail 可折叠注单侧栏,优化 BetSlipPanel 交互 - 首页 upcoming 赛事卡片支持胜平负/让球/大小球快速下注 - 优化 DesktopShell 布局、3D 轮播、各账户/消息/充值页面样式 移动端: - 重构 MobileWalletView 钱包页 UI - 修复 MarketSelectionsPanel 下注区黑色背景问题 - 新增 PageBackButton 与 useSmartBack 智能返回逻辑 - 优化公告/消息/个人中心等详情页导航 API: - listUpcomingPublished 支持 includeMarkets,返回首页卡片所需核心玩法 --- .../applications/player/player.controller.ts | 2 +- .../src/domains/catalog/matches.service.ts | 31 +- apps/player/index.html | 6 + apps/player/src/components/BetSlipDrawer.vue | 72 +- apps/player/src/components/ConfirmDialog.vue | 39 +- .../player/src/components/FloatingMailbox.vue | 56 +- .../src/components/HomeAnnouncementCard.vue | 25 +- .../src/components/MessageListPanel.vue | 26 +- apps/player/src/components/PageBackButton.vue | 73 ++ .../src/components/desktop/AccountSideNav.vue | 10 +- .../src/components/desktop/BetSlipPanel.vue | 48 +- .../src/components/desktop/DataTable.vue | 6 +- .../desktop/DesktopBanner3DCarousel.vue | 524 +++++----- .../components/desktop/DesktopBetSlipRail.vue | 179 ++++ .../desktop/DesktopOddsBetPopover.vue | 14 +- .../desktop/DesktopOutrightEventCard.vue | 6 +- .../src/components/desktop/DesktopShell.vue | 31 +- .../src/components/desktop/DesktopTopNav.vue | 17 +- .../desktop/DesktopWalletPageHeader.vue | 43 + .../desktop/DesktopWalletSubNav.vue | 15 +- .../src/components/desktop/LeagueSidebar.vue | 27 +- .../src/components/desktop/MatchSidebar.vue | 10 +- .../src/components/desktop/Pagination.vue | 8 +- .../components/desktop/SportsCategoryBar.vue | 12 +- .../match-detail/CorrectScorePanel.vue | 33 +- .../match-detail/MarketSelectionsPanel.vue | 38 +- .../components/outright/OutrightBetModal.vue | 24 +- .../outright/OutrightEventSection.vue | 8 +- .../src/components/outright/OutrightPanel.vue | 2 +- .../player/src/composables/useMatchFilters.ts | 38 +- apps/player/src/composables/usePlayerHome.ts | 23 + apps/player/src/composables/useSmartBack.ts | 45 + apps/player/src/i18n/en-US.ts | 8 +- apps/player/src/i18n/ms-MY.ts | 7 +- apps/player/src/i18n/zh-CN.ts | 8 +- apps/player/src/layouts/MainLayout.vue | 16 +- apps/player/src/router/index.ts | 4 +- apps/player/src/stores/betSlip.ts | 38 + apps/player/src/styles.css | 9 +- .../player/src/styles/desktop/interaction.css | 13 + apps/player/src/styles/desktop/layout.css | 42 +- apps/player/src/styles/desktop/tokens.css | 2 + .../views/MobileAnnouncementDetailView.vue | 39 +- .../src/views/MobileAnnouncementListView.vue | 119 ++- .../src/views/MobileMessageDetailView.vue | 85 +- .../src/views/MobileOutrightDetailView.vue | 2 +- apps/player/src/views/MobileProfileView.vue | 47 +- .../MobileWalletTransactionDetailView.vue | 8 +- apps/player/src/views/MobileWalletView.vue | 937 ++++++++---------- apps/player/src/views/ProfileView.vue | 15 +- apps/player/src/views/WalletView.vue | 12 +- .../desktop/DesktopAnnouncementDetailView.vue | 2 +- .../desktop/DesktopAnnouncementListView.vue | 14 +- .../desktop/DesktopAnnouncementsHubShell.vue | 3 +- .../views/desktop/DesktopBetDetailView.vue | 4 +- .../desktop/DesktopCashbackRecordsView.vue | 21 +- .../src/views/desktop/DesktopFootballView.vue | 6 +- .../desktop/DesktopForgotPasswordView.vue | 26 +- .../src/views/desktop/DesktopHomeView.vue | 495 +++++++-- .../src/views/desktop/DesktopInboxHubView.vue | 6 +- .../src/views/desktop/DesktopLoginView.vue | 26 +- .../views/desktop/DesktopMatchDetailView.vue | 34 +- .../views/desktop/DesktopMessagesHubShell.vue | 7 +- .../src/views/desktop/DesktopMyBetsView.vue | 2 +- .../desktop/DesktopOutrightDetailView.vue | 14 +- .../views/desktop/DesktopProfileEditView.vue | 14 +- .../src/views/desktop/DesktopProfileView.vue | 66 +- .../desktop/DesktopRechargeHistoryView.vue | 15 +- .../src/views/desktop/DesktopRechargeView.vue | 21 +- .../src/views/desktop/DesktopRegisterView.vue | 26 +- .../views/desktop/DesktopWalletDetailView.vue | 216 ++-- .../DesktopWalletTransactionDetailView.vue | 4 +- .../src/views/desktop/DesktopWalletView.vue | 494 ++++++--- 73 files changed, 2866 insertions(+), 1552 deletions(-) create mode 100644 apps/player/src/components/PageBackButton.vue create mode 100644 apps/player/src/components/desktop/DesktopBetSlipRail.vue create mode 100644 apps/player/src/components/desktop/DesktopWalletPageHeader.vue create mode 100644 apps/player/src/composables/useSmartBack.ts diff --git a/apps/api/src/applications/player/player.controller.ts b/apps/api/src/applications/player/player.controller.ts index ec16982..499b645 100644 --- a/apps/api/src/applications/player/player.controller.ts +++ b/apps/api/src/applications/player/player.controller.ts @@ -190,7 +190,7 @@ export class PlayerController { this.content.listActive('BANNER', locale), this.content.listActiveAnnouncements(locale), this.matches.listPublished(locale, undefined, { includeMarkets: false }), - this.matches.listUpcomingPublished(locale), + 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..cdb6456 100644 --- a/apps/api/src/domains/catalog/matches.service.ts +++ b/apps/api/src/domains/catalog/matches.service.ts @@ -1592,17 +1592,42 @@ export class MatchesService { ); } + /** 首页卡片快速下注所需的核心玩法类型 */ + private static readonly HOME_CARD_MARKET_TYPES = [ + 'FT_1X2', + 'FT_HANDICAP', + 'FT_OVER_UNDER', + ] as const; + /** 未来 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); + const marketsRelation = includeMarkets + ? { + where: { + status: { in: ['OPEN', 'SUSPENDED', 'CLOSED'] }, + showOnPlayer: true, + marketType: { in: [...MatchesService.HOME_CARD_MARKET_TYPES] }, + }, + include: { + selections: { + where: { status: { in: ['OPEN', 'SUSPENDED', 'CLOSED'] } }, + orderBy: { sortOrder: 'asc' as const }, + }, + }, + orderBy: { sortOrder: 'asc' as const }, + } + : this.playerMarketStatusInclude; + const matches = await this.prisma.match.findMany({ where: { status: { in: ['PUBLISHED', 'CLOSED', 'PENDING_SETTLEMENT'] }, @@ -1625,14 +1650,14 @@ export class MatchesService { homeTeam: true, awayTeam: true, score: true, - markets: this.playerMarketStatusInclude, + markets: marketsRelation, }, 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/index.html b/apps/player/index.html index 237a9f2..92d3cba 100644 --- a/apps/player/index.html +++ b/apps/player/index.html @@ -12,6 +12,12 @@ + + + TheBet365 diff --git a/apps/player/src/components/FloatingMailbox.vue b/apps/player/src/components/FloatingMailbox.vue index 331f457..26f36ff 100644 --- a/apps/player/src/components/FloatingMailbox.vue +++ b/apps/player/src/components/FloatingMailbox.vue @@ -32,12 +32,27 @@ const showMessageActions = computed( () => inboxEnabled.value && activeTab.value === 'messages' && auth.token && hasMessages.value && !selectedMessageId.value, ); -const isBettingDesktop = computed(() => { - if (!isDesktop.value) return false; +const hideOnMessagesPage = computed( + () => isDesktop.value && route.path.startsWith('/messages'), +); + +const offsetForBetSlip = computed(() => { + if (!isDesktop.value || hideOnMessagesPage.value) return false; const p = route.path; - return p === '/bet' || p.startsWith('/match/') || p.startsWith('/outright/'); + if (p === '/bets' || p.startsWith('/bets/')) return false; + if (p.startsWith('/wallet') || p.startsWith('/profile')) return false; + return true; }); +watch( + () => route.path, + (path) => { + if (isDesktop.value && path.startsWith('/messages') && isOpen.value) { + close(); + } + }, +); + watch(isOpen, (opened) => { if (opened) { if (!inboxEnabled.value) activeTab.value = 'support'; @@ -86,7 +101,11 @@ onMounted(() => {