From b36ab3b10de2bef09c14bc961a5e24a43e2ffcee Mon Sep 17 00:00:00 2001 From: yuchenglong Date: Thu, 22 Jan 2026 10:21:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BD=91=E7=90=83=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=95=B0=E6=8D=AE=E6=98=A0=E5=B0=84=E5=92=8C=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=88=B0=20MatchCard=20=E7=BB=84=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=AF=94=E8=B5=9B=E4=BF=A1=E6=81=AF=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(tabs)/live.tsx | 7 +++ components/match-card.tsx | 106 +++++++++++++++++++++++++++++++++++--- lib/api.ts | 1 + types/api.ts | 23 +++++++-- 4 files changed, 127 insertions(+), 10 deletions(-) diff --git a/app/(tabs)/live.tsx b/app/(tabs)/live.tsx index 06f6fc1..df4659a 100644 --- a/app/(tabs)/live.tsx +++ b/app/(tabs)/live.tsx @@ -72,6 +72,13 @@ export default function LiveScreen() { sportId: state.selectedSportId ?? undefined, isLive: true, date: item.event_date, + // Tennis specific mapping + eventFirstPlayer: item.event_first_player, + eventSecondPlayer: item.event_second_player, + eventFirstPlayerLogo: item.event_first_player_logo, + eventSecondPlayerLogo: item.event_second_player_logo, + eventServe: item.event_serve, + scores: item.scores ? JSON.stringify(item.scores) : undefined, })); const token = await storage.getAccessToken(); diff --git a/components/match-card.tsx b/components/match-card.tsx index a7ce83a..160b080 100644 --- a/components/match-card.tsx +++ b/components/match-card.tsx @@ -106,6 +106,36 @@ export function MatchCard({ return !!match.isLive; }, [match.isLive]); + // Tennis logic + const isTennis = match.sportId === 3 || match.sport === "tennis"; + const homeName = isTennis + ? liveDetail?.event_first_player || match.eventFirstPlayer + : liveDetail?.event_home_team || match.homeTeamName || match.home; + const awayName = isTennis + ? liveDetail?.event_second_player || match.eventSecondPlayer + : liveDetail?.event_away_team || match.awayTeamName || match.away; + const homeLogo = isTennis + ? liveDetail?.event_first_player_logo || match.eventFirstPlayerLogo + : liveDetail?.home_team_logo || match.homeTeamLogo; + const awayLogo = isTennis + ? liveDetail?.event_second_player_logo || match.eventSecondPlayerLogo + : liveDetail?.away_team_logo || match.awayTeamLogo; + + const tennisScores = React.useMemo(() => { + // 优先使用 liveDetail 中的 scores + if (isTennis && liveDetail?.scores && Array.isArray(liveDetail.scores)) { + return liveDetail.scores; + } + + if (!isTennis || !match.scores) return []; + try { + const parsed = JSON.parse(match.scores); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } + }, [match.scores, isTennis, liveDetail]); + const timeLabel = React.useMemo(() => { const raw = (match.time || "").trim(); if (!raw) return ""; @@ -121,7 +151,31 @@ export function MatchCard({ }, [match.leagueName, match.league]); const scoreParts = React.useMemo(() => { + if (isTennis) { + if (tennisScores.length > 0) { + const last = tennisScores[tennisScores.length - 1]; + const h = parseInt(last.score_first || "0"); + const a = parseInt(last.score_second || "0"); + return { + home: last.score_first || "0", + away: last.score_second || "0", + hasScore: true, + homeLead: h > a, + awayLead: a > h, + }; + } + // 如果没有分数(如未开始),显示 0-0 或 - + return { + home: "0", + away: "0", + hasScore: true, // 保持 ScoreBox 显示,以便显示 0-0 + homeLead: false, + awayLead: false, + }; + } + const s = (match.scoreText || "").trim(); + const m = s.match(/(\d+)\s*[-:]\s*(\d+)/); if (m) { const h = parseInt(m[1]); @@ -271,7 +325,33 @@ export function MatchCard({ } }; + const renderTennisSetScores = (isHome: boolean) => { + // 显示除最后一盘之外的盘分 (当前盘分在右侧大框显示) + // 如果只有一盘,则这里不显示任何内容 + if (tennisScores.length <= 1) return null; + + const prevSets = tennisScores.slice(0, tennisScores.length - 1); + + return ( + + {prevSets.map((s: any, i: number) => ( + + {isHome ? s.score_first : s.score_second} + + ))} + + ); + }; + const renderOddsRow = (bookmakerName: string, isHighlight: boolean) => { + if (isTennis) return renderTennisSetScores(isHighlight); // Reuse isHighlight param as 'isHome' (true for first player logic) + if (!oddsSettings.enabled || !bookmakerName) return null; const item = odds.find((o) => o.odd_bookmakers === bookmakerName); @@ -386,7 +466,7 @@ export function MatchCard({ - {match.homeTeamName || match.home} + {homeName} - {renderCardsInline(cardsCount.homeYellow, cardsCount.homeRed)} + {!isTennis && + renderCardsInline(cardsCount.homeYellow, cardsCount.homeRed)} {renderOddsRow(oddsSettings.selectedBookmakers[0], true)} @@ -410,7 +491,7 @@ export function MatchCard({ - {match.awayTeamName || match.away} + {awayName} - {renderCardsInline(cardsCount.awayYellow, cardsCount.awayRed)} + {!isTennis && + renderCardsInline(cardsCount.awayYellow, cardsCount.awayRed)} {renderOddsRow(oddsSettings.selectedBookmakers[1], false)} @@ -710,4 +792,16 @@ const styles = StyleSheet.create({ cardBadgeTextDark: { color: "#000", }, + tennisScoresRow: { + flexDirection: "row", + gap: 12, + marginRight: 4, + alignItems: "center", + }, + tennisScoreText: { + fontSize: 14, + fontWeight: "500", + minWidth: 14, + textAlign: "center", + }, }); diff --git a/lib/api.ts b/lib/api.ts index e3e47d2..5772e33 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -182,6 +182,7 @@ export const fetchTodayMatches = async (options: { { params }, ); if (response.data.code === 0) { + // console.log("Fetched today matches:", JSON.stringify(response.data.data)); return response.data.data; } throw new Error(response.data.message); diff --git a/types/api.ts b/types/api.ts index aac8012..d57047a 100644 --- a/types/api.ts +++ b/types/api.ts @@ -37,6 +37,13 @@ export interface Match { isLive?: boolean; meta?: string; odds?: OddsItem[]; + // Tennis specific + eventFirstPlayer?: string; + eventSecondPlayer?: string; + eventFirstPlayerLogo?: string; + eventSecondPlayerLogo?: string; + scores?: string; + eventServe?: string; } export interface LiveScoreMatch { @@ -61,6 +68,14 @@ export interface LiveScoreMatch { country_name: string; country_logo: string; event_country_key: number; + // Tennis specific + event_first_player?: string; + event_first_player_logo?: string; + event_second_player?: string; + event_second_player_logo?: string; + event_serve?: string; + pointbypoint?: any[]; + scores?: any[]; // LiveScoreMatch uses array for scores, Match uses string goalscorers?: { time: string; home_scorer: string; @@ -88,11 +103,11 @@ export interface LiveScoreMatch { substitutes?: { time: string; home_scorer: - | { in: string; out: string; in_id: number; out_id: number } - | any[]; + | { in: string; out: string; in_id: number; out_id: number } + | any[]; away_scorer: - | { in: string; out: string; in_id: number; out_id: number } - | any[]; + | { in: string; out: string; in_id: number; out_id: number } + | any[]; info: string; info_time: string; score: string;