diff --git a/apps/api/src/applications/player/player.controller.ts b/apps/api/src/applications/player/player.controller.ts index ec16982..5f7c6fd 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..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/src/components/desktop/SportsCategoryBar.vue b/apps/player/src/components/desktop/SportsCategoryBar.vue index a48786e..5e2732d 100644 --- a/apps/player/src/components/desktop/SportsCategoryBar.vue +++ b/apps/player/src/components/desktop/SportsCategoryBar.vue @@ -5,9 +5,9 @@ import { useI18n } from 'vue-i18n'; type SportCategory = 'football' | 'basketball' | 'tennis'; const SPORT_CATEGORIES = [ - { id: 'football' as const, icon: '⚽', labelKey: 'bet.sport_football', enabled: true }, - { id: 'basketball' as const, icon: '🏀', labelKey: 'bet.sport_basketball', enabled: false }, - { id: 'tennis' as const, icon: '🎾', labelKey: 'bet.sport_tennis', enabled: false }, + { id: 'football' as const, icon: '', labelKey: 'bet.sport_football', enabled: true }, + { id: 'basketball' as const, icon: '', labelKey: 'bet.sport_basketball', enabled: false }, + { id: 'tennis' as const, icon: '', labelKey: 'bet.sport_tennis', enabled: false }, ] as const; const { t } = useI18n(); @@ -34,7 +34,7 @@ function selectSport(id: SportCategory, enabled: boolean) { :aria-current="sport.enabled && activeSport === sport.id ? 'page' : undefined" @click="selectSport(sport.id, sport.enabled)" > - + {{ t(sport.labelKey) }} {{ t('bet.sport_coming_soon') }} diff --git a/apps/player/src/i18n/en-US.ts b/apps/player/src/i18n/en-US.ts index 9e69320..8b225a5 100644 --- a/apps/player/src/i18n/en-US.ts +++ b/apps/player/src/i18n/en-US.ts @@ -405,6 +405,7 @@ export default { correct_score: 'Correct score', no_markets: 'No markets available', no_markets_filtered: 'No markets match the current filter', + no_market_odds: 'No odds available', popover_title: 'Confirm bet', place_now: 'Bet now', add_to_single: 'Add to singles', diff --git a/apps/player/src/i18n/ms-MY.ts b/apps/player/src/i18n/ms-MY.ts index 113e3b6..21d0aea 100644 --- a/apps/player/src/i18n/ms-MY.ts +++ b/apps/player/src/i18n/ms-MY.ts @@ -417,6 +417,7 @@ export default { correct_score: 'Skor tepat', no_markets: 'Tiada pasaran tersedia', no_markets_filtered: 'Tiada pasaran sepadan dengan penapis semasa', + no_market_odds: 'Tiada odds pasaran', popover_title: 'Sahkan pertaruhan', place_now: 'Pertaruhan sekarang', add_to_single: 'Tambah tunggal', diff --git a/apps/player/src/i18n/zh-CN.ts b/apps/player/src/i18n/zh-CN.ts index 29a56ca..139b752 100644 --- a/apps/player/src/i18n/zh-CN.ts +++ b/apps/player/src/i18n/zh-CN.ts @@ -405,6 +405,7 @@ export default { correct_score: '波胆', no_markets: '暂无可用盘口', no_markets_filtered: '当前筛选下暂无盘口', + no_market_odds: '暂无盘口赔率', popover_title: '确认投注', place_now: '立即下注', add_to_single: '加入单关', diff --git a/apps/player/src/views/desktop/DesktopFootballView.vue b/apps/player/src/views/desktop/DesktopFootballView.vue index 295f87b..0c4c327 100644 --- a/apps/player/src/views/desktop/DesktopFootballView.vue +++ b/apps/player/src/views/desktop/DesktopFootballView.vue @@ -131,7 +131,7 @@ void loadParlayMatches(true); :class="{ active: mainTab === 'matches' }" @click="setMainTab('matches')" > - ⚽ {{ t('bet.tab_matches') }} + {{ t('bet.tab_matches') }}