diff --git a/app/live-detail/[id].tsx b/app/live-detail/[id].tsx
index 6219dcd..e661e01 100644
--- a/app/live-detail/[id].tsx
+++ b/app/live-detail/[id].tsx
@@ -69,6 +69,7 @@ export default function LiveDetailScreen() {
if (liveData && Array.isArray(liveData)) {
// Find the specific match
const found = liveData.find((m) => m.event_key.toString() === id);
+ console.log("found", JSON.stringify(found, null, 2));
if (found) {
setMatch(found);
}
diff --git a/app/match-detail/[id].tsx b/app/match-detail/[id].tsx
index e509f1a..62b7dcf 100644
--- a/app/match-detail/[id].tsx
+++ b/app/match-detail/[id].tsx
@@ -1,4 +1,5 @@
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 { CardsCard } from "@/components/match-detail/football/cards-card";
@@ -60,7 +61,7 @@ export default function MatchDetailScreen() {
validTabs = ["info", "stats", "odds", "h2h", "chat"];
} else if (sportId === 2) {
// 篮球
- validTabs = ["info", "stats", "h2h", "chat"];
+ validTabs = ["info", "stats", "overall", "h2h", "chat"];
} else if (sportId === 3) {
// 网球
validTabs = ["info", "chat"];
@@ -82,8 +83,8 @@ export default function MatchDetailScreen() {
setError(null);
const result = await fetchMatchDetail(id as string);
setData(result);
- console.log("首发阵容", result.match.players?.away_team);
- console.log("红黄牌", result.events);
+ // console.log("首发阵容", result.match.players?.away_team);
+ // console.log("红黄牌", result.events);
@@ -177,6 +178,8 @@ export default function MatchDetailScreen() {
);
}
+ case "overall":
+ return ;
case "odds":
// 将 MatchDetailData.match 转换为 LiveScoreMatch 格式
const matchForOdds = {
diff --git a/components/match-detail/basketball/basketball-overall-stats.tsx b/components/match-detail/basketball/basketball-overall-stats.tsx
new file mode 100644
index 0000000..40c2c3b
--- /dev/null
+++ b/components/match-detail/basketball/basketball-overall-stats.tsx
@@ -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 (
+
+
+ {t("detail.empty_stats")}
+
+
+ );
+ }
+
+ return (
+
+ {/* Card */}
+
+ {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 (
+
+
+
+
+
+ {getStatLabel(item.type)}
+
+
+
+
+
+
+
+ );
+ })}
+
+
+ );
+}
+
+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 (
+
+ {text}
+
+ );
+}
+
+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 (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+/* ----------------- 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,
+ },
+});
diff --git a/components/match-detail/match-tabs.tsx b/components/match-detail/match-tabs.tsx
index db29fa8..498c228 100644
--- a/components/match-detail/match-tabs.tsx
+++ b/components/match-detail/match-tabs.tsx
@@ -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"),
diff --git a/i18n/locales/en.json b/i18n/locales/en.json
index fe0d285..6b821dd 100644
--- a/i18n/locales/en.json
+++ b/i18n/locales/en.json
@@ -92,6 +92,7 @@
"fetch_failed": "Failed to fetch details",
"not_found": "Match data not found",
"tabs": {
+ "overall": "Overall",
"info": "Details",
"stats": "Statistics",
"odds": "Odds",
@@ -166,6 +167,12 @@
"lineups_coach": "Coach",
"lineups_subs": "Substitutes",
"lineups_missing": "Missing players"
+ },
+ "overall_stats": {
+ "total_assists": "Assists",
+ "total_blocks": "Blocks",
+ "total_steals": "Steals",
+ "total_turnovers": "Turnovers"
}
},
"selection": {
diff --git a/i18n/locales/hi.json b/i18n/locales/hi.json
index 1cd85fe..6d145a1 100644
--- a/i18n/locales/hi.json
+++ b/i18n/locales/hi.json
@@ -88,6 +88,7 @@
"fetch_failed": "विवरण प्राप्त करने में विफल",
"not_found": "मैच डेटा नहीं मिला",
"tabs": {
+ "overall": "कुल",
"info": "विवरण",
"stats": "आँकड़े",
"odds": "ऑड्स",
@@ -162,6 +163,12 @@
"lineups_coach": "कोच",
"lineups_subs": "सब्स्टीट्यूट",
"lineups_missing": "अनुपस्थित खिलाड़ी"
+ },
+ "overall_stats": {
+ "total_assists": "असिस्ट",
+ "total_blocks": "ब्लॉक",
+ "total_steals": "स्टील",
+ "total_turnovers": "टर्नओवर"
}
},
"selection": {
diff --git a/i18n/locales/id.json b/i18n/locales/id.json
index 05fdc9c..3a93703 100644
--- a/i18n/locales/id.json
+++ b/i18n/locales/id.json
@@ -88,6 +88,7 @@
"fetch_failed": "Gagal mengambil data",
"not_found": "Data pertandingan tidak ditemukan",
"tabs": {
+ "overall": "Keseluruhan",
"info": "Detail",
"stats": "Statistik",
"odds": "Odds",
@@ -162,6 +163,12 @@
"lineups_coach": "Pelatih",
"lineups_subs": "Cadangan",
"lineups_missing": "Pemain Absen"
+ },
+ "overall_stats": {
+ "total_assists": "Asisten",
+ "total_blocks": "Blok",
+ "total_steals": "Steal",
+ "total_turnovers": "Turnover"
}
},
"selection": {
diff --git a/i18n/locales/ms.json b/i18n/locales/ms.json
index fb7df10..69784a4 100644
--- a/i18n/locales/ms.json
+++ b/i18n/locales/ms.json
@@ -88,6 +88,7 @@
"fetch_failed": "Gagal mendapatkan maklumat",
"not_found": "Data perlawanan tidak ditemui",
"tabs": {
+ "overall": "Keseluruhan",
"info": "Maklumat",
"stats": "Statistik",
"odds": "Odds",
@@ -162,6 +163,12 @@
"lineups_coach": "Jurulatih",
"lineups_subs": "Pemain Simpanan",
"lineups_missing": "Pemain Tidak Tersenarai"
+ },
+ "overall_stats": {
+ "total_assists": "Bantuan",
+ "total_blocks": "Sekatan",
+ "total_steals": "Curi",
+ "total_turnovers": "Pusingan"
}
},
"selection": {
diff --git a/i18n/locales/th.json b/i18n/locales/th.json
index 7b16919..eb02df7 100644
--- a/i18n/locales/th.json
+++ b/i18n/locales/th.json
@@ -88,6 +88,7 @@
"fetch_failed": "ไม่สามารถดึงข้อมูลได้",
"not_found": "ไม่พบข้อมูลการแข่งขัน",
"tabs": {
+ "overall": "ทั้งหมด",
"info": "รายละเอียด",
"stats": "สถิติ",
"odds": "อัตราต่อรอง",
@@ -162,6 +163,12 @@
"lineups_coach": "โค้ช",
"lineups_subs": "ตัวสำรอง",
"lineups_missing": "ผู้เล่นที่ขาดหาย"
+ },
+ "overall_stats": {
+ "total_assists": "แอสซิสต์",
+ "total_blocks": "บล็อก",
+ "total_steals": "สตีล",
+ "total_turnovers": "เทิร์นโอเวอร์"
}
},
"selection": {
diff --git a/i18n/locales/vi.json b/i18n/locales/vi.json
index e79cbbb..fcc9642 100644
--- a/i18n/locales/vi.json
+++ b/i18n/locales/vi.json
@@ -88,6 +88,7 @@
"fetch_failed": "Không thể tải dữ liệu",
"not_found": "Không tìm thấy dữ liệu trận đấu",
"tabs": {
+ "overall": "Tổng",
"info": "Thông tin",
"stats": "Thống kê",
"odds": "Tỷ lệ cược",
@@ -162,6 +163,12 @@
"lineups_coach": "Huấn luyện viên",
"lineups_subs": "Dự bị",
"lineups_missing": "Cầu thủ vắng mặt"
+ },
+ "overall_stats": {
+ "total_assists": "Kiến tạo",
+ "total_blocks": "Chặn bóng",
+ "total_steals": "Cướp bóng",
+ "total_turnovers": "Mất bóng"
}
},
"selection": {
diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json
index 478afcf..1f7018d 100644
--- a/i18n/locales/zh.json
+++ b/i18n/locales/zh.json
@@ -92,6 +92,7 @@
"fetch_failed": "获取详情失败",
"not_found": "未找到比赛数据",
"tabs": {
+ "overall": "全部",
"info": "详情",
"stats": "统计数据",
"odds": "赔率",
@@ -166,6 +167,12 @@
"lineups_coach": "主教练",
"lineups_subs": "替补球员",
"lineups_missing": "缺席球员"
+ },
+ "overall_stats": {
+ "total_assists": "助攻",
+ "total_blocks": "盖帽",
+ "total_steals": "抢断",
+ "total_turnovers": "失误"
}
},
"selection": {