298 lines
10 KiB
TypeScript
298 lines
10 KiB
TypeScript
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,
|
||
},
|
||
});
|