篮球live详情
This commit is contained in:
@@ -388,8 +388,8 @@ export default function HomeScreen() {
|
|||||||
away: item.event_away_team,
|
away: item.event_away_team,
|
||||||
homeTeamName: item.event_home_team,
|
homeTeamName: item.event_home_team,
|
||||||
awayTeamName: item.event_away_team,
|
awayTeamName: item.event_away_team,
|
||||||
homeTeamLogo: item.home_team_logo,
|
homeTeamLogo: item.home_team_logo || "",
|
||||||
awayTeamLogo: item.away_team_logo,
|
awayTeamLogo: item.away_team_logo || "",
|
||||||
scoreText: item.event_halftime_result || "0 - 0",
|
scoreText: item.event_halftime_result || "0 - 0",
|
||||||
fav: false,
|
fav: false,
|
||||||
sportId: sportId,
|
sportId: sportId,
|
||||||
|
|||||||
@@ -5,14 +5,17 @@ import { LiveScoreHeader } from "@/components/live-detail/live-score-header";
|
|||||||
import { OddsCard } from "@/components/live-detail/odds-card";
|
import { OddsCard } from "@/components/live-detail/odds-card";
|
||||||
import { OtherInfoCard } from "@/components/live-detail/other-info-card";
|
import { OtherInfoCard } from "@/components/live-detail/other-info-card";
|
||||||
import { StatsCard } from "@/components/live-detail/stats-card";
|
import { StatsCard } from "@/components/live-detail/stats-card";
|
||||||
|
import { BasketballOverallStats } from "@/components/match-detail/basketball/basketball-overall-stats";
|
||||||
|
import { BasketballScoreTable } from "@/components/match-detail/basketball/basketball-score-table";
|
||||||
|
import { BasketballStats } from "@/components/match-detail/basketball/basketball-stats";
|
||||||
import { ThemedText } from "@/components/themed-text";
|
import { ThemedText } from "@/components/themed-text";
|
||||||
import { ThemedView } from "@/components/themed-view";
|
import { ThemedView } from "@/components/themed-view";
|
||||||
import { Colors } from "@/constants/theme";
|
import { Colors } from "@/constants/theme";
|
||||||
import { useTheme } from "@/context/ThemeContext";
|
import { useTheme } from "@/context/ThemeContext";
|
||||||
import { fetchLiveScore } from "@/lib/api";
|
import { fetchLiveScore } from "@/lib/api";
|
||||||
import { LiveScoreMatch } from "@/types/api";
|
import { LiveScoreMatch, MatchDetailData } from "@/types/api";
|
||||||
import { useLocalSearchParams } from "expo-router";
|
import { useLocalSearchParams } from "expo-router";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useMemo, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
@@ -36,11 +39,72 @@ export default function LiveDetailScreen() {
|
|||||||
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [match, setMatch] = useState<LiveScoreMatch | null>(null);
|
const [match, setMatch] = useState<LiveScoreMatch | null>(null);
|
||||||
const [activeTab, setActiveTab] = useState("detail"); // Default to detail to show all data
|
const [activeTab, setActiveTab] = useState("detail");
|
||||||
|
|
||||||
|
const convertToMatchDetailData = useMemo((): MatchDetailData | null => {
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
match: {
|
||||||
|
ID: 0,
|
||||||
|
CreatedAt: "",
|
||||||
|
UpdatedAt: "",
|
||||||
|
DeletedAt: null,
|
||||||
|
eventKey: match.event_key.toString(),
|
||||||
|
eventDate: match.event_date,
|
||||||
|
eventTime: match.event_time,
|
||||||
|
eventHomeTeam: match.event_home_team,
|
||||||
|
homeTeamKey: match.home_team_key.toString(),
|
||||||
|
homeTeamLogo: match.home_team_logo || "",
|
||||||
|
eventAwayTeam: match.event_away_team,
|
||||||
|
awayTeamKey: match.away_team_key.toString(),
|
||||||
|
awayTeamLogo: match.away_team_logo || "",
|
||||||
|
eventHalftimeResult: match.event_halftime_result || "",
|
||||||
|
eventFinalResult: match.event_final_result,
|
||||||
|
eventFtResult: "",
|
||||||
|
eventPenaltyResult: "",
|
||||||
|
eventStatus: match.event_status,
|
||||||
|
countryName: match.country_name,
|
||||||
|
leagueName: match.league_name,
|
||||||
|
leagueKey: match.league_key.toString(),
|
||||||
|
leagueRound: match.league_round,
|
||||||
|
leagueSeason: match.league_season,
|
||||||
|
eventLive: match.event_live,
|
||||||
|
eventStadium: "",
|
||||||
|
eventReferee: "",
|
||||||
|
eventCountryKey: match.event_country_key?.toString() || "",
|
||||||
|
leagueLogo: match.league_logo || "",
|
||||||
|
countryLogo: match.country_logo || "",
|
||||||
|
eventHomeFormation: "",
|
||||||
|
eventAwayFormation: "",
|
||||||
|
fkStageKey: "",
|
||||||
|
stageName: "",
|
||||||
|
leagueGroup: "",
|
||||||
|
sportId: parseInt(sport_id || "1"),
|
||||||
|
eventQuarter: match.event_quarter || "",
|
||||||
|
eventSet: "",
|
||||||
|
eventType: "",
|
||||||
|
eventToss: "",
|
||||||
|
eventManOfMatch: "",
|
||||||
|
scores: match.scores,
|
||||||
|
stats: match.statistics,
|
||||||
|
players: match.player_statistics ? {
|
||||||
|
home_team: (match.player_statistics.home_team || []).map(p => ({
|
||||||
|
...p,
|
||||||
|
player_oncourt: p.player_oncourt || undefined,
|
||||||
|
})),
|
||||||
|
away_team: (match.player_statistics.away_team || []).map(p => ({
|
||||||
|
...p,
|
||||||
|
player_oncourt: p.player_oncourt || undefined,
|
||||||
|
})),
|
||||||
|
} : undefined,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, [match, sport_id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadLiveDetail();
|
loadLiveDetail();
|
||||||
// 设置每 15 秒更新一次直播比分
|
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
refreshLiveDetail();
|
refreshLiveDetail();
|
||||||
}, 15000);
|
}, 15000);
|
||||||
@@ -60,14 +124,12 @@ export default function LiveDetailScreen() {
|
|||||||
|
|
||||||
const refreshLiveDetail = async () => {
|
const refreshLiveDetail = async () => {
|
||||||
try {
|
try {
|
||||||
// Fetch live scores for the league
|
|
||||||
const sportId = parseInt(sport_id || "1");
|
const sportId = parseInt(sport_id || "1");
|
||||||
const leagueId = parseInt(league_id || "0");
|
const leagueId = parseInt(league_id || "0");
|
||||||
|
|
||||||
const liveData = await fetchLiveScore(sportId, leagueId);
|
const liveData = await fetchLiveScore(sportId, leagueId);
|
||||||
|
|
||||||
if (liveData && Array.isArray(liveData)) {
|
if (liveData && Array.isArray(liveData)) {
|
||||||
// Find the specific match
|
|
||||||
const found = liveData.find((m) => m.event_key.toString() === id);
|
const found = liveData.find((m) => m.event_key.toString() === id);
|
||||||
console.log("found", JSON.stringify(found, null, 2));
|
console.log("found", JSON.stringify(found, null, 2));
|
||||||
if (found) {
|
if (found) {
|
||||||
@@ -99,7 +161,37 @@ export default function LiveDetailScreen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const renderTabContent = () => {
|
const renderTabContent = () => {
|
||||||
|
if (!match) return null;
|
||||||
|
|
||||||
const numericSportId = parseInt(sport_id || "1");
|
const numericSportId = parseInt(sport_id || "1");
|
||||||
|
|
||||||
|
if (numericSportId === 2 && convertToMatchDetailData) {
|
||||||
|
switch (activeTab) {
|
||||||
|
case "stats":
|
||||||
|
return <BasketballStats data={convertToMatchDetailData} isDark={isDark} />;
|
||||||
|
case "overall":
|
||||||
|
return <BasketballOverallStats data={convertToMatchDetailData} isDark={isDark} />;
|
||||||
|
case "odds":
|
||||||
|
return (
|
||||||
|
<OddsCard match={match} isDark={isDark} sportId={numericSportId} />
|
||||||
|
);
|
||||||
|
case "detail":
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BasketballScoreTable data={convertToMatchDetailData} isDark={isDark} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<View style={styles.center}>
|
||||||
|
<ThemedText style={{ opacity: 0.5 }}>
|
||||||
|
{t("detail.empty_stats")}
|
||||||
|
</ThemedText>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (activeTab) {
|
switch (activeTab) {
|
||||||
case "stats":
|
case "stats":
|
||||||
return <StatsCard match={match} isDark={isDark} />;
|
return <StatsCard match={match} isDark={isDark} />;
|
||||||
@@ -140,6 +232,7 @@ export default function LiveDetailScreen() {
|
|||||||
activeTab={activeTab}
|
activeTab={activeTab}
|
||||||
onTabChange={setActiveTab}
|
onTabChange={setActiveTab}
|
||||||
isDark={isDark}
|
isDark={isDark}
|
||||||
|
sportId={parseInt(sport_id || "1")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{renderTabContent()}
|
{renderTabContent()}
|
||||||
|
|||||||
@@ -8,31 +8,53 @@ interface LiveMatchTabsProps {
|
|||||||
activeTab: string;
|
activeTab: string;
|
||||||
onTabChange: (tab: string) => void;
|
onTabChange: (tab: string) => void;
|
||||||
isDark: boolean;
|
isDark: boolean;
|
||||||
|
sportId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LiveMatchTabs({
|
export function LiveMatchTabs({
|
||||||
activeTab,
|
activeTab,
|
||||||
onTabChange,
|
onTabChange,
|
||||||
isDark,
|
isDark,
|
||||||
|
sportId = 1,
|
||||||
}: LiveMatchTabsProps) {
|
}: LiveMatchTabsProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const containerBg = isDark ? "#121212" : "#F5F5F5";
|
const containerBg = isDark ? "#121212" : "#F5F5F5";
|
||||||
|
|
||||||
const tabs = [
|
const getTabs = () => {
|
||||||
{
|
if (sportId === 2) {
|
||||||
id: "detail",
|
return [
|
||||||
label: t("detail.tabs.info"),
|
{
|
||||||
icon: "document-text-outline",
|
id: "detail",
|
||||||
},
|
label: t("detail.tabs.info"),
|
||||||
{ id: "stats", label: t("detail.tabs.stats"), icon: "stats-chart-outline" },
|
icon: "document-text-outline",
|
||||||
{ id: "odds", label: t("detail.tabs.odds"), icon: "cash-outline" },
|
},
|
||||||
{ id: "lineup", label: t("detail.tabs.lineup"), icon: "shirt-outline" },
|
{ id: "stats", label: t("detail.tabs.stats"), icon: "stats-chart-outline" },
|
||||||
{
|
{
|
||||||
id: "analysis",
|
id: "overall",
|
||||||
label: t("detail.tabs.analysis"),
|
label: t("detail.tabs.overall"),
|
||||||
icon: "pie-chart-outline",
|
icon: "bar-chart-outline",
|
||||||
},
|
},
|
||||||
];
|
{ id: "odds", label: t("detail.tabs.odds"), icon: "cash-outline" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "detail",
|
||||||
|
label: t("detail.tabs.info"),
|
||||||
|
icon: "document-text-outline",
|
||||||
|
},
|
||||||
|
{ id: "stats", label: t("detail.tabs.stats"), icon: "stats-chart-outline" },
|
||||||
|
{ id: "odds", label: t("detail.tabs.odds"), icon: "cash-outline" },
|
||||||
|
{ id: "lineup", label: t("detail.tabs.lineup"), icon: "shirt-outline" },
|
||||||
|
{
|
||||||
|
id: "analysis",
|
||||||
|
label: t("detail.tabs.analysis"),
|
||||||
|
icon: "pie-chart-outline",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const tabs = getTabs();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, { backgroundColor: containerBg }]}>
|
<View style={[styles.container, { backgroundColor: containerBg }]}>
|
||||||
|
|||||||
71
types/api.ts
71
types/api.ts
@@ -52,10 +52,10 @@ export interface LiveScoreMatch {
|
|||||||
event_time: string;
|
event_time: string;
|
||||||
event_home_team: string;
|
event_home_team: string;
|
||||||
home_team_key: number;
|
home_team_key: number;
|
||||||
home_team_logo: string;
|
home_team_logo: string | null;
|
||||||
event_away_team: string;
|
event_away_team: string;
|
||||||
away_team_key: number;
|
away_team_key: number;
|
||||||
away_team_logo: string;
|
away_team_logo: string | null;
|
||||||
event_final_result: string;
|
event_final_result: string;
|
||||||
event_halftime_result: string;
|
event_halftime_result: string;
|
||||||
event_status: string;
|
event_status: string;
|
||||||
@@ -68,6 +68,7 @@ export interface LiveScoreMatch {
|
|||||||
country_name: string;
|
country_name: string;
|
||||||
country_logo: string;
|
country_logo: string;
|
||||||
event_country_key: number;
|
event_country_key: number;
|
||||||
|
event_quarter?: string;
|
||||||
// Tennis specific
|
// Tennis specific
|
||||||
event_first_player?: string;
|
event_first_player?: string;
|
||||||
event_first_player_logo?: string;
|
event_first_player_logo?: string;
|
||||||
@@ -75,7 +76,12 @@ export interface LiveScoreMatch {
|
|||||||
event_second_player_logo?: string;
|
event_second_player_logo?: string;
|
||||||
event_serve?: string;
|
event_serve?: string;
|
||||||
pointbypoint?: any[];
|
pointbypoint?: any[];
|
||||||
scores?: any[]; // LiveScoreMatch uses array for scores, Match uses string
|
scores?: any[] | {
|
||||||
|
"1stQuarter"?: Array<{ score_home: string; score_away: string }>;
|
||||||
|
"2ndQuarter"?: Array<{ score_home: string; score_away: string }>;
|
||||||
|
"3rdQuarter"?: Array<{ score_home: string; score_away: string }>;
|
||||||
|
"4thQuarter"?: Array<{ score_home: string; score_away: string }>;
|
||||||
|
};
|
||||||
goalscorers?: {
|
goalscorers?: {
|
||||||
time: string;
|
time: string;
|
||||||
home_scorer: string;
|
home_scorer: string;
|
||||||
@@ -112,7 +118,64 @@ export interface LiveScoreMatch {
|
|||||||
info_time: string;
|
info_time: string;
|
||||||
score: string;
|
score: string;
|
||||||
}[];
|
}[];
|
||||||
lineups?: unknown;
|
lineups?: {
|
||||||
|
home_team?: {
|
||||||
|
starting_lineups?: Array<{ player: string; player_id: number }>;
|
||||||
|
substitutes?: Array<{ player: string; player_id: number }>;
|
||||||
|
};
|
||||||
|
away_team?: {
|
||||||
|
starting_lineups?: Array<{ player: string; player_id: number }>;
|
||||||
|
substitutes?: Array<{ player: string; player_id: number }>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
player_statistics?: {
|
||||||
|
home_team?: Array<{
|
||||||
|
player: string;
|
||||||
|
player_id: number;
|
||||||
|
player_position?: string;
|
||||||
|
player_minutes?: string;
|
||||||
|
player_points?: string;
|
||||||
|
player_total_rebounds?: string;
|
||||||
|
player_offence_rebounds?: string;
|
||||||
|
player_defense_rebounds?: string;
|
||||||
|
player_assists?: string;
|
||||||
|
player_steals?: string;
|
||||||
|
player_blocks?: string;
|
||||||
|
player_turnovers?: string;
|
||||||
|
player_personal_fouls?: string;
|
||||||
|
player_plus_minus?: string;
|
||||||
|
player_field_goals_made?: string;
|
||||||
|
player_field_goals_attempts?: string;
|
||||||
|
player_threepoint_goals_made?: string;
|
||||||
|
player_threepoint_goals_attempts?: string;
|
||||||
|
player_freethrows_goals_made?: string;
|
||||||
|
player_freethrows_goals_attempts?: string;
|
||||||
|
player_oncourt?: string | null;
|
||||||
|
}>;
|
||||||
|
away_team?: Array<{
|
||||||
|
player: string;
|
||||||
|
player_id: number;
|
||||||
|
player_position?: string;
|
||||||
|
player_minutes?: string;
|
||||||
|
player_points?: string;
|
||||||
|
player_total_rebounds?: string;
|
||||||
|
player_offence_rebounds?: string;
|
||||||
|
player_defense_rebounds?: string;
|
||||||
|
player_assists?: string;
|
||||||
|
player_steals?: string;
|
||||||
|
player_blocks?: string;
|
||||||
|
player_turnovers?: string;
|
||||||
|
player_personal_fouls?: string;
|
||||||
|
player_plus_minus?: string;
|
||||||
|
player_field_goals_made?: string;
|
||||||
|
player_field_goals_attempts?: string;
|
||||||
|
player_threepoint_goals_made?: string;
|
||||||
|
player_threepoint_goals_attempts?: string;
|
||||||
|
player_freethrows_goals_made?: string;
|
||||||
|
player_freethrows_goals_attempts?: string;
|
||||||
|
player_oncourt?: string | null;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
statistics?: {
|
statistics?: {
|
||||||
type: string;
|
type: string;
|
||||||
home: string;
|
home: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user