Kaynağa Gözat

修复tab乱跳

zbb 3 ay önce
ebeveyn
işleme
59c13ae3be

+ 16 - 6
app/award-detail-yfs/index.tsx

@@ -260,12 +260,22 @@ export default function AwardDetailYfsScreen() {
                     <View style={{ height: 180 }} /> 
                     
                     <ProductSwiper 
-                        products={data?.luckGoodsList?.map((item: any) => ({
-                            cover: item.spu.cover,
-                            name: item.spu.name,
-                            level: item.level,
-                            quantity: item.quantity
-                        })) || []} 
+                        products={data?.luckGoodsList?.map((item: any) => {
+                            let prob = '0%';
+                            const total = data.leftQuantity || 1; 
+                            if (item.level === 'A') prob = ((data.leftQuantityA / total) * 100).toFixed(4) + '%';
+                            else if (item.level === 'B') prob = ((data.leftQuantityB / total) * 100).toFixed(4) + '%';
+                            else if (item.level === 'C') prob = ((data.leftQuantityC / total) * 100).toFixed(4) + '%';
+                            else if (item.level === 'D') prob = ((data.leftQuantityD / total) * 100).toFixed(4) + '%';
+                            
+                            return {
+                                cover: item.spu.cover,
+                                name: item.spu.name,
+                                level: item.level,
+                                probability: prob,
+                                quantity: item.quantity
+                            };
+                        }) || []} 
                     />
                     
                     <ImageBackground 

+ 18 - 24
app/award-detail/components/RecordModal.tsx

@@ -70,12 +70,15 @@ export const RecordModal = forwardRef<RecordModalRef, RecordModalProps>(({ poolI
            const currentLastId = isRefresh ? undefined : lastId;
            const res = await getBuyRecord(poolId, currentLastId, activeTab.value as any);
            if (res && res.length > 0) {
-               setData(prev => isRefresh ? res : [...prev, ...res]);
+               setData(prev => {
+                   if (isRefresh) return res;
+                   // Deduplicate based on ID
+                   const existingIds = new Set(prev.map(item => item.id));
+                   const newItems = res.filter(item => !existingIds.has(item.id));
+                   return [...prev, ...newItems];
+               });
                setLastId(res[res.length - 1].id);
-               // Assuming page size is 10 or 20. If less than that, no more data.
-               // Let's assume default is 10. If strictly < 10, definitely no more. 
-               // If == 10, maybe more. 
-               // Safer: if 0 returned, handled by outside if.
+               // Assuming page size is 10 or 20.
            } else {
                setHasMore(false);
            }
@@ -95,12 +98,6 @@ export const RecordModal = forwardRef<RecordModalRef, RecordModalProps>(({ poolI
         setLastId(undefined);
         setData([]);
         setHasMore(true);
-        // We can't immediately call loadData due to state update async nature, 
-        // but let's assume useEffect or immediate call with arg works.
-        // Actually best to useEffect on activeTab change?
-        // Or specific effect for tab
-        // Let's do explicit reload in effect or here
-        // setData is sync-ish in RN usually batching, let's use effect.
     };
 
     useEffect(() => {
@@ -117,10 +114,7 @@ export const RecordModal = forwardRef<RecordModalRef, RecordModalProps>(({ poolI
             <Image source={{ uri: item.avatar || Images.common.defaultAvatar }} style={styles.avatar} />
             <Text style={styles.nickname} numberOfLines={1}>{item.nickname}</Text>
             
-            {/* Level Icon - Assuming we have images for level text similar to legacy */}
-            {/* Legacy: :src="LEVEL_MAP[item.level].titleText" */}
-            {/* We don't have titleText in our config yet, need to map or use text */}
-            {/* For now use Text with color */}
+            {/* Level Icon */}
             <View style={[styles.levelTag, { borderColor: LEVEL_MAP[item.level]?.color || '#000' }]}>
                  <Text style={[styles.levelText, { color: LEVEL_MAP[item.level]?.color || '#000' }]}>
                      {LEVEL_MAP[item.level]?.title}
@@ -160,12 +154,12 @@ export const RecordModal = forwardRef<RecordModalRef, RecordModalProps>(({ poolI
                          <View style={styles.statItem}>
                              <Text style={styles.statNum}>{taggingA}</Text>
                              <Text style={styles.statLabel}>发未出</Text>
-                             <Text style={[styles.statLevel, { color: LEVEL_MAP.A.color }]}>超神款</Text>
+                             <Image source={{ uri: LEVEL_MAP.A.titleText }} style={{ width: 45, height: 16 }} contentFit="contain" />
                          </View>
                          <View style={styles.statItem}>
                              <Text style={styles.statNum}>{taggingB}</Text>
                              <Text style={styles.statLabel}>发未出</Text>
-                             <Text style={[styles.statLevel, { color: LEVEL_MAP.B.color }]}>欧皇款</Text>
+                             <Image source={{ uri: LEVEL_MAP.B.titleText }} style={{ width: 45, height: 16 }} contentFit="contain" />
                          </View>
                      </ImageBackground>
 
@@ -188,7 +182,7 @@ export const RecordModal = forwardRef<RecordModalRef, RecordModalProps>(({ poolI
                      <FlatList
                         data={data}
                         renderItem={renderItem}
-                        keyExtractor={(item, index) => item.id || String(index)}
+                        keyExtractor={(item, index) => (item.id ? `${item.id}_${index}` : String(index))}
                         style={styles.list}
                         contentContainerStyle={{ paddingBottom: 20 }}
                         onEndReached={() => loadData(false)}
@@ -257,17 +251,17 @@ const styles = StyleSheet.create({
         alignItems: 'center',
     },
     statNum: { 
-        fontSize: 16, 
-        fontWeight: 'bold', 
-        color: '#fff', 
-        textShadowColor: '#000', 
-        textShadowOffset: { width: -1, height: -1 },
+        fontSize: 24, 
+        fontWeight: '900', 
+        color: '#000', 
+        textShadowColor: '#fff', 
+        textShadowOffset: { width: 1, height: 1 },
         textShadowRadius: 1,
     },
     statLabel: { 
         fontSize: 12, 
         color: '#928D81', 
-        fontWeight: '300',
+        fontWeight: '300', 
         marginHorizontal: 8 
     },
     statLevel: { fontSize: 12, fontWeight: 'bold' },

+ 28 - 7
components/CustomTabBar.tsx

@@ -1,6 +1,6 @@
 import { Images } from '@/constants/images';
 import { Image } from 'expo-image';
-import { usePathname, useRouter } from 'expo-router';
+import { usePathname, useRouter, useSegments } from 'expo-router';
 import React from 'react';
 import { Dimensions, ImageBackground, StyleSheet, TouchableOpacity, View } from 'react-native';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -36,18 +36,39 @@ const tabList = [
 
 export function CustomTabBar() {
   const router = useRouter();
+  const segments = useSegments();
   const pathname = usePathname();
   const insets = useSafeAreaInsets();
 
   const getTabIndex = () => {
-    if (pathname === '/' || pathname === '/index') return 0;
-    if (pathname === '/box') return 1;
-    if (pathname === '/welfare') return 2;
-    if (pathname === '/mine') return 3; 
-    return 0;
+    // Check Box
+    if (segments[1] === 'box' || pathname?.startsWith('/box')) return 1;
+    // Check Welfare
+    if (segments[1] === 'welfare' || pathname?.startsWith('/welfare')) return 2;
+    // Check Mine
+    if (segments[1] === 'mine' || pathname?.startsWith('/mine')) return 3;
+    
+    // Check Home (Explicit)
+    // Home is usually index. or path /
+    if (segments[1] === 'index' || pathname === '/' || pathname === '/index') return 0;
+
+    // No valid tab match (e.g. navigating to detail page)
+    return -1;
   };
 
-  const currentIndex = getTabIndex();
+  // Initialize with correct value to avoid mount flash
+  const [currentIndex, setCurrentIndex] = React.useState(() => {
+      const idx = getTabIndex();
+      return idx === -1 ? 0 : idx;
+  });
+
+  // Update only when valid match found (avoids unmatch flash)
+  React.useEffect(() => {
+      const idx = getTabIndex();
+      if (idx !== -1) {
+          setCurrentIndex(idx);
+      }
+  }, [segments, pathname]);
 
   const handlePress = (index: number) => {
     const route = tabList[index].route;

+ 2 - 1
components/award-detail-yfs/ProductSwiper.tsx

@@ -19,6 +19,7 @@ interface Product {
   name: string;
   level: string;
   quantity?: number;
+  probability?: string;
 }
 
 interface ProductSwiperProps {
@@ -30,7 +31,7 @@ export default function ProductSwiper({ products, onShowSwipe }: ProductSwiperPr
     const [current, setCurrent] = useState(0);
 
     const getProbability = (item: Product) => {
-        return "10%"; // Placeholder
+        return item.probability || "";
     };
 
     if (!products || products.length === 0) return null;

+ 5 - 1
constants/config.ts

@@ -1,28 +1,32 @@
 import { Images } from './images';
 
-export const LEVEL_MAP: Record<string, { title: string; color: string; productItem: string; bg: string }> = {
+export const LEVEL_MAP: Record<string, { title: string; color: string; productItem: string; bg: string; titleText: string }> = {
   A: {
     title: '超神款',
     color: '#E82C31',
     productItem: Images.box.detail.productItemA,
     bg: Images.box.detail.levelA,
+    titleText: Images.box.detail.levelTextA,
   },
   B: {
     title: '欧皇款',
     color: '#FF9500',
     productItem: Images.box.detail.productItemB,
     bg: Images.box.detail.levelB,
+    titleText: Images.box.detail.levelTextB,
   },
   C: {
     title: '隐藏款',
     color: '#8E44AD',
     productItem: Images.box.detail.productItemC,
     bg: Images.box.detail.levelC,
+    titleText: Images.box.detail.levelTextC,
   },
   D: {
     title: '普通款',
     color: '#34495E', // Using a dark grey/blue as fallback if not strictly defined in legacy view, legacy had #666
     productItem: Images.box.detail.productItemD,
     bg: Images.box.detail.levelD,
+    titleText: Images.box.detail.levelTextD,
   },
 };

+ 1 - 0
supermart-app

@@ -0,0 +1 @@
+Subproject commit a23d34688f1b87ab6a7f7bc34f96fdbd029916ec