import { Images } from '@/constants/images'; import { getBoxList } from '@/services/award'; import { Image, ImageBackground } from 'expo-image'; import React, { forwardRef, useCallback, useImperativeHandle, useState } from 'react'; import { ActivityIndicator, Dimensions, FlatList, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); interface BoxSelectionModalProps { poolId: string; onSelect: (box: any) => void; } export interface BoxSelectionModalRef { show: () => void; close: () => void; } const TABS = [ { title: '全部', value: '' }, { title: '超神款', value: 'A' }, { title: '欧皇款', value: 'B' }, { title: '隐藏款', value: 'C' }, { title: '普通款', value: 'D' }, ]; export const BoxSelectionModal = forwardRef( ({ poolId, onSelect }, ref) => { const [visible, setVisible] = useState(false); const [activeTab, setActiveTab] = useState(TABS[0]); const [list, setList] = useState([]); const [loading, setLoading] = useState(false); const [refreshing, setRefreshing] = useState(false); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const loadData = useCallback(async (pageNum = 1, tabValue = '', isRefresh = false) => { if (isRefresh) setRefreshing(true); if (pageNum === 1 && !isRefresh) setLoading(true); try { const res = await getBoxList(poolId, tabValue as any, pageNum, 20); // Using 20 as pageSize const newData = res?.records || []; // Assuming standard paginated response if (pageNum === 1) { setList(newData); } else { setList(prev => [...prev, ...newData]); } setHasMore(newData.length >= 20); setPage(pageNum); } catch (error) { console.error('Failed to load boxes:', error); } finally { setLoading(false); setRefreshing(false); } }, [poolId]); useImperativeHandle(ref, () => ({ show: () => { setVisible(true); setActiveTab(TABS[0]); loadData(1, TABS[0].value); }, close: () => { setVisible(false); }, })); const handleTabChange = (tab: typeof TABS[0]) => { setActiveTab(tab); loadData(1, tab.value); }; const handleLoadMore = () => { if (!loading && hasMore) { loadData(page + 1, activeTab.value); } }; const handleRefresh = () => { loadData(1, activeTab.value, true); } const renderItem = ({ item, index }: { item: any; index: number }) => { if (item.leftQuantity <= 0) return null; return ( { setVisible(false); onSelect(item); }} > {index + 1} {/* Left Icon & Count */} 剩{item.leftQuantity}发 {/* Breakdown */} ); }; return ( setVisible(false)}> setVisible(false)} /> {/* Header */} 换盒 setVisible(false)}> × {/* Tabs */} {TABS.map(tab => ( handleTabChange(tab)} > {tab.title} ))} {/* List */} item.id || String(index)} contentContainerStyle={styles.listContent} onEndReached={handleLoadMore} onEndReachedThreshold={0.5} refreshing={refreshing} onRefresh={handleRefresh} ListEmptyComponent={ !loading ? 暂无数据 : null } ListFooterComponent={loading && !refreshing ? : null} /> ); } ); const BreakdownItem = ({ label, current, total, icon }: { label: string, current: number, total: number, icon: string }) => ( {current}/{total} ); const styles = StyleSheet.create({ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end', }, mask: { flex: 1 }, container: { height: 600, // wrapperWidth rpx in Vue paddingTop: 15, borderTopLeftRadius: 15, borderTopRightRadius: 15, overflow: 'hidden', backgroundColor: '#fff', // Fallback }, header: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginBottom: 15, position: 'relative', height: 44, }, title: { fontSize: 18, fontWeight: 'bold', color: '#fff', marginHorizontal: 10, textShadowColor: '#000', textShadowOffset: { width: 1, height: 1 }, textShadowRadius: 1, }, titleDecor: { width: 17, height: 19, }, closeBtn: { position: 'absolute', right: 15, top: 0, width: 24, height: 24, backgroundColor: '#ebebeb', borderRadius: 12, justifyContent: 'center', alignItems: 'center', }, closeText: { fontSize: 18, color: '#a2a2a2', marginTop: -2, }, tabs: { flexDirection: 'row', justifyContent: 'space-around', paddingHorizontal: 15, marginBottom: 10, }, tab: { paddingVertical: 6, paddingHorizontal: 12, borderRadius: 15, backgroundColor: '#f5f5f5', }, activeTab: { backgroundColor: '#ffdb4d', }, tabText: { fontSize: 12, color: '#666', }, activeTabText: { color: '#333', fontWeight: 'bold', }, listContainer: { flex: 1, backgroundColor: '#f3f3f3', marginHorizontal: 15, marginBottom: 20, borderWidth: 2, borderColor: '#000', padding: 10, }, listContent: { paddingBottom: 20, }, item: { backgroundColor: '#fff', borderWidth: 3, borderColor: '#000', marginBottom: 10, flexDirection: 'row', height: 80, position: 'relative', overflow: 'hidden', }, itemIndex: { position: 'absolute', left: 0, top: 0, width: 22, height: 22, backgroundColor: '#fff', borderRightWidth: 2, borderBottomWidth: 2, borderColor: '#000', justifyContent: 'center', alignItems: 'center', zIndex: 1, }, indexText: { fontSize: 12, fontWeight: 'bold', }, itemContent: { flex: 1, flexDirection: 'row', alignItems: 'center', paddingHorizontal: 10, paddingLeft: 22, // Space for index }, leftSection: { width: 60, alignItems: 'center', justifyContent: 'center', }, boxIcon: { width: 24, height: 24, marginBottom: 2, }, leftText: { fontSize: 10, color: '#333', }, divider: { width: 1, height: 40, backgroundColor: '#dcdad3', marginHorizontal: 10, opacity: 0.5, }, breakdown: { flex: 1, flexDirection: 'row', flexWrap: 'wrap', }, breakdownItem: { width: '50%', flexDirection: 'row', alignItems: 'center', marginBottom: 4, }, levelIcon: { width: 45, height: 16, marginRight: 4, }, breakdownText: { fontSize: 12, color: '#666', }, currentNum: { color: '#000', fontWeight: 'bold', }, emptyText: { textAlign: 'center', marginTop: 20, color: '#999', }, });