Files
physical-expo/components/match-detail/h2h.tsx
2026-01-15 15:36:41 +08:00

377 lines
10 KiB
TypeScript

import { ThemedText } from "@/components/themed-text";
import { fetchH2H } from "@/lib/api";
import { H2HData, H2HMatch, MatchDetailData } from "@/types/api";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import {
ActivityIndicator,
StyleSheet,
TouchableOpacity,
View,
} from "react-native";
interface H2HProps {
data: MatchDetailData;
isDark: boolean;
}
export function H2H({ data, isDark }: H2HProps) {
const { t } = useTranslation();
const { match } = data;
const [h2hData, setH2hData] = useState<H2HData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [activeSection, setActiveSection] = useState<"h2h" | "first" | "second">("h2h");
const bgColor = isDark ? "#1C1C1E" : "#FFF";
const borderColor = isDark ? "rgba(150,150,150,0.2)" : "rgba(150,150,150,0.2)";
// 颜色常量配置
const colors = {
bg: isDark ? "#000000" : "#F8F8F8",
card: bgColor,
border: borderColor,
textMain: isDark ? "#FFFFFF" : "#000000",
textSecondary: isDark ? "#8E8E93" : "#8E8E93",
win: "#4CAF50",
loss: "#F44336",
draw: "#2196F3",
scoreHighlight: "#FFB800",
};
useEffect(() => {
loadH2H();
}, []);
const loadH2H = async () => {
try {
setLoading(true);
setError(null);
const sportId = match.sportId;
const options: any = {};
if (sportId === 3) {
options.firstPlayerId = parseInt(match.homeTeamKey);
options.secondPlayerId = parseInt(match.awayTeamKey);
} else {
options.firstTeamId = parseInt(match.homeTeamKey);
options.secondTeamId = parseInt(match.awayTeamKey);
}
const result = await fetchH2H(sportId, options);
setH2hData(result);
} catch (err: any) {
setError(err?.message || t("detail.h2h.error"));
} finally {
setLoading(false);
}
};
// 核心逻辑:计算胜负状态
const getMatchResult = (item: H2HMatch) => {
const homeScore = parseInt(item.event_final_result?.split("-")[0] || "0");
const awayScore = parseInt(item.event_final_result?.split("-")[1] || "0");
// 确定当前视角球队名称
const perspectiveTeam = activeSection === "first" ? match.eventHomeTeam :
activeSection === "second" ? match.eventAwayTeam : null;
if (!perspectiveTeam) return { label: "", color: "transparent" };
const isHome = item.event_home_team === perspectiveTeam;
if (homeScore === awayScore) return { label: "D", color: colors.draw };
const isWin = isHome ? homeScore > awayScore : awayScore > homeScore;
return isWin ? { label: "W", color: colors.win } : { label: "L", color: colors.loss };
};
const renderMatchItem = ({ item, index }: { item: H2HMatch; index: number }) => {
const homeScore = parseInt(item.event_final_result?.split("-")[0] || "0");
const awayScore = parseInt(item.event_final_result?.split("-")[1] || "0");
const { label, color } = getMatchResult(item);
const matchYear = item.event_date?.split("-")[0] || "";
return (
<View
key={item.event_key ?? index}
style={[styles.matchRow, { borderBottomColor: borderColor }]}
>
{/* 左侧:日期状态 */}
<View style={styles.leftCol}>
<ThemedText style={styles.yearText}>{matchYear}</ThemedText>
<ThemedText style={styles.ftText}>FT</ThemedText>
</View>
{/* 中间:球队对阵 */}
<View style={styles.centerCol}>
<View style={styles.teamLine}>
<ThemedText
style={[
styles.teamName,
homeScore > awayScore && [
styles.boldText,
{ color: isDark ? "#FFF" : "#000" }
]
]}
numberOfLines={1}
>
{item.event_home_team}
</ThemedText>
</View>
<View style={styles.teamLine}>
<ThemedText
style={[
styles.teamName,
awayScore > homeScore && [
styles.boldText,
{ color: isDark ? "#FFF" : "#000" }
]
]}
numberOfLines={1}
>
{item.event_away_team}
</ThemedText>
</View>
</View>
{/* 右侧:比分与胜负角标 */}
<View style={styles.rightCol}>
<View style={styles.scoreContainer}>
<ThemedText style={[styles.scoreText, homeScore > awayScore && { color: colors.scoreHighlight }]}>
{homeScore}
</ThemedText>
<ThemedText style={[styles.scoreText, awayScore > homeScore && { color: colors.scoreHighlight }]}>
{awayScore}
</ThemedText>
</View>
{label !== "" && (
<View style={[styles.resultBadge, { backgroundColor: color }]}>
<ThemedText style={styles.resultBadgeText}>{label}</ThemedText>
</View>
)}
</View>
</View>
);
};
// 状态处理 (Loading, Error, Empty) 保持原样但应用新背景色
if (loading) {
return (
<View style={[styles.container, { backgroundColor: colors.card }]}>
<View style={styles.loadingContainer}>
<ActivityIndicator size="small" color={isDark ? "#FFF" : "#000"} />
<ThemedText style={styles.loadingText}>
{t("detail.h2h.loading")}
</ThemedText>
</View>
</View>
);
}
if (error) {
return (
<View style={[styles.container, { backgroundColor: colors.card }]}>
<View style={styles.errorContainer}>
<ThemedText style={styles.errorText}>{error}</ThemedText>
<TouchableOpacity
style={[styles.retryButton, { backgroundColor: isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.05)" }]}
onPress={loadH2H}
>
<ThemedText style={styles.retryText}>
{t("detail.h2h.retry")}
</ThemedText>
</TouchableOpacity>
</View>
</View>
);
}
if (!h2hData) {
return (
<View style={[styles.container, { backgroundColor: colors.card }]}>
<View style={styles.emptyContainer}>
<ThemedText style={styles.emptyText}>
{t("detail.h2h.no_data")}
</ThemedText>
</View>
</View>
);
}
const currentData = activeSection === "h2h" ? h2hData?.H2H :
activeSection === "first" ? h2hData?.firstTeamResults : h2hData?.secondTeamResults;
return (
<View style={[styles.container, { backgroundColor: colors.card }]}>
{/* 选项卡 */}
<View style={[styles.tabs, { borderBottomColor: borderColor }]}>
{[
{ id: "h2h", label: t("detail.h2h.h2h") },
{ id: "first", label: match.eventHomeTeam },
{ id: "second", label: match.eventAwayTeam }
].map((tab) => (
<TouchableOpacity
key={tab.id}
style={[styles.tab, activeSection === tab.id && styles.tabActive, { borderBottomColor: activeSection === tab.id ? colors.scoreHighlight : 'transparent' }]}
onPress={() => setActiveSection(tab.id as any)}
>
<ThemedText style={[styles.tabText, activeSection === tab.id && styles.tabTextActive]}>
{tab.label}
</ThemedText>
</TouchableOpacity>
))}
</View>
{/* 列表内容 */}
<View style={styles.listContent}>
{currentData && currentData.length > 0 ? (
currentData.map((item, index) => renderMatchItem({ item, index }))
) : (
<ThemedText style={styles.emptyText}>
{t("detail.h2h.no_data")}
</ThemedText>
)}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
margin: 16,
marginTop: 0,
borderRadius: 12,
padding: 16,
elevation: 2,
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
loadingContainer: {
padding: 40,
alignItems: "center",
gap: 12,
},
loadingText: {
fontSize: 14,
opacity: 0.6,
},
errorContainer: {
padding: 40,
alignItems: "center",
gap: 16,
},
errorText: {
fontSize: 14,
opacity: 0.7,
textAlign: "center",
},
retryButton: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
},
retryText: {
fontSize: 13,
fontWeight: "600",
},
emptyContainer: {
padding: 40,
alignItems: "center",
},
tabs: {
flexDirection: "row",
marginBottom: 12,
paddingBottom: 12,
borderBottomWidth: StyleSheet.hairlineWidth,
},
tab: {
paddingVertical: 12,
marginRight: 20,
borderBottomWidth: 2,
},
tabActive: {},
tabText: {
fontSize: 14,
opacity: 0.6,
},
tabTextActive: {
fontWeight: "bold",
opacity: 1,
},
listContent: {
paddingBottom: 20,
},
matchRow: {
flexDirection: "row",
paddingVertical: 12,
borderBottomWidth: StyleSheet.hairlineWidth,
alignItems: "center",
},
leftCol: {
width: 50,
alignItems: "flex-start",
},
yearText: {
fontSize: 12,
color: "#8E8E93",
},
ftText: {
fontSize: 11,
fontWeight: "bold",
marginTop: 2,
color: "#666",
},
centerCol: {
flex: 1,
paddingLeft: 4,
},
teamLine: {
height: 24,
justifyContent: "center",
},
teamName: {
fontSize: 14,
color: "#BBB",
},
boldText: {
fontWeight: "600",
},
rightCol: {
flexDirection: "row",
alignItems: "center",
},
scoreContainer: {
backgroundColor: "#1C1C1E",
borderRadius: 6,
paddingHorizontal: 10,
paddingVertical: 4,
minWidth: 40,
alignItems: "center",
marginRight: 10,
},
scoreText: {
fontSize: 14,
fontWeight: "bold",
lineHeight: 20,
color: "#FFF",
},
resultBadge: {
width: 22,
height: 38,
borderRadius: 4,
justifyContent: "center",
alignItems: "center",
},
resultBadgeText: {
color: "#FFF",
fontSize: 13,
fontWeight: "bold",
},
emptyText: {
textAlign: "center",
marginTop: 40,
opacity: 0.5,
},
});