281 lines
7.8 KiB
TypeScript
281 lines
7.8 KiB
TypeScript
import { ThemedText } from "@/components/themed-text";
|
||
import { IconSymbol } from "@/components/ui/icon-symbol";
|
||
import { useTheme } from "@/context/ThemeContext";
|
||
import { addFavorite, removeFavorite } from "@/lib/api";
|
||
import { Match } from "@/types/api";
|
||
import { useRouter } from "expo-router";
|
||
import React, { useState } from "react";
|
||
import { Image, Pressable, StyleSheet, TouchableOpacity, View } from "react-native";
|
||
|
||
interface MatchCardLeagueProps {
|
||
match: Match;
|
||
onPress?: (match: Match) => void;
|
||
onFavoriteToggle?: (matchId: string, isFav: boolean) => void;
|
||
}
|
||
|
||
export function MatchCardLeague({
|
||
match,
|
||
onPress,
|
||
onFavoriteToggle,
|
||
}: MatchCardLeagueProps) {
|
||
const router = useRouter();
|
||
const { theme } = useTheme();
|
||
const [isFav, setIsFav] = useState(match.fav);
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
React.useEffect(() => {
|
||
setIsFav(match.fav);
|
||
}, [match.fav]);
|
||
|
||
const isDark = theme === "dark";
|
||
// 截图中的卡片背景通常非常深,接近纯黑
|
||
const cardBg = isDark ? "#1C1C1E" : "#FFFFFF";
|
||
const textColor = isDark ? "#FFFFFF" : "#000000";
|
||
// 赢家的高亮颜色 (截图中的橙黄色)
|
||
const winnerColor = "#FF9500";
|
||
const loserColor = isDark ? "#FFFFFF" : "#000000";
|
||
|
||
const handlePress = () => {
|
||
if (onPress) {
|
||
onPress(match);
|
||
} else {
|
||
router.push(`/match-detail/${match.id}`);
|
||
}
|
||
};
|
||
|
||
const toggleFavorite = async () => {
|
||
if (loading) return;
|
||
setLoading(true);
|
||
const newFavState = !isFav;
|
||
try {
|
||
if (newFavState) {
|
||
await addFavorite({
|
||
matchId: parseInt(match.id),
|
||
type: "match",
|
||
typeId: match.id,
|
||
notify: true,
|
||
});
|
||
} else {
|
||
await removeFavorite({
|
||
type: "match",
|
||
typeId: match.id,
|
||
});
|
||
}
|
||
setIsFav(newFavState);
|
||
if (onFavoriteToggle) {
|
||
onFavoriteToggle(match.id, newFavState);
|
||
}
|
||
} catch (error) {
|
||
console.error("Toggle favorite error:", error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
// --- 数据解析与样式逻辑 ---
|
||
|
||
// 假设 match 对象中有 homeScore 和 awayScore (数字或字符串)
|
||
// 如果 API 只有 "1 - 0" 这种 scoreText,你需要在这里拆分
|
||
// 这里为了演示,假设 match 对象已经扩展了这些字段,或者我们从 scoreText 简单解析
|
||
let homeScore = 0;
|
||
let awayScore = 0;
|
||
|
||
// 简单的解析逻辑 demo (根据你的实际数据结构调整)
|
||
if (match.scoreText && match.scoreText.includes("-")) {
|
||
const parts = match.scoreText.split("-");
|
||
homeScore = parseInt(parts[0].trim()) || 0;
|
||
awayScore = parseInt(parts[1].trim()) || 0;
|
||
}
|
||
// 如果 match 对象里直接有 match.homeScore 最好:
|
||
// homeScore = match.homeScore;
|
||
// awayScore = match.awayScore;
|
||
|
||
// 判断文字颜色和背景样式
|
||
let homeColor = loserColor;
|
||
let awayColor = loserColor;
|
||
let homeScoreBg = "#2C2C2E"; // 默认深灰背景
|
||
let awayScoreBg = "#2C2C2E";
|
||
let homeBorderColor = "transparent";
|
||
let awayBorderColor = "transparent";
|
||
|
||
if (homeScore > awayScore) {
|
||
homeColor = "#000000"; // 赢家黑色文字
|
||
homeScoreBg = winnerColor; // 金色背景
|
||
homeBorderColor = winnerColor;
|
||
} else if (awayScore > homeScore) {
|
||
awayColor = "#000000"; // 赢家黑色文字
|
||
awayScoreBg = winnerColor; // 金色背景
|
||
awayBorderColor = winnerColor;
|
||
}
|
||
// 如果相等,保持默认深灰背景
|
||
|
||
return (
|
||
<Pressable
|
||
onPress={handlePress}
|
||
style={({ pressed }) => [
|
||
styles.card,
|
||
{ backgroundColor: cardBg, opacity: pressed ? 0.8 : 1 },
|
||
]}
|
||
>
|
||
{/* 1. 左侧:时间/状态 */}
|
||
<View style={styles.leftColumn}>
|
||
{/* 如果有状态字段 match.meta (如 'FT', 'AET'), 优先显示,否则显示时间 */}
|
||
<ThemedText style={styles.statusText}>
|
||
{(match.meta || match.time || "").toUpperCase()}
|
||
</ThemedText>
|
||
</View>
|
||
|
||
{/* 2. 中间:球队信息 (上下排布) */}
|
||
<View style={styles.teamsColumn}>
|
||
{/* 主队行 */}
|
||
<View style={styles.teamRow}>
|
||
<Image
|
||
source={{ uri: (match as any).homeLogo || match.homeTeamLogo || "https://placehold.co/24x24/png" }}
|
||
style={styles.teamLogo}
|
||
/>
|
||
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
||
{match.home || match.homeTeamName}
|
||
</ThemedText>
|
||
</View>
|
||
|
||
{/* 客队行 */}
|
||
<View style={[styles.teamRow, { marginTop: 6 }]}>
|
||
<Image
|
||
source={{ uri: (match as any).awayLogo || match.awayTeamLogo || "https://placehold.co/24x24/png" }}
|
||
style={styles.teamLogo}
|
||
/>
|
||
<ThemedText style={[styles.teamName, { color: textColor }]} numberOfLines={1}>
|
||
{match.away || match.awayTeamName}
|
||
</ThemedText>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 3. 右侧:比分与铃铛 */}
|
||
<View style={styles.rightWrapper}>
|
||
{/* 比分列 (上下排布) */}
|
||
<View style={styles.scoresColumn}>
|
||
<View style={[
|
||
styles.scoreBox,
|
||
{
|
||
backgroundColor: homeScoreBg,
|
||
borderColor: homeBorderColor,
|
||
borderWidth: homeBorderColor !== "transparent" ? 1.5 : 0,
|
||
}
|
||
]}>
|
||
<ThemedText style={[styles.scoreText, { color: homeColor }]}>
|
||
{homeScore}
|
||
</ThemedText>
|
||
</View>
|
||
<View style={[
|
||
styles.scoreBox,
|
||
{
|
||
backgroundColor: awayScoreBg,
|
||
borderColor: awayBorderColor,
|
||
borderWidth: awayBorderColor !== "transparent" ? 1.5 : 0,
|
||
marginTop: 6,
|
||
}
|
||
]}>
|
||
<ThemedText style={[styles.scoreText, { color: awayColor }]}>
|
||
{awayScore}
|
||
</ThemedText>
|
||
</View>
|
||
</View>
|
||
|
||
{/* 收藏按钮 */}
|
||
<TouchableOpacity
|
||
onPress={(e) => {
|
||
e.stopPropagation();
|
||
toggleFavorite();
|
||
}}
|
||
disabled={loading}
|
||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||
style={styles.favoriteButton}
|
||
>
|
||
<IconSymbol
|
||
name={isFav ? "star" : "star-outline"}
|
||
size={20}
|
||
color={isFav ? "#FFD700" : "#545458"}
|
||
/>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</Pressable>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
card: {
|
||
flexDirection: "row",
|
||
paddingVertical: 14,
|
||
paddingHorizontal: 16,
|
||
marginHorizontal: 0,
|
||
marginBottom: 0,
|
||
borderRadius: 12,
|
||
alignItems: "center",
|
||
minHeight: 68,
|
||
},
|
||
// 左侧时间列
|
||
leftColumn: {
|
||
width: 52,
|
||
justifyContent: "center",
|
||
alignItems: "flex-start",
|
||
marginRight: 12,
|
||
},
|
||
statusText: {
|
||
fontSize: 12,
|
||
color: "#8E8E93", // 次要文本颜色 (Grey)
|
||
fontWeight: "600",
|
||
},
|
||
// 中间球队列
|
||
teamsColumn: {
|
||
flex: 1,
|
||
justifyContent: "center",
|
||
paddingRight: 8,
|
||
},
|
||
teamRow: {
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
},
|
||
teamLogo: {
|
||
width: 22,
|
||
height: 22,
|
||
borderRadius: 11, // 圆形图标
|
||
marginRight: 10,
|
||
backgroundColor: "#3A3A3C", // 图片加载占位
|
||
},
|
||
teamName: {
|
||
fontSize: 16,
|
||
fontWeight: "500",
|
||
flex: 1,
|
||
},
|
||
// 右侧整体包装
|
||
rightWrapper: {
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
},
|
||
// 比分列
|
||
scoresColumn: {
|
||
alignItems: "flex-end", // 数字右对齐
|
||
justifyContent: "center",
|
||
marginRight: 12,
|
||
},
|
||
scoreBox: {
|
||
minWidth: 32,
|
||
height: 28,
|
||
borderRadius: 6,
|
||
justifyContent: "center",
|
||
alignItems: "center",
|
||
paddingHorizontal: 8,
|
||
},
|
||
scoreText: {
|
||
fontSize: 16,
|
||
fontWeight: "700",
|
||
lineHeight: 20,
|
||
},
|
||
// 收藏按钮
|
||
favoriteButton: {
|
||
paddingHorizontal: 4,
|
||
paddingVertical: 4,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
}
|
||
}); |