You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
4.5 KiB
210 lines
4.5 KiB
|
1 month ago
|
/**
|
||
|
|
* 游戏大厅组件
|
||
|
|
*
|
||
|
|
* 展示游戏列表,使用真实数据
|
||
|
|
*/
|
||
|
|
|
||
|
|
import React, { useState, useEffect, useMemo } from 'react';
|
||
|
|
import {
|
||
|
|
View,
|
||
|
|
Text,
|
||
|
|
StyleSheet,
|
||
|
|
FlatList,
|
||
|
|
TouchableOpacity,
|
||
|
|
Image,
|
||
|
|
ActivityIndicator,
|
||
|
|
Dimensions,
|
||
|
|
} from 'react-native';
|
||
|
|
import { createThemeStyles } from '@/theme';
|
||
|
|
import Colors from '@/constants/Colors';
|
||
|
|
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 {
|
||
|
|
theme: 'light' | 'dark';
|
||
|
|
games?: Game[];
|
||
|
|
selectedCategory?: number;
|
||
|
|
onGamePress?: (game: Game) => void;
|
||
|
|
topHeight?: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 游戏大厅组件
|
||
|
|
*/
|
||
|
|
export default function Lobby({
|
||
|
|
theme,
|
||
|
|
games: propGames,
|
||
|
|
selectedCategory = 0,
|
||
|
|
onGamePress,
|
||
|
|
topHeight = 0,
|
||
|
|
}: LobbyProps) {
|
||
|
|
const s = styles[theme];
|
||
|
|
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(selectedCategory);
|
||
|
|
// setGames(response.games.length > 0 ? response.games : getMockGamesByCategory(selectedCategory));
|
||
|
|
} catch (error) {
|
||
|
|
console.error('加载游戏失败:', error);
|
||
|
|
setGames(getMockGamesByCategory(selectedCategory));
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
loadGames();
|
||
|
|
}, [propGames, selectedCategory]);
|
||
|
|
|
||
|
|
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[theme].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>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|