215 lines
5.9 KiB
TypeScript
215 lines
5.9 KiB
TypeScript
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",
|
||
},
|
||
});
|