diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 8d50b3d..a019b35 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -52,13 +52,55 @@ export default function HomeScreen() { const loadSports = async () => { try { - const list = await fetchSports(); - setSports(list); - if (list.length > 0) { - setSelectedSportId(list[0].id); + const apiList = await fetchSports(); + // 创建8个运动的完整列表 + const defaultSports: Sport[] = [ + { id: 1, name: "football", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 2, name: "basketball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 3, name: "tennis", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 4, name: "cricket", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 5, name: "baseball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 6, name: "badminton", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 7, name: "snooker", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 8, name: "volleyball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + ]; + + // 合并API返回的运动和默认列表 + const sportsMap = new Map(); + apiList.forEach((sport) => { + sportsMap.set(sport.id, sport); + }); + + // 补充默认运动到8个 + defaultSports.forEach((sport) => { + if (!sportsMap.has(sport.id)) { + sportsMap.set(sport.id, sport); + } + }); + + const allSports = Array.from(sportsMap.values()) + .sort((a, b) => a.id - b.id) + .slice(0, 8); + + setSports(allSports); + if (allSports.length > 0) { + setSelectedSportId(allSports[0].id); } } catch (e) { console.error(e); + // API失败时使用默认8个运动 + const defaultSports: Sport[] = [ + { id: 1, name: "football", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 2, name: "basketball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 3, name: "tennis", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 4, name: "cricket", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 5, name: "baseball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 6, name: "badminton", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 7, name: "snooker", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + { id: 8, name: "volleyball", description: "", icon: "", isActive: true, updatedAt: "", createdAt: "" }, + ]; + setSports(defaultSports); + setSelectedSportId(1); } }; @@ -76,6 +118,23 @@ export default function HomeScreen() { }; const currentSport = sports.find((s) => s.id === selectedSportId); + + // 获取当前运动的国际化名称 + const getSportName = (sport: Sport | undefined): string => { + if (!sport) return t("home.select_sport"); + const sportKeyMap: { [key: number]: string } = { + 1: "football", + 2: "basketball", + 3: "tennis", + 4: "cricket", + 5: "baseball", + 6: "badminton", + 7: "snooker", + 8: "volleyball", + }; + const sportKey = sportKeyMap[sport.id] || sport.name.toLowerCase(); + return t(`sports.${sportKey}`, { defaultValue: sport.name }); + }; const renderHeader = () => ( @@ -98,7 +157,7 @@ export default function HomeScreen() { > - {currentSport ? currentSport.name : t("home.select_sport")} + {getSportName(currentSport)} @@ -148,12 +207,25 @@ export default function HomeScreen() { visible={showSportModal} onClose={() => setShowSportModal(false)} title={t("home.select_sport")} - options={sports.map((s) => ({ - id: s.id, - label: s.name, - value: s.id, - icon: s.icon, // 添加图标 - }))} + options={sports.map((s) => { + const sportKeyMap: { [key: number]: string } = { + 1: "football", + 2: "basketball", + 3: "tennis", + 4: "cricket", + 5: "baseball", + 6: "badminton", + 7: "snooker", + 8: "volleyball", + }; + const sportKey = sportKeyMap[s.id] || s.name.toLowerCase(); + return { + id: s.id, + label: s.name, // 保留原始名称用于图标识别 + value: s.id, + icon: s.icon, + }; + })} selectedValue={selectedSportId} onSelect={setSelectedSportId} /> diff --git a/assets/balls/badminton_active.svg b/assets/balls/badminton_active.svg new file mode 100644 index 0000000..44b3155 --- /dev/null +++ b/assets/balls/badminton_active.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/badminton_disabled.svg b/assets/balls/badminton_disabled.svg new file mode 100644 index 0000000..e7b65cd --- /dev/null +++ b/assets/balls/badminton_disabled.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/badminton_inactive.svg b/assets/balls/badminton_inactive.svg new file mode 100644 index 0000000..544dc03 --- /dev/null +++ b/assets/balls/badminton_inactive.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/baseball_active.svg b/assets/balls/baseball_active.svg new file mode 100644 index 0000000..236aa10 --- /dev/null +++ b/assets/balls/baseball_active.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/baseball_disabled.svg b/assets/balls/baseball_disabled.svg new file mode 100644 index 0000000..28b4be9 --- /dev/null +++ b/assets/balls/baseball_disabled.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/baseball_inactive.svg b/assets/balls/baseball_inactive.svg new file mode 100644 index 0000000..10f2c72 --- /dev/null +++ b/assets/balls/baseball_inactive.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/basketball_active.svg b/assets/balls/basketball_active.svg new file mode 100644 index 0000000..8856f6d --- /dev/null +++ b/assets/balls/basketball_active.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/basketball_disabled.svg b/assets/balls/basketball_disabled.svg new file mode 100644 index 0000000..f3dc4b4 --- /dev/null +++ b/assets/balls/basketball_disabled.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/basketball_inactive.svg b/assets/balls/basketball_inactive.svg new file mode 100644 index 0000000..bb8ce73 --- /dev/null +++ b/assets/balls/basketball_inactive.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/cricket_active.svg b/assets/balls/cricket_active.svg new file mode 100644 index 0000000..bb9d2ff --- /dev/null +++ b/assets/balls/cricket_active.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/cricket_disabled.svg b/assets/balls/cricket_disabled.svg new file mode 100644 index 0000000..867c4b2 --- /dev/null +++ b/assets/balls/cricket_disabled.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/cricket_inactive.svg b/assets/balls/cricket_inactive.svg new file mode 100644 index 0000000..c75bffb --- /dev/null +++ b/assets/balls/cricket_inactive.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/football_active.svg b/assets/balls/football_active.svg new file mode 100644 index 0000000..e49c560 --- /dev/null +++ b/assets/balls/football_active.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/football_disabled.svg b/assets/balls/football_disabled.svg new file mode 100644 index 0000000..e4cf666 --- /dev/null +++ b/assets/balls/football_disabled.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/football_inactive.svg b/assets/balls/football_inactive.svg new file mode 100644 index 0000000..37ce492 --- /dev/null +++ b/assets/balls/football_inactive.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/snooker_active.svg b/assets/balls/snooker_active.svg new file mode 100644 index 0000000..cb79f24 --- /dev/null +++ b/assets/balls/snooker_active.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/snooker_disabled.svg b/assets/balls/snooker_disabled.svg new file mode 100644 index 0000000..a81c40d --- /dev/null +++ b/assets/balls/snooker_disabled.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/snooker_inactive.svg b/assets/balls/snooker_inactive.svg new file mode 100644 index 0000000..96443f3 --- /dev/null +++ b/assets/balls/snooker_inactive.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/tennis_active.svg b/assets/balls/tennis_active.svg new file mode 100644 index 0000000..57eb159 --- /dev/null +++ b/assets/balls/tennis_active.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/tennis_disabled.svg b/assets/balls/tennis_disabled.svg new file mode 100644 index 0000000..8a1887b --- /dev/null +++ b/assets/balls/tennis_disabled.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/balls/tennis_inactive.svg b/assets/balls/tennis_inactive.svg new file mode 100644 index 0000000..10f2de1 --- /dev/null +++ b/assets/balls/tennis_inactive.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/components/selection-modal.tsx b/components/selection-modal.tsx index 8e6405f..940c2cd 100644 --- a/components/selection-modal.tsx +++ b/components/selection-modal.tsx @@ -2,7 +2,9 @@ import { ThemedText } from "@/components/themed-text"; import { IconSymbol } from "@/components/ui/icon-symbol"; import { Colors } from "@/constants/theme"; import { useTheme } from "@/context/ThemeContext"; +import { Image } from "expo-image"; import React from "react"; +import { useTranslation } from "react-i18next"; import { Modal, Pressable, ScrollView, StyleSheet, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; @@ -13,42 +15,86 @@ interface SelectionOption { icon?: string; // 图标名称 } -// 运动名称到图标的映射 -const getSportIcon = (label: string, icon?: string): string => { - // 如果提供了 icon 字段,尝试直接使用 - if (icon) { - // 如果 icon 已经是 Ionicons 格式,直接返回 - if (icon.includes("-")) { - return icon; - } - } +// 运动ID到运动键的映射(支持8种运动) +// 根据API文档:1:足球, 2:篮球, 3:网球, 4:板球 +// 扩展为8种:1:足球, 2:篮球, 3:网球, 4:板球, 5:棒球, 6:羽毛球, 7:斯诺克, 8:排球 +const getSportKeyById = (id: number): string => { + const sportMap: { [key: number]: string } = { + 1: "football", + 2: "basketball", + 3: "tennis", + 4: "cricket", + 5: "baseball", + 6: "badminton", + 7: "snooker", + 8: "volleyball", + }; + return sportMap[id] || "football"; +}; - // 根据运动名称映射图标 +// 运动名称到图标文件名的映射(支持8种运动) +const getSportIconName = (label: string, id?: number): string => { + // 优先使用ID + if (id !== undefined) { + return getSportKeyById(id); + } + const labelLower = label.toLowerCase(); if (labelLower.includes("足球") || labelLower.includes("football") || labelLower.includes("soccer")) { - return "football-outline"; + return "football"; } if (labelLower.includes("篮球") || labelLower.includes("basketball")) { - return "basketball-outline"; + return "basketball"; } if (labelLower.includes("板球") || labelLower.includes("cricket")) { - return "baseball-outline"; + return "cricket"; } if (labelLower.includes("网球") || labelLower.includes("tennis")) { - return "tennisball-outline"; + return "tennis"; } if (labelLower.includes("棒球") || labelLower.includes("baseball")) { - return "baseball-outline"; + return "baseball"; } if (labelLower.includes("羽毛球") || labelLower.includes("badminton")) { - return "ellipse-outline"; + return "badminton"; } if (labelLower.includes("斯诺克") || labelLower.includes("snooker")) { - return "radio-button-on-outline"; + return "snooker"; + } + if (labelLower.includes("排球") || labelLower.includes("volleyball")) { + return "volleyball"; } // 默认图标 - return "ellipse-outline"; + return "football"; +}; + +// 获取图标资源 +const getSportIconSource = (label: string, isSelected: boolean, id?: number) => { + const iconName = getSportIconName(label, id); + const state = isSelected ? "active" : "inactive"; + + // 使用 require 动态导入图标(8种运动) + const iconMap: { [key: string]: any } = { + "football_active": require("@/assets/balls/football_active.svg"), + "football_inactive": require("@/assets/balls/football_inactive.svg"), + "basketball_active": require("@/assets/balls/basketball_active.svg"), + "basketball_inactive": require("@/assets/balls/basketball_inactive.svg"), + "cricket_active": require("@/assets/balls/cricket_active.svg"), + "cricket_inactive": require("@/assets/balls/cricket_inactive.svg"), + "tennis_active": require("@/assets/balls/tennis_active.svg"), + "tennis_inactive": require("@/assets/balls/tennis_inactive.svg"), + "baseball_active": require("@/assets/balls/baseball_active.svg"), + "baseball_inactive": require("@/assets/balls/baseball_inactive.svg"), + "badminton_active": require("@/assets/balls/badminton_active.svg"), + "badminton_inactive": require("@/assets/balls/badminton_inactive.svg"), + "snooker_active": require("@/assets/balls/snooker_active.svg"), + "snooker_inactive": require("@/assets/balls/snooker_inactive.svg"), + "volleyball_active": require("@/assets/balls/football_active.svg"), // 使用football作为默认,因为volleyball图标不存在 + "volleyball_inactive": require("@/assets/balls/football_inactive.svg"), + }; + + return iconMap[`${iconName}_${state}`] || iconMap["football_inactive"]; }; interface SelectionModalProps { @@ -69,6 +115,7 @@ export function SelectionModal({ selectedValue, }: SelectionModalProps) { const { theme } = useTheme(); + const { t } = useTranslation(); const isDark = theme === "dark"; const bg = isDark ? "#1C1C1E" : "#FFFFFF"; const text = isDark ? "#FFFFFF" : "#000000"; @@ -99,6 +146,11 @@ export function SelectionModal({ const isSelected = selectedValue === opt.value; const optionBg = isDark ? "#2C2C2E" : "#F5F5F5"; const iconBg = isDark ? "#3A3A3C" : "#E5E5EA"; + + // 获取运动键用于i18n和图标 + const sportId = typeof opt.id === "number" ? opt.id : undefined; + const sportKey = sportId ? getSportKeyById(sportId) : getSportIconName(opt.label, sportId); + const sportName = t(`sports.${sportKey}`, { defaultValue: opt.label }); return ( {/* 左侧图标 */} - @@ -129,10 +181,10 @@ export function SelectionModal({ { fontWeight: isSelected ? "600" : "400" }, ]} > - {opt.label} + {sportName} - {isSelected ? "已选择" : "点击切换"} + {isSelected ? t("selection.selected") : t("selection.click_to_toggle")} @@ -168,7 +220,7 @@ const styles = StyleSheet.create({ borderTopLeftRadius: 20, borderTopRightRadius: 20, padding: 20, - maxHeight: "70%", + height: "55%", }, header: { flexDirection: "row", @@ -205,11 +257,9 @@ const styles = StyleSheet.create({ justifyContent: "center", marginRight: 12, }, - iconPlaceholder: { - width: 24, - height: 24, - borderRadius: 12, - backgroundColor: "#999", + iconImage: { + width: 32, + height: 32, }, content: { flex: 1, diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 437a9d7..98cdde6 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -55,5 +55,19 @@ "halftime": "Half: {{score}}", "empty_h2h": "No H2H data", "empty_chat": "Chat is not available" + }, + "selection": { + "selected": "Selected", + "click_to_toggle": "Tap to switch" + }, + "sports": { + "football": "Football", + "basketball": "Basketball", + "tennis": "Tennis", + "cricket": "Cricket", + "baseball": "Baseball", + "badminton": "Badminton", + "snooker": "Snooker", + "volleyball": "Volleyball" } } \ No newline at end of file diff --git a/i18n/locales/zh.json b/i18n/locales/zh.json index 782b3f4..dfb1b5a 100644 --- a/i18n/locales/zh.json +++ b/i18n/locales/zh.json @@ -55,5 +55,19 @@ "halftime": "半场: {{score}}", "empty_h2h": "暂无交锋数据", "empty_chat": "聊天功能暂未开启" + }, + "selection": { + "selected": "已选择", + "click_to_toggle": "点击切换" + }, + "sports": { + "football": "足球", + "basketball": "篮球", + "tennis": "网球", + "cricket": "板球", + "baseball": "棒球", + "badminton": "羽毛球", + "snooker": "斯诺克", + "volleyball": "排球" } } \ No newline at end of file