Files
physical-expo/app/match-detail/[id].tsx
2026-01-23 16:54:24 +08:00

298 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { OddsCard } from "@/components/live-detail/odds-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 { CricketH2H } from "@/components/match-detail/cricket/cricket-h2h";
import { CricketH2HCard } from "@/components/match-detail/cricket/cricket-h2h-card";
import { CricketMatchInfoCard } from "@/components/match-detail/cricket/cricket-match-info-card";
import { CricketTeamsCard } from "@/components/match-detail/cricket/cricket-teams-card";
import { CardsCard } from "@/components/match-detail/football/cards-card";
import { FootballScoreTable } from "@/components/match-detail/football/football-score-table";
import { GoalsCard } from "@/components/match-detail/football/goals-card";
import { LineupsCard } from "@/components/match-detail/football/lineups-card";
import { SubstitutesCard } from "@/components/match-detail/football/substitutes-card";
import { H2H } from "@/components/match-detail/h2h";
import { LeagueInfo } from "@/components/match-detail/league-info";
import { MatchInfoCard } from "@/components/match-detail/match-info-card";
import { MatchTabs } from "@/components/match-detail/match-tabs";
import { ScoreHeader } from "@/components/match-detail/score-header";
import { TennisScoreTable } from "@/components/match-detail/tennis/tennis-score-table";
import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view";
import { Colors } from "@/constants/theme";
import { useTheme } from "@/context/ThemeContext";
import { fetchMatchDetail } from "@/lib/api";
import { MatchDetailData } from "@/types/api";
import { useLocalSearchParams, useRouter } from "expo-router";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
ActivityIndicator,
ScrollView,
StyleSheet,
TouchableOpacity,
View,
} from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
export default function MatchDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const router = useRouter();
const { theme } = useTheme();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const isDark = theme === "dark";
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [data, setData] = useState<MatchDetailData | null>(null);
const [activeTab, setActiveTab] = useState("info");
useEffect(() => {
if (id) {
loadMatchDetail();
}
}, [id]);
// 当数据加载完成且 sportId 变化时,检查并重置 activeTab
useEffect(() => {
if (data?.match?.sportId) {
const sportId = data.match.sportId;
let validTabs: string[] = [];
if (sportId === 1) {
// 足球
validTabs = ["info", "stats", "odds", "h2h", "chat"];
} else if (sportId === 2) {
// 篮球
validTabs = ["info", "stats", "overall", "h2h", "chat"];
} else if (sportId === 3) {
// 网球
validTabs = ["info", "chat"];
} else if (sportId === 4) {
// 板球
validTabs = ["info", "lineup", "h2h", "chat"];
} else {
// 默认
validTabs = ["info", "h2h", "chat"];
}
// 如果当前 activeTab 不在有效标签列表中,重置为第一个
if (!validTabs.includes(activeTab)) {
setActiveTab(validTabs[0]);
}
}
}, [data?.match?.sportId]);
const loadMatchDetail = async () => {
try {
setLoading(true);
setError(null);
const result = await fetchMatchDetail(id as string);
setData(result);
// console.log("首发阵容", result.match.players?.away_team);
// console.log("红黄牌", result.events);
} catch (err: any) {
setError(err.message || t("detail.fetch_failed"));
} finally {
setLoading(false);
}
};
if (loading) {
return (
<ThemedView style={styles.center}>
<ActivityIndicator size="large" color={Colors[theme].tint} />
</ThemedView>
);
}
if (error || !data) {
return (
<ThemedView style={styles.center}>
<ThemedText>{error || t("detail.not_found")}</ThemedText>
<TouchableOpacity style={styles.retryButton} onPress={loadMatchDetail}>
<ThemedText style={styles.retryText}>{t("detail.retry")}</ThemedText>
</TouchableOpacity>
</ThemedView>
);
}
const renderTabContent = () => {
const sportId = data?.match?.sportId || 1;
switch (activeTab) {
case "info":
// 根据 sportId 显示不同的详情组件
if (sportId === 1) {
// 足球:显示 FootballScoreTable (半场/全场) 和 MatchInfoCard
return (
<>
<FootballScoreTable data={data} isDark={isDark} />
<MatchInfoCard data={data} isDark={isDark} />
</>
);
} else if (sportId === 2) {
// 篮球:显示 BasketballScoreTable (4节) 和 MatchInfoCard
return (
<>
<BasketballScoreTable data={data} isDark={isDark} />
<MatchInfoCard data={data} isDark={isDark} />
</>
);
} else if (sportId === 3) {
// 网球:显示 TennisScoreTable 和 MatchInfoCard
return (
<>
<TennisScoreTable data={data} isDark={isDark} />
<MatchInfoCard data={data} isDark={isDark} />
</>
);
} else if (sportId === 4) {
// 板球
// json数据中如果有就展示没有和我说 (Team Card, Match Info Card implemented. H2H skipped as not in JSON)
return (
<>
<CricketTeamsCard data={data} isDark={isDark} />
<CricketH2HCard data={data} isDark={isDark} />
<CricketMatchInfoCard data={data} isDark={isDark} />
</>
);
} else {
// 默认使用足球组件
return (
<>
<FootballScoreTable data={data} isDark={isDark} />
<MatchInfoCard data={data} isDark={isDark} />
</>
);
}
case "stats":
// 统计数据:显示进球、红黄牌、换人、首发阵容(分开显示)
if (sportId === 1) {
// 足球:分别显示各个卡片
return (
<>
<GoalsCard data={data} isDark={isDark} />
<CardsCard data={data} isDark={isDark} />
<SubstitutesCard data={data} isDark={isDark} />
<LineupsCard data={data} isDark={isDark} />
</>
);
} else if (sportId === 2) {
// 篮球:显示统计数据
return <BasketballStats data={data} isDark={isDark} />;
} else {
// 其他运动暂时显示空状态
return (
<View style={styles.emptyContent}>
<ThemedText style={styles.emptyText}>
{t("detail.empty_stats")}
</ThemedText>
</View>
);
}
case "overall":
return <BasketballOverallStats data={data} isDark={isDark} />;
case "odds":
// 将 MatchDetailData.match 转换为 LiveScoreMatch 格式
const matchForOdds = {
event_key: parseInt(data.match.eventKey) || 0,
event_date: data.match.eventDate,
event_time: data.match.eventTime,
event_home_team: data.match.eventHomeTeam,
home_team_key: parseInt(data.match.homeTeamKey) || 0,
home_team_logo: data.match.homeTeamLogo,
event_away_team: data.match.eventAwayTeam,
away_team_key: parseInt(data.match.awayTeamKey) || 0,
away_team_logo: data.match.awayTeamLogo,
event_final_result: data.match.eventFinalResult,
event_halftime_result: data.match.eventHalftimeResult,
event_status: data.match.eventStatus,
event_live: data.match.eventLive,
league_key: parseInt(data.match.leagueKey) || 0,
league_name: data.match.leagueName,
league_logo: data.match.leagueLogo,
league_round: data.match.leagueRound,
league_season: data.match.leagueSeason,
country_name: data.match.countryName,
country_logo: data.match.countryLogo,
event_country_key: parseInt(data.match.eventCountryKey) || 0,
};
return (
<OddsCard sportId={sportId} match={matchForOdds} isDark={isDark} />
);
case "h2h":
if (sportId === 4) {
return <CricketH2H data={data} isDark={isDark} />;
}
return <H2H data={data} isDark={isDark} />;
case "chat":
return (
<View style={styles.emptyContent}>
<ThemedText style={styles.emptyText}>
{t("detail.empty_chat")}
</ThemedText>
</View>
);
default:
return null;
}
};
return (
<ThemedView style={styles.container}>
<ScrollView
bounces={false}
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: insets.bottom + 20 }}
>
<ScoreHeader data={data} isDark={isDark} topInset={insets.top} />
<LeagueInfo data={data} isDark={isDark} />
<MatchTabs
activeTab={activeTab}
onTabChange={setActiveTab}
isDark={isDark}
sportId={data.match.sportId}
/>
{renderTabContent()}
</ScrollView>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
center: {
flex: 1,
justifyContent: "center",
alignItems: "center",
padding: 20,
},
errorText: {
fontSize: 16,
marginBottom: 20,
textAlign: "center",
},
retryButton: {
paddingHorizontal: 20,
paddingVertical: 10,
backgroundColor: "#007AFF",
borderRadius: 8,
},
retryText: {
color: "#FFF",
fontWeight: "600",
},
emptyContent: {
padding: 50,
alignItems: "center",
},
emptyText: {
opacity: 0.5,
},
});