Files
2025-11-13 16:47:10 +08:00

197 lines
4.6 KiB
TypeScript

/**
* 游戏大厅组件
*
* 展示游戏列表,使用真实数据
*/
import React, { useState, useEffect, useMemo } from 'react';
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
Image,
ActivityIndicator,
Dimensions,
} from 'react-native';
import { createThemeStyles, useColorScheme, useThemeInfo } from '@/theme';
import { useGameMenuTabs } from '@/hooks/useGameMenus';
import { getMockGamesByCategory } from '@/services/mockHomeService';
import type { Game } from '@/types/home';
const { width } = Dimensions.get('window');
/**
* 创建主题样式
*/
const styles = createThemeStyles((colors) => ({
container: {
flex: 1,
backgroundColor: colors.background,
paddingHorizontal: 12,
paddingVertical: 12,
},
gameGrid: {
paddingBottom: 20,
},
gameCard: {
flex: 1,
margin: 6,
borderRadius: 12,
overflow: 'hidden',
backgroundColor: colors.card,
elevation: 3,
shadowColor: colors.cardShadow,
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.15,
shadowRadius: 6,
},
gameCardPressed: {
opacity: 0.8,
},
gameImage: {
width: '100%',
height: 140,
backgroundColor: colors.backgroundSecondary,
justifyContent: 'center',
alignItems: 'center',
},
gameIcon: {
fontSize: 48,
},
gameInfo: {
padding: 10,
},
gameName: {
fontSize: 13,
fontWeight: '600',
color: colors.text,
marginBottom: 6,
},
gameButton: {
backgroundColor: colors.primary,
paddingVertical: 8,
borderRadius: 6,
alignItems: 'center',
marginTop: 6,
},
gameButtonText: {
color: '#FFFFFF',
fontSize: 12,
fontWeight: '600',
},
emptyContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 40,
},
emptyText: {
fontSize: 14,
color: colors.textSecondary,
marginTop: 12,
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
}));
interface LobbyProps {
games?: Game[];
onGamePress?: (game: Game) => void;
topHeight?: number;
}
/**
* 游戏大厅组件
*/
export default function Lobby({ games: propGames, onGamePress, topHeight = 0 }: LobbyProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const { colors } = useThemeInfo();
const { activeMainMenuTab, activeSubMenuTab } = useGameMenuTabs();
const [games, setGames] = useState<Game[]>(propGames || []);
const [loading, setLoading] = useState(true);
// 加载游戏数据
useEffect(() => {
if (propGames && propGames.length > 0) {
setGames(propGames);
setLoading(false);
return;
}
const loadGames = async () => {
try {
setLoading(true);
// const response = await getGames(activeMainMenuTab);
// setGames(response.games.length > 0 ? response.games : getMockGamesByCategory(activeMainMenuTab));
} catch (error) {
console.error('加载游戏失败:', error);
setGames(getMockGamesByCategory(activeMainMenuTab));
} finally {
setLoading(false);
}
};
loadGames();
}, [propGames, activeMainMenuTab]);
const renderGameCard = ({ item }: { item: Game }) => (
<TouchableOpacity style={s.gameCard} activeOpacity={0.7} onPress={() => onGamePress?.(item)}>
<View style={s.gameImage}>
{item.icon ? (
<Image
source={{ uri: item.icon }}
style={{ width: '100%', height: '100%' }}
resizeMode="cover"
/>
) : (
<Text style={s.gameIcon}>🎮</Text>
)}
</View>
<View style={s.gameInfo}>
<Text style={s.gameName} numberOfLines={2}>
{item.play_up_name}
{item.play_cname ? ` - ${item.play_cname}` : ''}
</Text>
<TouchableOpacity style={s.gameButton} onPress={() => onGamePress?.(item)}>
<Text style={s.gameButtonText}></Text>
</TouchableOpacity>
</View>
</TouchableOpacity>
);
if (loading) {
return (
<View style={s.loadingContainer}>
<ActivityIndicator size="large" color={colors.primary} />
</View>
);
}
if (games.length === 0) {
return (
<View style={s.emptyContainer}>
<Text style={{ fontSize: 40 }}>🎮</Text>
<Text style={s.emptyText}></Text>
</View>
);
}
return (
<View style={s.container}>
<FlatList
data={games}
renderItem={renderGameCard}
keyExtractor={(item) => item.id}
numColumns={2}
scrollEnabled={false}
contentContainerStyle={s.gameGrid}
/>
</View>
);
}