|
@@ -7,6 +7,7 @@ import {
|
|
|
Dimensions,
|
|
Dimensions,
|
|
|
FlatList,
|
|
FlatList,
|
|
|
Modal,
|
|
Modal,
|
|
|
|
|
+ ScrollView,
|
|
|
StyleSheet,
|
|
StyleSheet,
|
|
|
Text,
|
|
Text,
|
|
|
TouchableOpacity,
|
|
TouchableOpacity,
|
|
@@ -40,25 +41,25 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
const [list, setList] = useState<any[]>([]);
|
|
const [list, setList] = useState<any[]>([]);
|
|
|
const [loading, setLoading] = useState(false);
|
|
const [loading, setLoading] = useState(false);
|
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
const [refreshing, setRefreshing] = useState(false);
|
|
|
- const [page, setPage] = useState(1);
|
|
|
|
|
- const [hasMore, setHasMore] = useState(true);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Bucket Logic
|
|
|
|
|
+ const [total, setTotal] = useState(0);
|
|
|
|
|
+ const [activeBucket, setActiveBucket] = useState(0);
|
|
|
|
|
+ const BUCKET_SIZE = 100;
|
|
|
|
|
|
|
|
- const loadData = useCallback(async (pageNum = 1, tabValue = '', isRefresh = false) => {
|
|
|
|
|
|
|
+ const loadData = useCallback(async (bucketIndex = 0, tabValue = '', isRefresh = false) => {
|
|
|
if (isRefresh) setRefreshing(true);
|
|
if (isRefresh) setRefreshing(true);
|
|
|
- if (pageNum === 1 && !isRefresh) setLoading(true);
|
|
|
|
|
|
|
+ setLoading(true);
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- const res = await getBoxList(poolId, tabValue as any, pageNum, 20); // Using 20 as pageSize
|
|
|
|
|
- const newData = res?.records || []; // Assuming standard paginated response
|
|
|
|
|
|
|
+ // Page is bucketIndex + 1, Size is 100
|
|
|
|
|
+ const res = await getBoxList(poolId, tabValue as any, bucketIndex + 1, BUCKET_SIZE);
|
|
|
|
|
+ const newData = res?.records || [];
|
|
|
|
|
|
|
|
- if (pageNum === 1) {
|
|
|
|
|
- setList(newData);
|
|
|
|
|
- } else {
|
|
|
|
|
- setList(prev => [...prev, ...newData]);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ setList(newData);
|
|
|
|
|
+ setTotal(res?.total || 0); // Assuming API returns total
|
|
|
|
|
+ setActiveBucket(bucketIndex);
|
|
|
|
|
|
|
|
- setHasMore(newData.length >= 20);
|
|
|
|
|
- setPage(pageNum);
|
|
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
console.error('Failed to load boxes:', error);
|
|
console.error('Failed to load boxes:', error);
|
|
|
} finally {
|
|
} finally {
|
|
@@ -71,7 +72,9 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
show: () => {
|
|
show: () => {
|
|
|
setVisible(true);
|
|
setVisible(true);
|
|
|
setActiveTab(TABS[0]);
|
|
setActiveTab(TABS[0]);
|
|
|
- loadData(1, TABS[0].value);
|
|
|
|
|
|
|
+ setActiveBucket(0);
|
|
|
|
|
+ setTotal(0);
|
|
|
|
|
+ loadData(0, TABS[0].value);
|
|
|
},
|
|
},
|
|
|
close: () => {
|
|
close: () => {
|
|
|
setVisible(false);
|
|
setVisible(false);
|
|
@@ -80,17 +83,16 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
|
|
|
|
|
const handleTabChange = (tab: typeof TABS[0]) => {
|
|
const handleTabChange = (tab: typeof TABS[0]) => {
|
|
|
setActiveTab(tab);
|
|
setActiveTab(tab);
|
|
|
- loadData(1, tab.value);
|
|
|
|
|
|
|
+ // Reset to first bucket when changing level tab
|
|
|
|
|
+ loadData(0, tab.value);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const handleLoadMore = () => {
|
|
|
|
|
- if (!loading && hasMore) {
|
|
|
|
|
- loadData(page + 1, activeTab.value);
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const handleBucketChange = (index: number) => {
|
|
|
|
|
+ loadData(index, activeTab.value);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const handleRefresh = () => {
|
|
const handleRefresh = () => {
|
|
|
- loadData(1, activeTab.value, true);
|
|
|
|
|
|
|
+ loadData(activeBucket, activeTab.value, true);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const renderItem = ({ item, index }: { item: any; index: number }) => {
|
|
const renderItem = ({ item, index }: { item: any; index: number }) => {
|
|
@@ -105,7 +107,8 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
}}
|
|
}}
|
|
|
>
|
|
>
|
|
|
<View style={styles.itemIndex}>
|
|
<View style={styles.itemIndex}>
|
|
|
- <Text style={styles.indexText}>{index + 1}</Text>
|
|
|
|
|
|
|
+ {/* Index should be absolute based on bucket */}
|
|
|
|
|
+ <Text style={styles.indexText}>{(activeBucket * BUCKET_SIZE) + index + 1}</Text>
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
<View style={styles.itemContent}>
|
|
<View style={styles.itemContent}>
|
|
@@ -129,6 +132,8 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ const buckets = Math.ceil(total / BUCKET_SIZE);
|
|
|
|
|
+
|
|
|
return (
|
|
return (
|
|
|
<Modal visible={visible} transparent animationType="slide" onRequestClose={() => setVisible(false)}>
|
|
<Modal visible={visible} transparent animationType="slide" onRequestClose={() => setVisible(false)}>
|
|
|
<View style={styles.overlay}>
|
|
<View style={styles.overlay}>
|
|
@@ -150,7 +155,7 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
</TouchableOpacity>
|
|
</TouchableOpacity>
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
- {/* Tabs */}
|
|
|
|
|
|
|
+ {/* Level Tabs */}
|
|
|
<View style={styles.tabs}>
|
|
<View style={styles.tabs}>
|
|
|
{TABS.map(tab => (
|
|
{TABS.map(tab => (
|
|
|
<TouchableOpacity
|
|
<TouchableOpacity
|
|
@@ -165,6 +170,30 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
))}
|
|
))}
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
|
|
|
+ {/* Bucket Selector (Range 1-100, etc.) */}
|
|
|
|
|
+ {buckets > 1 && (
|
|
|
|
|
+ <View style={styles.bucketContainer}>
|
|
|
|
|
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.bucketScroll}>
|
|
|
|
|
+ {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 (
|
|
|
|
|
+ <TouchableOpacity
|
|
|
|
|
+ key={index}
|
|
|
|
|
+ style={[styles.bucketItem, isActive && styles.bucketItemActive]}
|
|
|
|
|
+ onPress={() => handleBucketChange(index)}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Text style={[styles.bucketText, isActive && styles.bucketTextActive]}>
|
|
|
|
|
+ {start}~{end}
|
|
|
|
|
+ </Text>
|
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
|
+ );
|
|
|
|
|
+ })}
|
|
|
|
|
+ </ScrollView>
|
|
|
|
|
+ </View>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
{/* List */}
|
|
{/* List */}
|
|
|
<View style={styles.listContainer}>
|
|
<View style={styles.listContainer}>
|
|
|
<FlatList
|
|
<FlatList
|
|
@@ -172,14 +201,12 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
|
|
|
renderItem={renderItem}
|
|
renderItem={renderItem}
|
|
|
keyExtractor={(item, index) => item.id || String(index)}
|
|
keyExtractor={(item, index) => item.id || String(index)}
|
|
|
contentContainerStyle={styles.listContent}
|
|
contentContainerStyle={styles.listContent}
|
|
|
- onEndReached={handleLoadMore}
|
|
|
|
|
- onEndReachedThreshold={0.5}
|
|
|
|
|
refreshing={refreshing}
|
|
refreshing={refreshing}
|
|
|
onRefresh={handleRefresh}
|
|
onRefresh={handleRefresh}
|
|
|
ListEmptyComponent={
|
|
ListEmptyComponent={
|
|
|
!loading ? <Text style={styles.emptyText}>暂无数据</Text> : null
|
|
!loading ? <Text style={styles.emptyText}>暂无数据</Text> : null
|
|
|
}
|
|
}
|
|
|
- ListFooterComponent={loading && !refreshing ? <ActivityIndicator /> : null}
|
|
|
|
|
|
|
+ ListFooterComponent={loading && !refreshing ? <ActivityIndicator style={{ marginTop: 20 }} /> : null}
|
|
|
/>
|
|
/>
|
|
|
</View>
|
|
</View>
|
|
|
|
|
|
|
@@ -371,4 +398,31 @@ const styles = StyleSheet.create({
|
|
|
marginTop: 20,
|
|
marginTop: 20,
|
|
|
color: '#999',
|
|
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',
|
|
|
|
|
+ },
|
|
|
});
|
|
});
|