Files
physical-expo/components/live-detail/odds-card.tsx

215 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ThemedText } from "@/components/themed-text";
import { ThemedView } from "@/components/themed-view";
import { fetchOdds } from "@/lib/api";
import { LiveScoreMatch, OddsItem } from "@/types/api";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { ActivityIndicator, StyleSheet, View } from "react-native";
interface OddsCardProps {
match: LiveScoreMatch;
isDark: boolean;
sportId: number;
}
export function OddsCard({ match, isDark, sportId }: OddsCardProps) {
const { t } = useTranslation();
const [odds, setOdds] = useState<OddsItem | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadOdds();
// 设置每 30 秒自动更新一次
const interval = setInterval(loadOdds, 30000);
return () => clearInterval(interval);
}, [match.event_key, sportId]);
const loadOdds = async () => {
try {
const data = await fetchOdds(sportId, match.event_key);
const matchOdds = data[match.event_key.toString()];
if (matchOdds && matchOdds.data && matchOdds.data.length > 0) {
// 优先选择包含常见赔率项的博彩公司,这里暂取第一个
setOdds(matchOdds.data[0]);
}
} catch (error) {
console.error("Load odds error:", error);
} finally {
setLoading(false);
}
};
// 提取队名缩写或前3个字母
const homeAbbr =
match.event_home_team?.substring(0, 3).toUpperCase() || "HOME";
const awayAbbr =
match.event_away_team?.substring(0, 3).toUpperCase() || "AWAY";
// 获取显示的盘口和赔率
const renderOddsContent = () => {
if (loading && !odds) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="small" color="#FF9800" />
</View>
);
}
// 尝试寻找亚洲盘口 (AH) 或者 主客和 (1X2)
// 这里为了演示演示,优先寻找 AH 相关数据
const ahKey1 = Object.keys(odds || {}).find(
(k) => k.startsWith("ah") && k.endsWith("_1")
);
const ahKey2 = ahKey1 ? ahKey1.replace("_1", "_2") : null;
if (odds && ahKey1 && ahKey2) {
const handicapValue = ahKey1.replace("ah", "").replace("_1", "");
return (
<View style={styles.row}>
<View style={[styles.item, isDark && styles.darkItem]}>
<ThemedText style={styles.team}>{homeAbbr}</ThemedText>
<ThemedText style={styles.odds}>{odds[ahKey1] || "-"}</ThemedText>
</View>
<View style={[styles.item, isDark && styles.darkItem]}>
<ThemedText style={styles.team}>HDP</ThemedText>
<ThemedText style={styles.odds}>{handicapValue}</ThemedText>
</View>
<View style={[styles.item, isDark && styles.darkItem]}>
<ThemedText style={styles.team}>{awayAbbr}</ThemedText>
<ThemedText style={styles.odds}>{odds[ahKey2] || "-"}</ThemedText>
</View>
</View>
);
}
// 如果没有 AH显示 1X2
if (odds && odds.odd_1 && odds.odd_2) {
return (
<View style={styles.row}>
<View style={[styles.item, isDark && styles.darkItem]}>
<ThemedText style={styles.team}>{homeAbbr}</ThemedText>
<ThemedText style={styles.odds}>{odds.odd_1}</ThemedText>
</View>
<View style={[styles.item, isDark && styles.darkItem]}>
<ThemedText style={styles.team}>DRAW</ThemedText>
<ThemedText style={styles.odds}>{odds.odd_x || "-"}</ThemedText>
</View>
<View style={[styles.item, isDark && styles.darkItem]}>
<ThemedText style={styles.team}>{awayAbbr}</ThemedText>
<ThemedText style={styles.odds}>{odds.odd_2}</ThemedText>
</View>
</View>
);
}
return (
<View style={styles.emptyContainer}>
<ThemedText style={{ color: "#999" }}>
{t("detail.empty_odds")}
</ThemedText>
</View>
);
};
return (
<ThemedView style={[styles.container, isDark && styles.darkContainer]}>
<View style={styles.header}>
<ThemedText style={styles.title}>
{t("detail.odds_card.title")}
</ThemedText>
<View style={styles.badge}>
<ThemedText style={styles.badgeText}>
{odds?.odd_bookmakers || "bet365"}
</ThemedText>
</View>
</View>
{renderOddsContent()}
<ThemedText style={styles.disclaimer}>
{t("detail.odds_card.disclaimer")}
</ThemedText>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
margin: 16,
borderRadius: 20,
padding: 20,
backgroundColor: "#FFF",
marginBottom: 16,
shadowColor: "#000",
shadowOpacity: 0.05,
shadowRadius: 10,
elevation: 2,
},
darkContainer: {
backgroundColor: "#1E1E20",
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 20,
},
title: {
fontSize: 18,
fontWeight: "500",
},
badge: {
backgroundColor: "#1E4D40",
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 8,
},
badgeText: {
color: "#FFF",
fontSize: 14,
fontWeight: "bold",
},
row: {
flexDirection: "row",
gap: 10,
},
item: {
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: "#F8F8F8",
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 14,
},
darkItem: {
backgroundColor: "rgba(255,255,255,0.05)",
},
team: {
fontSize: 14,
fontWeight: "500",
},
odds: {
fontSize: 14,
fontWeight: "700",
color: "#FF9800",
},
disclaimer: {
fontSize: 12,
color: "#BBB",
marginTop: 20,
textAlign: "center",
},
loadingContainer: {
height: 60,
justifyContent: "center",
alignItems: "center",
},
emptyContainer: {
height: 60,
justifyContent: "center",
alignItems: "center",
},
});