268 lines
7.1 KiB
TypeScript
268 lines
7.1 KiB
TypeScript
import { MatchCardLeague } from "@/components/match-card-league";
|
|
import { ThemedText } from "@/components/themed-text";
|
|
import { useTheme } from "@/context/ThemeContext";
|
|
import { Match } from "@/types/api";
|
|
import React, { useState } from "react";
|
|
import {
|
|
Image,
|
|
LayoutAnimation,
|
|
Platform,
|
|
ScrollView,
|
|
StyleSheet,
|
|
TouchableOpacity,
|
|
UIManager,
|
|
View,
|
|
} from "react-native";
|
|
|
|
// 开启 Android 上的 LayoutAnimation
|
|
if (
|
|
Platform.OS === "android" &&
|
|
UIManager.setLayoutAnimationEnabledExperimental
|
|
) {
|
|
UIManager.setLayoutAnimationEnabledExperimental(true);
|
|
}
|
|
|
|
interface MatchesByLeagueProps {
|
|
matches: Match[];
|
|
onFavoriteToggle?: (matchId: string, isFav: boolean) => void;
|
|
/**
|
|
* 是否支持折叠收起
|
|
* @default true
|
|
*/
|
|
enableCollapsible?: boolean;
|
|
}
|
|
|
|
|
|
export function MatchesByLeague({
|
|
matches,
|
|
onFavoriteToggle,
|
|
enableCollapsible = true,
|
|
}: MatchesByLeagueProps) {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === "dark";
|
|
|
|
// 数据分组逻辑
|
|
const matchesByLeague = React.useMemo(() => {
|
|
const grouped: Record<string, Match[]> = {};
|
|
matches.forEach((match) => {
|
|
const league = match.league || match.leagueName || "其他";
|
|
if (!grouped[league]) {
|
|
grouped[league] = [];
|
|
}
|
|
grouped[league].push(match);
|
|
});
|
|
return grouped;
|
|
}, [matches]);
|
|
|
|
const leagueNames = Object.keys(matchesByLeague);
|
|
|
|
// 状态:记录哪些联赛被折叠了 (Key 为联赛名称),默认全部收起
|
|
const [collapsedSections, setCollapsedSections] = useState<
|
|
Record<string, boolean>
|
|
>({});
|
|
|
|
// 当联赛列表变化时,确保所有联赛默认都是收起状态
|
|
React.useEffect(() => {
|
|
setCollapsedSections((prev) => {
|
|
const updated: Record<string, boolean> = {};
|
|
leagueNames.forEach((name) => {
|
|
updated[name] = prev[name] !== undefined ? prev[name] : true;
|
|
});
|
|
return updated;
|
|
});
|
|
}, [leagueNames.join(",")]);
|
|
|
|
// 切换折叠状态
|
|
const toggleCollapse = (leagueName: string) => {
|
|
if (!enableCollapsible) return;
|
|
|
|
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
|
|
setCollapsedSections((prev) => ({
|
|
...prev,
|
|
[leagueName]: !prev[leagueName],
|
|
}));
|
|
};
|
|
|
|
if (leagueNames.length === 0) {
|
|
return (
|
|
<View style={styles.emptyContainer}>
|
|
<ThemedText>暂无比赛</ThemedText>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<ScrollView
|
|
style={[
|
|
styles.container,
|
|
{ backgroundColor: isDark ? "#000000" : "#F2F2F7" }
|
|
]}
|
|
contentContainerStyle={{ paddingBottom: 40 }}
|
|
>
|
|
{leagueNames.map((leagueName) => {
|
|
const leagueMatches = matchesByLeague[leagueName];
|
|
// 取该组第一场比赛的数据作为头部信息的来源(图标、国家等)
|
|
const firstMatch = leagueMatches[0];
|
|
const isCollapsed = collapsedSections[leagueName];
|
|
|
|
return (
|
|
<View key={leagueName} style={styles.leagueSection}>
|
|
{/* 联赛头部 */}
|
|
<TouchableOpacity
|
|
activeOpacity={enableCollapsible ? 0.7 : 1}
|
|
onPress={() => toggleCollapse(leagueName)}
|
|
style={styles.leagueHeaderWrapper}
|
|
>
|
|
<View style={styles.leagueHeaderLeft}>
|
|
{/* 联赛 Logo */}
|
|
<Image
|
|
source={{
|
|
uri: firstMatch.leagueLogo || (firstMatch as any).leagueLogoUrl || "https://placehold.co/40x40/png",
|
|
}}
|
|
style={styles.leagueLogo}
|
|
/>
|
|
|
|
<View style={styles.leagueInfoText}>
|
|
{/* 联赛名称 */}
|
|
<ThemedText style={styles.leagueTitle}>{leagueName}</ThemedText>
|
|
|
|
{/* 国家信息行 */}
|
|
<View style={styles.countryRow}>
|
|
<Image
|
|
source={{ uri: firstMatch.countryLogo || (firstMatch as any).countryFlagUrl || "https://placehold.co/20x20/png" }}
|
|
style={styles.countryFlag}
|
|
/>
|
|
<ThemedText style={styles.countryName}>
|
|
{firstMatch.countryName || (firstMatch as any).countryName || "International"}
|
|
</ThemedText>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
<View style={styles.leagueHeaderRight}>
|
|
{/* 比赛数量 */}
|
|
<ThemedText style={styles.matchCount}>
|
|
{leagueMatches.length}
|
|
</ThemedText>
|
|
|
|
{/* 折叠箭头 (仅当支持折叠时显示) */}
|
|
{enableCollapsible && (
|
|
<ThemedText style={styles.chevron}>
|
|
{isCollapsed ? "⌄" : "⌃"}
|
|
</ThemedText>
|
|
)}
|
|
</View>
|
|
</TouchableOpacity>
|
|
|
|
{/* 比赛列表内容 (根据状态显示/隐藏) */}
|
|
{!isCollapsed && (
|
|
<View style={styles.matchListContainer}>
|
|
{leagueMatches.map((match, index) => (
|
|
<View
|
|
key={match.id}
|
|
style={[
|
|
styles.matchCardWrapper,
|
|
index < leagueMatches.length - 1 && styles.matchCardDivider
|
|
]}
|
|
>
|
|
<MatchCardLeague
|
|
match={match}
|
|
onFavoriteToggle={onFavoriteToggle}
|
|
/>
|
|
</View>
|
|
))}
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
})}
|
|
</ScrollView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
leagueSection: {
|
|
marginBottom: 16,
|
|
},
|
|
leagueHeaderWrapper: {
|
|
flexDirection: "row",
|
|
justifyContent: "space-between",
|
|
alignItems: "center",
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
backgroundColor: "transparent",
|
|
},
|
|
leagueHeaderLeft: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
flex: 1,
|
|
},
|
|
leagueLogo: {
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 6,
|
|
marginRight: 12,
|
|
backgroundColor: "#3A3A3C",
|
|
},
|
|
leagueInfoText: {
|
|
justifyContent: "center",
|
|
},
|
|
leagueTitle: {
|
|
fontSize: 17,
|
|
fontWeight: "600",
|
|
color: "#FFFFFF",
|
|
marginBottom: 3,
|
|
},
|
|
countryRow: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
},
|
|
countryFlag: {
|
|
width: 16,
|
|
height: 12,
|
|
marginRight: 5,
|
|
borderRadius: 2,
|
|
},
|
|
countryName: {
|
|
fontSize: 13,
|
|
color: "#8E8E93",
|
|
fontWeight: "500",
|
|
},
|
|
leagueHeaderRight: {
|
|
flexDirection: "row",
|
|
alignItems: "center",
|
|
gap: 8,
|
|
},
|
|
matchCount: {
|
|
fontSize: 15,
|
|
color: "#8E8E93",
|
|
fontWeight: "600",
|
|
},
|
|
chevron: {
|
|
fontSize: 16,
|
|
color: "#8E8E93",
|
|
fontWeight: '600',
|
|
},
|
|
matchListContainer: {
|
|
backgroundColor: "#1C1C1E",
|
|
borderRadius: 12,
|
|
marginHorizontal: 16,
|
|
overflow: "hidden",
|
|
},
|
|
matchCardWrapper: {
|
|
// 卡片包装器
|
|
},
|
|
matchCardDivider: {
|
|
borderBottomWidth: 0.5,
|
|
borderBottomColor: "#3A3A3C",
|
|
},
|
|
emptyContainer: {
|
|
flex: 1,
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
paddingTop: 100,
|
|
},
|
|
}); |