ソースを参照

一番赏箱子选择效果优化

zbb 3 ヶ月 前
コミット
b0efb2d1c7

+ 73 - 12
app/boxInBox/components/BoxPopup.tsx

@@ -21,12 +21,12 @@ export interface BoxPopupRef {
 }
 
 export const BoxPopup = forwardRef<BoxPopupRef, BoxPopupProps>(({ onSelect }, ref) => {
-  const [visible, setVisible] = useState(false);
-  const [list, setList] = useState<BoxItem[]>([]);
+  const [activeBucket, setActiveBucket] = useState(0);
 
   useImperativeHandle(ref, () => ({
     open: (data: BoxItem[]) => {
       setList(data);
+      setActiveBucket(0); // Reset to first bucket
       setVisible(true);
     },
     close: () => setVisible(false),
@@ -37,6 +37,11 @@ export const BoxPopup = forwardRef<BoxPopupRef, BoxPopupProps>(({ onSelect }, re
     setVisible(false);
   };
 
+  // Bucket logic
+  const BUCKET_SIZE = 100;
+  const buckets = Math.ceil(list.length / BUCKET_SIZE);
+  const displayList = list.slice(activeBucket * BUCKET_SIZE, (activeBucket + 1) * BUCKET_SIZE);
+
   return (
     <Modal visible={visible} transparent animationType="slide" onRequestClose={() => setVisible(false)}>
       <View style={styles.overlay}>
@@ -51,12 +56,36 @@ export const BoxPopup = forwardRef<BoxPopupRef, BoxPopupProps>(({ onSelect }, re
             </TouchableOpacity>
           </View>
 
+          {/* 区间选择器 (仅当数量超过100时显示) */}
+          {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, list.length);
+                  const isActive = activeBucket === index;
+                  return (
+                    <TouchableOpacity 
+                      key={index} 
+                      style={[styles.bucketItem, isActive && styles.bucketItemActive]} 
+                      onPress={() => setActiveBucket(index)}
+                    >
+                      <Text style={[styles.bucketText, isActive && styles.bucketTextActive]}>
+                        {start}~{end}
+                      </Text>
+                    </TouchableOpacity>
+                  );
+                })}
+              </ScrollView>
+            </View>
+          )}
+
           {/* 盒子列表 */}
           <View style={styles.content}>
             <ScrollView style={styles.scrollView} showsVerticalScrollIndicator={false}>
               <View style={styles.grid}>
-                {list.map((item, index) => (
-                  <TouchableOpacity key={index} style={styles.item} onPress={() => handleSelect(item)}>
+                {displayList.map((item, index) => (
+                  <TouchableOpacity key={item.boxNumber} style={styles.item} onPress={() => handleSelect(item)}>
                     <View style={styles.imgBox}>
                       <Image
                         source={{ uri: item.isCompleted ? Images.box.detail.packagingBox2 : Images.box.detail.packagingBox1 }}
@@ -91,7 +120,8 @@ const styles = StyleSheet.create({
     backgroundColor: '#ffc901',
     borderTopLeftRadius: 10,
     borderTopRightRadius: 10,
-    maxHeight: '80%',
+    maxHeight: '85%', // Increased slightly
+    paddingBottom: 20,
   },
   titleSection: {
     position: 'relative',
@@ -123,14 +153,44 @@ const styles = StyleSheet.create({
     color: '#333',
     fontWeight: 'bold',
   },
+  // Bucket Styles
+  bucketContainer: {
+    marginBottom: 10,
+    height: 40,
+  },
+  bucketScroll: {
+    paddingHorizontal: 10,
+  },
+  bucketItem: {
+    paddingHorizontal: 15,
+    paddingVertical: 6,
+    borderRadius: 15,
+    backgroundColor: '#fff',
+    marginRight: 10,
+    borderWidth: 1,
+    borderColor: '#eee',
+    justifyContent: 'center',
+  },
+  bucketItemActive: {
+    backgroundColor: '#ff8c16',
+    borderColor: '#ff8c16',
+  },
+  bucketText: {
+    fontSize: 12,
+    color: '#333',
+    fontWeight: '500',
+  },
+  bucketTextActive: {
+    color: '#fff',
+    fontWeight: 'bold',
+  },
   content: {
     backgroundColor: '#fff',
     marginHorizontal: 10,
-    marginBottom: 20,
     borderRadius: 13,
     borderWidth: 1,
     borderColor: '#000',
-    maxHeight: 470,
+    flex: 1, // Let it take remaining space
   },
   scrollView: {
     padding: 10,
@@ -138,6 +198,7 @@ const styles = StyleSheet.create({
   grid: {
     flexDirection: 'row',
     flexWrap: 'wrap',
+    paddingBottom: 20,
   },
   item: {
     width: '25%',
@@ -145,8 +206,8 @@ const styles = StyleSheet.create({
     marginBottom: 15,
   },
   imgBox: {
-    width: 54,
-    height: 54,
+    width: 50, // Slightly smaller to fit better
+    height: 50,
   },
   boxImg: {
     width: '100%',
@@ -155,17 +216,17 @@ const styles = StyleSheet.create({
   numBadge: {
     backgroundColor: '#959595',
     borderRadius: 15,
-    paddingHorizontal: 10,
+    paddingHorizontal: 8,
     paddingVertical: 2,
     marginTop: 5,
   },
   numText: {
     color: '#fff',
-    fontSize: 12,
+    fontSize: 10, // Smaller font
     fontWeight: '500',
   },
   remaining: {
-    fontSize: 12,
+    fontSize: 10,
     color: '#999',
     fontWeight: 'bold',
     marginTop: 3,

+ 79 - 25
components/award-detail-yfs/BoxSelectionModal.tsx

@@ -7,6 +7,7 @@ import {
     Dimensions,
     FlatList,
     Modal,
+    ScrollView,
     StyleSheet,
     Text,
     TouchableOpacity,
@@ -40,25 +41,25 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
     const [list, setList] = useState<any[]>([]);
     const [loading, setLoading] = 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 (pageNum === 1 && !isRefresh) setLoading(true);
+      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
+        // 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) {
         console.error('Failed to load boxes:', error);
       } finally {
@@ -71,7 +72,9 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
       show: () => {
         setVisible(true);
         setActiveTab(TABS[0]);
-        loadData(1, TABS[0].value);
+        setActiveBucket(0);
+        setTotal(0);
+        loadData(0, TABS[0].value);
       },
       close: () => {
         setVisible(false);
@@ -80,17 +83,16 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
 
     const handleTabChange = (tab: typeof TABS[0]) => {
       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 = () => {
-        loadData(1, activeTab.value, true);
+        loadData(activeBucket, activeTab.value, true);
     }
 
     const renderItem = ({ item, index }: { item: any; index: number }) => {
@@ -105,7 +107,8 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
                 }}
             >
                 <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 style={styles.itemContent}>
@@ -129,6 +132,8 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
         );
     };
 
+    const buckets = Math.ceil(total / BUCKET_SIZE);
+
     return (
       <Modal visible={visible} transparent animationType="slide" onRequestClose={() => setVisible(false)}>
         <View style={styles.overlay}>
@@ -150,7 +155,7 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
                     </TouchableOpacity>
                 </View>
 
-                {/* Tabs */}
+                {/* Level Tabs */}
                 <View style={styles.tabs}>
                     {TABS.map(tab => (
                         <TouchableOpacity 
@@ -165,6 +170,30 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
                     ))}
                 </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 */}
                 <View style={styles.listContainer}>
                      <FlatList
@@ -172,14 +201,12 @@ export const BoxSelectionModal = forwardRef<BoxSelectionModalRef, BoxSelectionMo
                         renderItem={renderItem}
                         keyExtractor={(item, index) => item.id || String(index)}
                         contentContainerStyle={styles.listContent}
-                        onEndReached={handleLoadMore}
-                        onEndReachedThreshold={0.5}
                         refreshing={refreshing}
                         onRefresh={handleRefresh}
                         ListEmptyComponent={
                             !loading ? <Text style={styles.emptyText}>暂无数据</Text> : null
                         }
-                        ListFooterComponent={loading && !refreshing ? <ActivityIndicator /> : null}
+                        ListFooterComponent={loading && !refreshing ? <ActivityIndicator style={{ marginTop: 20 }} /> : null}
                     />
                 </View>
 
@@ -371,4 +398,31 @@ const styles = StyleSheet.create({
       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',
+  },
 });

+ 26 - 23
components/award-detail-yfs/NumSelectionModal.tsx

@@ -264,18 +264,20 @@ export const NumSelectionModal = forwardRef<NumSelectionModalRef, NumSelectionMo
 
                 {/* Tabs for Ranges */}
                 {tabs.length > 1 && (
-                     <View style={styles.tabs}>
-                        {tabs.map(tab => (
-                            <TouchableOpacity 
-                                key={tab.title} 
-                                style={[styles.tab, activeTab?.value === tab.value && styles.activeTab]}
-                                onPress={() => handleTabChange(tab)}
-                            >
-                                <Text style={[styles.tabText, activeTab?.value === tab.value && styles.activeTabText]}>
-                                    {tab.title}
-                                </Text>
-                            </TouchableOpacity>
-                        ))}
+                     <View style={styles.tabsContainer}>
+                        <ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.tabsScroll}>
+                            {tabs.map(tab => (
+                                <TouchableOpacity 
+                                    key={tab.title} 
+                                    style={[styles.tab, activeTab?.value === tab.value && styles.activeTab]}
+                                    onPress={() => handleTabChange(tab)}
+                                >
+                                    <Text style={[styles.tabText, activeTab?.value === tab.value && styles.activeTabText]}>
+                                        {tab.title}
+                                    </Text>
+                                </TouchableOpacity>
+                            ))}
+                        </ScrollView>
                     </View>
                 )}
 
@@ -368,21 +370,22 @@ const styles = StyleSheet.create({
       alignItems: 'center',
   },
   closeText: { fontSize: 18, color: '#a2a2a2', marginTop: -2 },
-  tabs: {
-      flexDirection: 'row',
-      flexWrap: 'wrap',
-      paddingHorizontal: 10,
-      marginBottom: 10,
+  tabsContainer: {
+    height: 40,
+    marginBottom: 10,
+  },
+  tabsScroll: {
+    paddingHorizontal: 10,
   },
   tab: {
-      paddingVertical: 5,
-      paddingHorizontal: 10,
+      paddingVertical: 6,
+      paddingHorizontal: 12,
       backgroundColor: '#f5f5f5',
-      borderRadius: 4,
-      marginRight: 8,
-      marginBottom: 5,
+      borderRadius: 20,
+      marginRight: 10,
+      justifyContent: 'center',
   },
-  activeTab: { backgroundColor: '#ffdb4d' },
+  activeTab: { backgroundColor: '#FFC900' },
   tabText: { fontSize: 12, color: '#666' },
   activeTabText: { color: '#000', fontWeight: 'bold' },
   grid: {