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, ScrollView, 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); // Bucket Logic const [total, setTotal] = useState(0); const [activeBucket, setActiveBucket] = useState(0); const BUCKET_SIZE = 100; const loadData = useCallback(async (bucketIndex = 0, tabValue = '', isRefresh = false) => { if (isRefresh) setRefreshing(true); setLoading(true); try { // Page is bucketIndex + 1, Size is 100 const res = await getBoxList(poolId, tabValue as any, bucketIndex + 1, BUCKET_SIZE); const newData = res?.records || []; setList(newData); setTotal(res?.total || 0); // Assuming API returns total setActiveBucket(bucketIndex); } catch (error) { console.error('Failed to load boxes:', error); } finally { setLoading(false); setRefreshing(false); } }, [poolId]); useImperativeHandle(ref, () => ({ show: () => { setVisible(true); setActiveTab(TABS[0]); setActiveBucket(0); setTotal(0); loadData(0, TABS[0].value); }, close: () => { setVisible(false); }, })); const handleTabChange = (tab: typeof TABS[0]) => { setActiveTab(tab); // Reset to first bucket when changing level tab loadData(0, tab.value); }; const handleBucketChange = (index: number) => { loadData(index, activeTab.value); }; const handleRefresh = () => { loadData(activeBucket, activeTab.value, true); } const renderItem = ({ item, index }: { item: any; index: number }) => { if (item.leftQuantity <= 0) return null; return ( { setVisible(false); onSelect(item); }} > {/* Index should be absolute based on bucket */} {(activeBucket * BUCKET_SIZE) + index + 1} {/* Left Icon & Count */} 剩{item.leftQuantity}发 {/* Breakdown */} ); }; const buckets = Math.ceil(total / BUCKET_SIZE); return ( setVisible(false)}> setVisible(false)} /> {/* Header */} 换盒 setVisible(false)}> × {/* Level Tabs */} {TABS.map(tab => ( handleTabChange(tab)} > {tab.title} ))} {/* Bucket Selector (Range 1-100, etc.) */} {buckets > 1 && ( {Array.from({ length: buckets }).map((_, index) => { const start = index * BUCKET_SIZE + 1; const end = Math.min((index + 1) * BUCKET_SIZE, total); const isActive = activeBucket === index; return ( handleBucketChange(index)} > {start}~{end} ); })} )} {/* List */} item.id || String(index)} contentContainerStyle={styles.listContent} 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', }, // Bucket Styles bucketContainer: { height: 44, marginBottom: 10, }, bucketScroll: { paddingHorizontal: 15, }, bucketItem: { paddingHorizontal: 15, paddingVertical: 8, borderRadius: 20, backgroundColor: '#f5f5f5', marginRight: 10, justifyContent: 'center', }, bucketItemActive: { backgroundColor: '#ffdb4d', }, bucketText: { fontSize: 12, color: '#666', }, bucketTextActive: { color: '#333', fontWeight: 'bold', }, });