篮球整体统计数据
This commit is contained in:
285
components/match-detail/basketball/basketball-overall-stats.tsx
Normal file
285
components/match-detail/basketball/basketball-overall-stats.tsx
Normal file
@@ -0,0 +1,285 @@
|
||||
import { ThemedText } from "@/components/themed-text";
|
||||
import { MatchDetailData } from "@/types/api";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
|
||||
interface BasketballOverallStatsProps {
|
||||
data: MatchDetailData;
|
||||
isDark: boolean;
|
||||
}
|
||||
|
||||
type StatItem = {
|
||||
type: string;
|
||||
home: string | number;
|
||||
away: string | number;
|
||||
};
|
||||
|
||||
function toNumber(v: unknown): number {
|
||||
if (typeof v === "number") return Number.isFinite(v) ? v : 0;
|
||||
if (typeof v === "string") {
|
||||
const cleaned = v.trim().replace("%", "");
|
||||
const n = Number(cleaned);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function BasketballOverallStats({ data, isDark }: BasketballOverallStatsProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
useEffect(() => {
|
||||
console.log("=== Basketball Overall Stats Loaded ===");
|
||||
console.log(JSON.stringify(data.match.stats, null, 2));
|
||||
}, [data.match.stats]);
|
||||
|
||||
const stats = (data.match.stats || []) as StatItem[];
|
||||
|
||||
const getStatLabel = (type: string): string => {
|
||||
const key = `detail.overall_stats.${type.toLowerCase().replace(/\s+/g, "_")}`;
|
||||
const translated = t(key);
|
||||
return translated !== key ? translated : type;
|
||||
};
|
||||
|
||||
if (!Array.isArray(stats) || stats.length === 0) {
|
||||
return (
|
||||
<View style={styles.empty}>
|
||||
<ThemedText style={[styles.emptyText, { color: isDark ? "#A1A1AA" : "#6B7280" }]}>
|
||||
{t("detail.empty_stats")}
|
||||
</ThemedText>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
styles.wrap,
|
||||
{ backgroundColor: isDark ? "#151517" : "#F3F4F6" },
|
||||
]}
|
||||
>
|
||||
{/* Card */}
|
||||
<View
|
||||
style={[
|
||||
styles.card,
|
||||
{ backgroundColor: isDark ? "#1C1C1E" : "#FFFFFF" },
|
||||
]}
|
||||
>
|
||||
{stats.map((item, index) => {
|
||||
const home = toNumber(item.home);
|
||||
const away = toNumber(item.away);
|
||||
const maxV = Math.max(home, away, 1);
|
||||
|
||||
const homeLeading = home >= away;
|
||||
const awayLeading = away > home;
|
||||
|
||||
return (
|
||||
<View
|
||||
key={`${item.type}-${index}`}
|
||||
style={[
|
||||
styles.row,
|
||||
index !== stats.length - 1 && {
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)",
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={styles.rowHeader}>
|
||||
<ValuePill
|
||||
text={`${home}`}
|
||||
variant="home"
|
||||
isDark={isDark}
|
||||
/>
|
||||
|
||||
<ThemedText
|
||||
style={[
|
||||
styles.title,
|
||||
{ color: isDark ? "#EAEAF0" : "#111827" },
|
||||
]}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{getStatLabel(item.type)}
|
||||
</ThemedText>
|
||||
|
||||
<ValuePill
|
||||
text={`${away}`}
|
||||
variant={awayLeading ? "awayLeading" : "away"}
|
||||
isDark={isDark}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<CompareBar
|
||||
home={home}
|
||||
away={away}
|
||||
maxV={maxV}
|
||||
isDark={isDark}
|
||||
homeLeading={homeLeading}
|
||||
awayLeading={awayLeading}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function ValuePill({
|
||||
text,
|
||||
variant,
|
||||
isDark,
|
||||
}: {
|
||||
text: string;
|
||||
variant: "home" | "away" | "awayLeading";
|
||||
isDark: boolean;
|
||||
}) {
|
||||
const common = {
|
||||
start: { x: 0, y: 0 },
|
||||
end: { x: 1, y: 0 },
|
||||
style: styles.pill,
|
||||
} as const;
|
||||
|
||||
// 蓝色:主队/默认;金色:客队领先
|
||||
const blue = isDark
|
||||
? ["#0B2A73", "#0E4BFF"]
|
||||
: ["#1D4ED8", "#2563EB"];
|
||||
const gold = isDark
|
||||
? ["#3A2A00", "#C08B00"]
|
||||
: ["#B45309", "#D97706"];
|
||||
|
||||
const colors =
|
||||
variant === "awayLeading"
|
||||
? gold
|
||||
: blue;
|
||||
|
||||
return (
|
||||
<LinearGradient colors={[...colors] as [string, string]} {...common}>
|
||||
<ThemedText style={styles.pillText}>{text}</ThemedText>
|
||||
</LinearGradient>
|
||||
);
|
||||
}
|
||||
|
||||
function CompareBar({
|
||||
home,
|
||||
away,
|
||||
maxV,
|
||||
isDark,
|
||||
homeLeading,
|
||||
awayLeading,
|
||||
}: {
|
||||
home: number;
|
||||
away: number;
|
||||
maxV: number;
|
||||
isDark: boolean;
|
||||
homeLeading: boolean;
|
||||
awayLeading: boolean;
|
||||
}) {
|
||||
const SIDE_MAX = 150;
|
||||
const GAP = 10;
|
||||
|
||||
const homeW = Math.max(2, Math.round((home / maxV) * SIDE_MAX));
|
||||
const awayW = Math.max(2, Math.round((away / maxV) * SIDE_MAX));
|
||||
|
||||
const track = isDark ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.10)";
|
||||
const muted = isDark ? "rgba(255,255,255,0.18)" : "rgba(0,0,0,0.18)";
|
||||
const blue = "#1F5BFF";
|
||||
const gold = "#C08B00";
|
||||
|
||||
const homeColor = homeLeading ? blue : muted;
|
||||
const awayColor = awayLeading ? gold : muted;
|
||||
|
||||
return (
|
||||
<View style={[styles.barWrap, { width: SIDE_MAX * 2 + GAP }]}>
|
||||
<View style={[styles.barTrack, { backgroundColor: track }]} />
|
||||
<View style={[styles.barSide, { width: SIDE_MAX, justifyContent: "flex-end" }]}>
|
||||
<View style={[styles.barFill, { width: homeW, backgroundColor: homeColor }]} />
|
||||
</View>
|
||||
<View style={{ width: GAP }} />
|
||||
<View style={[styles.barSide, { width: SIDE_MAX, justifyContent: "flex-start" }]}>
|
||||
<View style={[styles.barFill, { width: awayW, backgroundColor: awayColor }]} />
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/* ----------------- Styles ----------------- */
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
wrap: {
|
||||
paddingHorizontal: 14,
|
||||
paddingTop: 12,
|
||||
paddingBottom: 16,
|
||||
},
|
||||
card: {
|
||||
borderRadius: 18,
|
||||
paddingVertical: 6,
|
||||
overflow: "hidden",
|
||||
},
|
||||
|
||||
row: {
|
||||
paddingHorizontal: 14,
|
||||
paddingVertical: 14,
|
||||
},
|
||||
rowHeader: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
},
|
||||
|
||||
pill: {
|
||||
minWidth: 54,
|
||||
height: 30,
|
||||
borderRadius: 16,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
pillText: {
|
||||
color: "#FFFFFF",
|
||||
fontSize: 14,
|
||||
fontWeight: "800",
|
||||
letterSpacing: 0.3,
|
||||
},
|
||||
|
||||
title: {
|
||||
flex: 1,
|
||||
textAlign: "center",
|
||||
fontSize: 15,
|
||||
fontWeight: "700",
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
|
||||
barWrap: {
|
||||
alignSelf: "center",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
marginTop: 10,
|
||||
height: 8,
|
||||
position: "relative",
|
||||
},
|
||||
barTrack: {
|
||||
position: "absolute",
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: 6,
|
||||
borderRadius: 999,
|
||||
},
|
||||
barSide: {
|
||||
height: 8,
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
},
|
||||
barFill: {
|
||||
height: 6,
|
||||
borderRadius: 999,
|
||||
},
|
||||
|
||||
empty: {
|
||||
padding: 40,
|
||||
alignItems: "center",
|
||||
},
|
||||
emptyText: {
|
||||
fontSize: 14,
|
||||
opacity: 0.85,
|
||||
},
|
||||
});
|
||||
@@ -63,7 +63,7 @@ export function MatchTabs({
|
||||
},
|
||||
];
|
||||
} else if (sportId === 2) {
|
||||
// 篮球: 详情、统计数据、交锋往绩、聊天
|
||||
// 篮球: 详情、统计数据、整体统计、交锋往绩、聊天
|
||||
return [
|
||||
{
|
||||
id: "info",
|
||||
@@ -75,6 +75,11 @@ export function MatchTabs({
|
||||
label: t("detail.tabs.stats"),
|
||||
icon: "stats-chart-outline",
|
||||
},
|
||||
{
|
||||
id: "overall",
|
||||
label: t("detail.tabs.overall"),
|
||||
icon: "bar-chart-outline",
|
||||
},
|
||||
{
|
||||
id: "h2h",
|
||||
label: t("detail.tabs.h2h"),
|
||||
|
||||
Reference in New Issue
Block a user