|
|
@@ -1,342 +1,471 @@
|
|
|
-import { Images } from '@/constants/images';
|
|
|
-import ServiceAward from '@/services/award';
|
|
|
-import { Ionicons } from '@expo/vector-icons';
|
|
|
-import { Stack, useRouter } from 'expo-router';
|
|
|
-import React, { useState } from 'react';
|
|
|
+import { Image } from 'expo-image';
|
|
|
+import { useRouter } from 'expo-router';
|
|
|
+import React, { useCallback, useEffect, useState } from 'react';
|
|
|
import {
|
|
|
ActivityIndicator,
|
|
|
- Dimensions,
|
|
|
+ Alert,
|
|
|
FlatList,
|
|
|
- Image,
|
|
|
ImageBackground,
|
|
|
+ Platform,
|
|
|
+ RefreshControl,
|
|
|
+ ScrollView,
|
|
|
StatusBar,
|
|
|
StyleSheet,
|
|
|
Text,
|
|
|
TouchableOpacity,
|
|
|
- View,
|
|
|
+ View
|
|
|
} from 'react-native';
|
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
|
|
|
|
-const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
|
|
+import { Images } from '@/constants/images';
|
|
|
+import {
|
|
|
+ getStore,
|
|
|
+ getTakeList,
|
|
|
+ moveOutSafeStore,
|
|
|
+ moveToSafeStore,
|
|
|
+} from '@/services/award';
|
|
|
+import CheckoutModal from './components/CheckoutModal';
|
|
|
|
|
|
-const LEVEL_MAP: any = {
|
|
|
- D: { title: '普通', color: '#666666' },
|
|
|
- C: { title: '隐藏', color: '#9745e6' },
|
|
|
- B: { title: '欧皇', color: '#ff0000' },
|
|
|
- A: { title: '超神', color: '#ffae00' },
|
|
|
+const LEVEL_MAP: Record<string, { title: string; color: string }> = {
|
|
|
+ A: { title: '超神', color: '#ffae00' },
|
|
|
+ B: { title: '欧皇', color: '#ff0000' },
|
|
|
+ C: { title: '隐藏', color: '#9745e6' },
|
|
|
+ D: { title: '普通', color: '#666666' },
|
|
|
};
|
|
|
|
|
|
-export default function StoreScreen() {
|
|
|
- const router = useRouter();
|
|
|
- const insets = useSafeAreaInsets();
|
|
|
- const [list, setList] = useState<any[]>([]);
|
|
|
- const [loading, setLoading] = useState(false);
|
|
|
- const [tabIndex, setTabIndex] = useState(0);
|
|
|
- const [page, setPage] = useState(1);
|
|
|
- const [hasMore, setHasMore] = useState(true);
|
|
|
+const LEVEL_TABS = [
|
|
|
+ { title: '全部', value: '' },
|
|
|
+ { title: '普通', value: 'D' },
|
|
|
+ { title: '隐藏', value: 'C' },
|
|
|
+ { title: '欧皇', value: 'B' },
|
|
|
+ { title: '超神', value: 'A' },
|
|
|
+];
|
|
|
+
|
|
|
+const FROM_TYPE_MAP: Record<string, string> = {
|
|
|
+ LUCK: '奖池', MALL: '商城', LUCK_ROOM: '福利房', LUCK_WHEEL: '魔天轮',
|
|
|
+ DOLL_MACHINE: '扭蛋', ACTIVITY: '活动', SUBSTITUTE: '置换', TRANSFER: '转赠',
|
|
|
+};
|
|
|
|
|
|
- const tabs = ['未使用', '保险柜', '已提货'];
|
|
|
+interface StoreItem {
|
|
|
+ id: string;
|
|
|
+ level: string;
|
|
|
+ safeFlag: number;
|
|
|
+ magicAmount?: number;
|
|
|
+ fromRelationType: string;
|
|
|
+ spu: { id: string; name: string; cover: string };
|
|
|
+}
|
|
|
|
|
|
- React.useEffect(() => {
|
|
|
- setPage(1);
|
|
|
- setList([]);
|
|
|
- setHasMore(true);
|
|
|
- loadData(1);
|
|
|
- }, [tabIndex]);
|
|
|
+interface PickupItem {
|
|
|
+ tradeNo: string;
|
|
|
+ createTime: string;
|
|
|
+ status: number;
|
|
|
+ contactName: string;
|
|
|
+ contactNo: string;
|
|
|
+ province: string;
|
|
|
+ city: string;
|
|
|
+ district: string;
|
|
|
+ address: string;
|
|
|
+ expressAmount: number;
|
|
|
+ paymentTime?: string;
|
|
|
+ paymentTimeoutTime?: string;
|
|
|
+ cancelRemark?: string;
|
|
|
+ itemList: Array<{ id: string; spuId: string; level: string; cover: string }>;
|
|
|
+}
|
|
|
|
|
|
- const loadData = async (pageNum: number) => {
|
|
|
- if (!hasMore && pageNum > 1) return;
|
|
|
+const STATUS_MAP: Record<number, { text: string; color: string }> = {
|
|
|
+ 0: { text: '待支付运费', color: '#ff6b00' },
|
|
|
+ 1: { text: '已进仓库进行配货', color: '#ff6b00' },
|
|
|
+ 2: { text: '待收货', color: '#ff6b00' },
|
|
|
+ 10: { text: '已取消', color: '#ff6b00' },
|
|
|
+ 11: { text: '超时取消', color: '#ff6b00' },
|
|
|
+ 12: { text: '系统取消', color: '#ff6b00' },
|
|
|
+ 99: { text: '已完成', color: '#52c41a' },
|
|
|
+};
|
|
|
|
|
|
- try {
|
|
|
- if (pageNum === 1) setLoading(true);
|
|
|
+export default function StoreScreen() {
|
|
|
+ const router = useRouter();
|
|
|
+ const insets = useSafeAreaInsets();
|
|
|
+ const [mainTabIndex, setMainTabIndex] = useState(0);
|
|
|
+ const [levelTabIndex, setLevelTabIndex] = useState(0);
|
|
|
+ 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);
|
|
|
+ const [checkMap, setCheckMap] = useState<Record<string, StoreItem>>({});
|
|
|
+ const [checkoutVisible, setCheckoutVisible] = useState(false);
|
|
|
|
|
|
- let res;
|
|
|
- if (tabIndex === 0) {
|
|
|
- // Not in safe, unused (status=0)
|
|
|
- res = await ServiceAward.getStore(pageNum, 20, 0); // Use smaller size for pagination demo
|
|
|
- } else if (tabIndex === 1) {
|
|
|
- // In safe, unused (status=0)
|
|
|
- res = await ServiceAward.getStore(pageNum, 20, 1);
|
|
|
+ const mainTabs = ['未使用', '保险柜', '已提货'];
|
|
|
+
|
|
|
+ const loadData = useCallback(async (pageNum: number, isRefresh = false) => {
|
|
|
+ if (loading && !isRefresh) return;
|
|
|
+ if (!hasMore && pageNum > 1 && !isRefresh) return;
|
|
|
+ try {
|
|
|
+ if (pageNum === 1) setLoading(true);
|
|
|
+ let res: any;
|
|
|
+ if (mainTabIndex === 0) {
|
|
|
+ res = await getStore(pageNum, 20, 0, LEVEL_TABS[levelTabIndex].value);
|
|
|
+ } else if (mainTabIndex === 1) {
|
|
|
+ res = await getStore(pageNum, 20, 1);
|
|
|
+ } else {
|
|
|
+ res = await getTakeList(pageNum, 20);
|
|
|
+ }
|
|
|
+ let records = Array.isArray(res) ? res : (res?.records || res || []);
|
|
|
+ // 处理已提货数据,合并相同商品
|
|
|
+ if (mainTabIndex === 2 && records.length > 0) {
|
|
|
+ records = records.map((item: PickupItem) => {
|
|
|
+ const goodsMap: Record<string, { total: number; data: any }> = {};
|
|
|
+ (item.itemList || []).forEach((goods: any) => {
|
|
|
+ const key = `${goods.spuId}_${goods.level}`;
|
|
|
+ if (goodsMap[key]) {
|
|
|
+ goodsMap[key].total += 1;
|
|
|
} else {
|
|
|
- // Picked up
|
|
|
- res = await ServiceAward.getTakeList(pageNum, 20);
|
|
|
+ goodsMap[key] = { total: 1, data: goods };
|
|
|
}
|
|
|
+ });
|
|
|
+ return { ...item, groupedList: Object.values(goodsMap) };
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (records.length < 20) setHasMore(false);
|
|
|
+ if (pageNum === 1 || isRefresh) setList(records);
|
|
|
+ else setList(prev => [...prev, ...records]);
|
|
|
+ } catch (e) {
|
|
|
+ console.error('加载仓库数据失败:', e);
|
|
|
+ } finally {
|
|
|
+ setLoading(false);
|
|
|
+ setRefreshing(false);
|
|
|
+ }
|
|
|
+ }, [mainTabIndex, levelTabIndex, loading, hasMore]);
|
|
|
|
|
|
- const records = Array.isArray(res) ? res : (res?.records || []);
|
|
|
+ useEffect(() => {
|
|
|
+ setPage(1); setList([]); setHasMore(true); setCheckMap({});
|
|
|
+ loadData(1, true);
|
|
|
+ }, [mainTabIndex]);
|
|
|
|
|
|
- if (records.length < 20) {
|
|
|
- setHasMore(false);
|
|
|
- }
|
|
|
+ useEffect(() => {
|
|
|
+ if (mainTabIndex === 0) {
|
|
|
+ setPage(1); setList([]); setHasMore(true); setCheckMap({});
|
|
|
+ loadData(1, true);
|
|
|
+ }
|
|
|
+ }, [levelTabIndex]);
|
|
|
|
|
|
- if (pageNum === 1) {
|
|
|
- setList(records);
|
|
|
- } else {
|
|
|
- setList(prev => [...prev, ...records]);
|
|
|
- }
|
|
|
- } catch (e) {
|
|
|
- console.error(e);
|
|
|
- } finally {
|
|
|
- setLoading(false);
|
|
|
- }
|
|
|
- };
|
|
|
+ const handleRefresh = () => { setRefreshing(true); setPage(1); setHasMore(true); loadData(1, true); };
|
|
|
+ const handleLoadMore = () => { if (!loading && hasMore) { const np = page + 1; setPage(np); loadData(np); } };
|
|
|
|
|
|
- const handleLoadMore = () => {
|
|
|
- if (!loading && hasMore) {
|
|
|
- const nextPage = page + 1;
|
|
|
- setPage(nextPage);
|
|
|
- loadData(nextPage);
|
|
|
- }
|
|
|
- };
|
|
|
+ const handleChoose = (item: StoreItem) => {
|
|
|
+ if (item.safeFlag === 1 && mainTabIndex === 0) return;
|
|
|
+ setCheckMap(prev => {
|
|
|
+ const newMap = { ...prev };
|
|
|
+ if (newMap[item.id]) delete newMap[item.id];
|
|
|
+ else newMap[item.id] = item;
|
|
|
+ return newMap;
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
- const handleLock = async (item: any) => {
|
|
|
- // Implement lock/unlock logic if needed
|
|
|
- };
|
|
|
+ const handleLock = async (item: StoreItem, index: number) => {
|
|
|
+ const res = await moveToSafeStore([item.id]);
|
|
|
+ if (res) {
|
|
|
+ const newList = [...list]; newList[index] = { ...item, safeFlag: 1 }; setList(newList);
|
|
|
+ setCheckMap(prev => { const m = { ...prev }; delete m[item.id]; return m; });
|
|
|
+ showAlert('已锁定到保险柜');
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- const renderItem = ({ item }: { item: any }) => (
|
|
|
- <ImageBackground
|
|
|
- source={{ uri: Images.mine.storeItemBg }}
|
|
|
- style={styles.cell}
|
|
|
- resizeMode="stretch"
|
|
|
- >
|
|
|
- <View style={styles.cellHeader}>
|
|
|
- <View style={styles.headerLeft}>
|
|
|
- <View style={[styles.checkBox]} />
|
|
|
- <Text style={[styles.levelTitle, { color: LEVEL_MAP[item.level]?.color || '#333' }]}>
|
|
|
- {LEVEL_MAP[item.level]?.title || '未知'}
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- <TouchableOpacity style={styles.lockBox} onPress={() => handleLock(item)}>
|
|
|
- <Text style={styles.lockText}>{item.safeFlag !== 1 ? '锁定' : '解锁'}</Text>
|
|
|
- <Image
|
|
|
- source={{ uri: item.safeFlag !== 1 ? Images.mine.lock : Images.mine.unlock }}
|
|
|
- style={styles.lockIcon}
|
|
|
- />
|
|
|
- </TouchableOpacity>
|
|
|
- </View>
|
|
|
+ const handleUnlock = async (item: StoreItem, index: number) => {
|
|
|
+ const res = await moveOutSafeStore([item.id]);
|
|
|
+ if (res) {
|
|
|
+ if (mainTabIndex === 1) setList(list.filter((_, i) => i !== index));
|
|
|
+ else { const newList = [...list]; newList[index] = { ...item, safeFlag: 0 }; setList(newList); }
|
|
|
+ showAlert('已从保险柜移出');
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- <View style={styles.cellBody}>
|
|
|
- <ImageBackground
|
|
|
- source={{ uri: Images.mine.storeGoodsImgBg }}
|
|
|
- style={styles.goodsImgBg}
|
|
|
- >
|
|
|
- <Image source={{ uri: item.spu?.cover }} style={styles.goodsImg} resizeMode="contain" />
|
|
|
- </ImageBackground>
|
|
|
- <View style={styles.goodsInfo}>
|
|
|
- <Text style={styles.goodsName} numberOfLines={2}>{item.spu?.name}</Text>
|
|
|
- <Text style={styles.goodsSource}>
|
|
|
- 从{item.fromRelationType === 'LUCK' ? '奖池' : '其他'}获得
|
|
|
- </Text>
|
|
|
- </View>
|
|
|
- </View>
|
|
|
- </ImageBackground>
|
|
|
- );
|
|
|
+ const handleMoveOutAll = async () => {
|
|
|
+ const selected = Object.values(checkMap);
|
|
|
+ if (selected.length === 0) { showAlert('请至少选择一个商品!'); return; }
|
|
|
+ const res = await moveOutSafeStore(selected.map(i => i.id));
|
|
|
+ if (res) { setCheckMap({}); handleRefresh(); showAlert('已从保险柜移出'); }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleTakeGoods = () => {
|
|
|
+ const selected = Object.values(checkMap);
|
|
|
+ if (selected.length === 0) { showAlert('请至少选择一个商品!'); return; }
|
|
|
+ setCheckoutVisible(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleCheckoutSuccess = () => {
|
|
|
+ setCheckoutVisible(false);
|
|
|
+ setCheckMap({});
|
|
|
+ handleRefresh();
|
|
|
+ };
|
|
|
|
|
|
+ const showAlert = (msg: string) => {
|
|
|
+ if (Platform.OS === 'web') window.alert(msg);
|
|
|
+ else Alert.alert('提示', msg);
|
|
|
+ };
|
|
|
+
|
|
|
+ const renderStoreItem = ({ item, index }: { item: StoreItem; index: number }) => {
|
|
|
+ const levelInfo = LEVEL_MAP[item.level] || LEVEL_MAP.D;
|
|
|
+ const isChecked = !!checkMap[item.id];
|
|
|
+ const canSelect = mainTabIndex === 1 || item.safeFlag !== 1;
|
|
|
return (
|
|
|
- <View style={styles.container}>
|
|
|
- <Stack.Screen options={{ headerShown: false }} />
|
|
|
- <StatusBar barStyle="light-content" />
|
|
|
- <View style={[styles.header, { paddingTop: insets.top }]}>
|
|
|
- <TouchableOpacity style={styles.backBtn} onPress={() => router.back()}>
|
|
|
- <Ionicons name="chevron-back" size={24} color="#fff" />
|
|
|
- </TouchableOpacity>
|
|
|
- <Text style={styles.title}>仓库</Text>
|
|
|
- </View>
|
|
|
+ <ImageBackground source={{ uri: Images.mine.storeItemBg }} style={styles.cell} resizeMode="stretch">
|
|
|
+ <TouchableOpacity style={styles.cellHeader} onPress={() => canSelect && handleChoose(item)}>
|
|
|
+ <View style={styles.headerLeft}>
|
|
|
+ {canSelect && (
|
|
|
+ <View style={[styles.checkBox, isChecked && styles.checkBoxChecked]}>
|
|
|
+ {isChecked && <Text style={styles.checkIcon}>✓</Text>}
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+ <Text style={[styles.levelTitle, { color: levelInfo.color }]}>{levelInfo.title}</Text>
|
|
|
+ </View>
|
|
|
+ <TouchableOpacity style={styles.lockBox} onPress={() => item.safeFlag !== 1 ? handleLock(item, index) : handleUnlock(item, index)}>
|
|
|
+ <Text style={styles.lockText}>{item.safeFlag !== 1 ? '锁定' : '解锁'}</Text>
|
|
|
+ <Image source={{ uri: item.safeFlag !== 1 ? Images.mine.lock : Images.mine.unlock }} style={styles.lockIcon} />
|
|
|
+ </TouchableOpacity>
|
|
|
+ </TouchableOpacity>
|
|
|
+ <View style={styles.cellBody}>
|
|
|
+ <ImageBackground source={{ uri: Images.mine.storeGoodsImgBg }} style={styles.goodsImgBg}>
|
|
|
+ <Image source={{ uri: item.spu?.cover }} style={styles.goodsImg} contentFit="contain" />
|
|
|
+ </ImageBackground>
|
|
|
+ <View style={styles.goodsInfo}>
|
|
|
+ <Text style={styles.goodsName} numberOfLines={2}>{item.spu?.name}</Text>
|
|
|
+ <Text style={styles.goodsSource}>从{FROM_TYPE_MAP[item.fromRelationType] || '其他'}获得</Text>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ </ImageBackground>
|
|
|
+ );
|
|
|
+ };
|
|
|
|
|
|
- <ImageBackground
|
|
|
- source={{ uri: Images.mine.kaixinMineBg }}
|
|
|
- style={styles.background}
|
|
|
- resizeMode="cover"
|
|
|
- >
|
|
|
- <Image
|
|
|
- source={{ uri: Images.mine.kaixinMineHeadBg }}
|
|
|
- style={styles.headerBg}
|
|
|
- resizeMode="cover"
|
|
|
- />
|
|
|
+ const copyToClipboard = (text: string) => {
|
|
|
+ showAlert(`订单号已复制: ${text}`);
|
|
|
+ };
|
|
|
|
|
|
- <View style={[styles.content, { paddingTop: insets.top + 50 }]}>
|
|
|
- {/* Tabs */}
|
|
|
- <View style={styles.tabs}>
|
|
|
- {tabs.map((tab, index) => (
|
|
|
- <TouchableOpacity
|
|
|
- key={index}
|
|
|
- style={[styles.tabItem, tabIndex === index && styles.tabItemActive]}
|
|
|
- onPress={() => setTabIndex(index)}
|
|
|
- >
|
|
|
- <Text style={[styles.tabText, tabIndex === index && styles.tabTextActive]}>{tab}</Text>
|
|
|
- {tabIndex === index && <View style={styles.tabLine} />}
|
|
|
- </TouchableOpacity>
|
|
|
- ))}
|
|
|
- </View>
|
|
|
+ const showExpress = (item: PickupItem) => {
|
|
|
+ router.push({ pathname: '/store/packages' as any, params: { tradeNo: item.tradeNo } });
|
|
|
+ };
|
|
|
|
|
|
- <FlatList
|
|
|
- data={list}
|
|
|
- renderItem={renderItem}
|
|
|
- keyExtractor={(item, index) => index.toString()}
|
|
|
- contentContainerStyle={{ paddingHorizontal: 16, paddingBottom: 100 }}
|
|
|
- onEndReached={handleLoadMore}
|
|
|
- onEndReachedThreshold={0.1}
|
|
|
- ListFooterComponent={
|
|
|
- loading && list.length > 0 ? (
|
|
|
- <ActivityIndicator color="#fff" style={{ marginVertical: 10 }} />
|
|
|
- ) : null
|
|
|
- }
|
|
|
- ListEmptyComponent={
|
|
|
- !loading ? (
|
|
|
- <View style={styles.emptyBox}>
|
|
|
- <Text style={styles.emptyText}>暂无物品</Text>
|
|
|
- </View>
|
|
|
- ) : null
|
|
|
- }
|
|
|
- />
|
|
|
+ const renderPickupItem = ({ item }: { item: PickupItem & { groupedList?: Array<{ total: number; data: any }> } }) => {
|
|
|
+ const statusInfo = STATUS_MAP[item.status] || { text: '未知', color: '#999' };
|
|
|
+ return (
|
|
|
+ <ImageBackground source={{ uri: Images.mine.storeItemBg }} style={styles.pickupCell} resizeMode="stretch">
|
|
|
+ {/* 顶部信息 */}
|
|
|
+ <View style={styles.pickupTop}>
|
|
|
+ <Text style={styles.pickupTime}>下单时间:{item.createTime}</Text>
|
|
|
+ <Text style={[styles.pickupStatus, { color: statusInfo.color }]}>{statusInfo.text}</Text>
|
|
|
+ </View>
|
|
|
+ {item.status === 0 && item.paymentTimeoutTime && (
|
|
|
+ <Text style={styles.pickupTimeout}>{item.paymentTimeoutTime} 将自动取消该订单,如有优惠券,将自动退回</Text>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 收货地址 */}
|
|
|
+ <View style={styles.pickupAddress}>
|
|
|
+ <Text style={styles.locationIcon}>📍</Text>
|
|
|
+ <View style={styles.addressInfo}>
|
|
|
+ <Text style={styles.addressName}>{item.contactName},{item.contactNo}</Text>
|
|
|
+ <Text style={styles.addressDetail}>{item.province}{item.city}{item.district}{item.address}</Text>
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {/* 商品列表 */}
|
|
|
+ <View style={styles.pickupGoodsBox}>
|
|
|
+ <ScrollView horizontal showsHorizontalScrollIndicator={false} style={styles.pickupGoodsList}>
|
|
|
+ {(item.groupedList || []).map((goods, idx) => (
|
|
|
+ <View key={idx} style={styles.pickupGoodsItem}>
|
|
|
+ <Image source={{ uri: goods.data.cover }} style={styles.pickupGoodsImg} contentFit="contain" />
|
|
|
+ <View style={styles.pickupGoodsCount}>
|
|
|
+ <Text style={styles.pickupGoodsCountText}>x{goods.total}</Text>
|
|
|
</View>
|
|
|
- </ImageBackground>
|
|
|
+ </View>
|
|
|
+ ))}
|
|
|
+ </ScrollView>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {/* 订单号 */}
|
|
|
+ <View style={styles.pickupOrderRow}>
|
|
|
+ <Text style={styles.pickupOrderLabel}>订单号:</Text>
|
|
|
+ <Text style={styles.pickupOrderNo} numberOfLines={1}>{item.tradeNo}</Text>
|
|
|
+ <TouchableOpacity style={styles.copyBtn} onPress={() => copyToClipboard(item.tradeNo)}>
|
|
|
+ <Text style={styles.copyBtnText}>复制</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {item.paymentTime && (
|
|
|
+ <View style={styles.pickupInfoRow}>
|
|
|
+ <Text style={styles.pickupInfoLabel}>付款时间:</Text>
|
|
|
+ <Text style={styles.pickupInfoValue}>{item.paymentTime}</Text>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {item.status === 12 && item.cancelRemark && (
|
|
|
+ <View style={styles.pickupInfoRow}>
|
|
|
+ <Text style={styles.pickupInfoLabel}>备注</Text>
|
|
|
+ <Text style={[styles.pickupInfoValue, { color: '#ff6b00' }]}>{item.cancelRemark}</Text>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 底部操作 */}
|
|
|
+ <View style={styles.pickupBottom}>
|
|
|
+ <Text style={styles.pickupExpressAmount}>配送费:<Text style={styles.priceText}>¥{item.expressAmount}</Text></Text>
|
|
|
+ {[1, 2, 99].includes(item.status) && (
|
|
|
+ <TouchableOpacity style={styles.expressBtn} onPress={() => showExpress(item)}>
|
|
|
+ <Text style={styles.expressBtnText}>物流信息</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ )}
|
|
|
</View>
|
|
|
+ </ImageBackground>
|
|
|
);
|
|
|
+ };
|
|
|
+
|
|
|
+ const selectedCount = Object.keys(checkMap).length;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <View style={styles.container}>
|
|
|
+ <StatusBar barStyle="light-content" />
|
|
|
+ <ImageBackground source={{ uri: Images.mine.kaixinMineBg }} style={styles.background} resizeMode="cover">
|
|
|
+ <Image source={{ uri: Images.mine.kaixinMineHeadBg }} style={styles.headerBg} contentFit="cover" />
|
|
|
+ <View style={[styles.header, { paddingTop: insets.top }]}>
|
|
|
+ <TouchableOpacity style={styles.backBtn} onPress={() => router.back()}>
|
|
|
+ <Text style={styles.backIcon}>‹</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ <Text style={styles.title}>仓库</Text>
|
|
|
+ <View style={styles.placeholder} />
|
|
|
+ </View>
|
|
|
+ <View style={[styles.content, { paddingTop: insets.top + 50 }]}>
|
|
|
+ <View style={styles.mainTabs}>
|
|
|
+ {mainTabs.map((tab, index) => (
|
|
|
+ <TouchableOpacity key={index} style={styles.mainTabItem} onPress={() => setMainTabIndex(index)}>
|
|
|
+ <Text style={[styles.mainTabText, mainTabIndex === index && styles.mainTabTextActive]}>{tab}</Text>
|
|
|
+ {mainTabIndex === index && <View style={styles.mainTabLine} />}
|
|
|
+ </TouchableOpacity>
|
|
|
+ ))}
|
|
|
+ </View>
|
|
|
+ {mainTabIndex === 0 && (
|
|
|
+ <View style={styles.levelTabs}>
|
|
|
+ {LEVEL_TABS.map((tab, index) => (
|
|
|
+ <TouchableOpacity key={index} style={[styles.levelTabItem, levelTabIndex === index && styles.levelTabItemActive]} onPress={() => setLevelTabIndex(index)}>
|
|
|
+ <Text style={[styles.levelTabText, levelTabIndex === index && styles.levelTabTextActive]}>{tab.title}</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ ))}
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+ <FlatList
|
|
|
+ data={list as any[]}
|
|
|
+ renderItem={mainTabIndex === 2 ? renderPickupItem as any : renderStoreItem as any}
|
|
|
+ keyExtractor={(item: any, index) => item.id || item.tradeNo || index.toString()}
|
|
|
+ contentContainerStyle={styles.listContent}
|
|
|
+ refreshControl={<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} tintColor="#fff" />}
|
|
|
+ onEndReached={handleLoadMore}
|
|
|
+ onEndReachedThreshold={0.3}
|
|
|
+ ListHeaderComponent={mainTabIndex === 2 ? (
|
|
|
+ <View style={styles.pickupTip}>
|
|
|
+ <Text style={styles.pickupTipIcon}>⚠️</Text>
|
|
|
+ <Text style={styles.pickupTipText}>您的包裹一般在5个工作日内发货,如遇特殊情况可能会有延迟,敬请谅解~</Text>
|
|
|
+ </View>
|
|
|
+ ) : null}
|
|
|
+ ListFooterComponent={loading && list.length > 0 ? <ActivityIndicator color="#fff" style={{ marginVertical: 10 }} /> : null}
|
|
|
+ ListEmptyComponent={!loading ? <View style={styles.emptyBox}><Text style={styles.emptyText}>暂无物品</Text></View> : null}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ {mainTabIndex !== 2 && list.length > 0 && (
|
|
|
+ <View style={[styles.bottomBar, { paddingBottom: insets.bottom + 10 }]}>
|
|
|
+ <Text style={styles.bottomInfoText}>已选 <Text style={styles.bottomInfoCount}>{selectedCount}</Text> 件商品</Text>
|
|
|
+ <TouchableOpacity style={styles.bottomBtn} onPress={mainTabIndex === 0 ? handleTakeGoods : handleMoveOutAll}>
|
|
|
+ <ImageBackground source={{ uri: Images.common.loginBtn }} style={styles.bottomBtnBg} resizeMode="contain">
|
|
|
+ <Text style={styles.bottomBtnText}>{mainTabIndex === 0 ? '立即提货' : '移出保险柜'}</Text>
|
|
|
+ </ImageBackground>
|
|
|
+ </TouchableOpacity>
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 提货弹窗 */}
|
|
|
+ <CheckoutModal
|
|
|
+ visible={checkoutVisible}
|
|
|
+ selectedItems={Object.values(checkMap)}
|
|
|
+ onClose={() => setCheckoutVisible(false)}
|
|
|
+ onSuccess={handleCheckoutSuccess}
|
|
|
+ />
|
|
|
+ </ImageBackground>
|
|
|
+ </View>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
- container: {
|
|
|
- flex: 1,
|
|
|
- backgroundColor: '#1a1a2e',
|
|
|
- },
|
|
|
- header: {
|
|
|
- position: 'absolute',
|
|
|
- top: 0,
|
|
|
- left: 0,
|
|
|
- right: 0,
|
|
|
- zIndex: 100,
|
|
|
- alignItems: 'center',
|
|
|
- paddingBottom: 10,
|
|
|
- },
|
|
|
- headerBg: {
|
|
|
- position: 'absolute',
|
|
|
- top: 0,
|
|
|
- left: 0,
|
|
|
- width: '100%',
|
|
|
- height: 260,
|
|
|
- },
|
|
|
- backBtn: {
|
|
|
- position: 'absolute',
|
|
|
- left: 10,
|
|
|
- bottom: 10,
|
|
|
- zIndex: 101,
|
|
|
- },
|
|
|
- title: {
|
|
|
- color: '#fff',
|
|
|
- fontSize: 16,
|
|
|
- fontWeight: 'bold',
|
|
|
- },
|
|
|
- background: {
|
|
|
- flex: 1,
|
|
|
- width: '100%',
|
|
|
- height: '100%',
|
|
|
- },
|
|
|
- content: {
|
|
|
- flex: 1,
|
|
|
- },
|
|
|
- tabs: {
|
|
|
- flexDirection: 'row',
|
|
|
- justifyContent: 'space-around',
|
|
|
- marginBottom: 10,
|
|
|
- },
|
|
|
- tabItem: {
|
|
|
- paddingVertical: 10,
|
|
|
- paddingHorizontal: 10,
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- tabItemActive: {},
|
|
|
- tabText: {
|
|
|
- color: '#aaa',
|
|
|
- fontSize: 14,
|
|
|
- },
|
|
|
- tabTextActive: {
|
|
|
- color: '#fff',
|
|
|
- fontWeight: 'bold',
|
|
|
- fontSize: 16,
|
|
|
- },
|
|
|
- tabLine: {
|
|
|
- width: 20,
|
|
|
- height: 3,
|
|
|
- backgroundColor: '#fff',
|
|
|
- marginTop: 5,
|
|
|
- borderRadius: 2,
|
|
|
- },
|
|
|
- cell: {
|
|
|
- width: '100%',
|
|
|
- height: 154,
|
|
|
- marginBottom: 10,
|
|
|
- padding: 15,
|
|
|
- },
|
|
|
- cellHeader: {
|
|
|
- flexDirection: 'row',
|
|
|
- justifyContent: 'space-between',
|
|
|
- alignItems: 'center',
|
|
|
- borderBottomWidth: 1,
|
|
|
- borderBottomColor: 'rgba(0,0,0,0.1)',
|
|
|
- paddingBottom: 10,
|
|
|
- marginBottom: 10,
|
|
|
- },
|
|
|
- headerLeft: {
|
|
|
- flexDirection: 'row',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- checkBox: {
|
|
|
- width: 16,
|
|
|
- height: 16,
|
|
|
- borderWidth: 1,
|
|
|
- borderColor: '#999',
|
|
|
- marginRight: 10,
|
|
|
- backgroundColor: '#fff',
|
|
|
- },
|
|
|
- levelTitle: {
|
|
|
- fontSize: 16,
|
|
|
- fontWeight: 'bold',
|
|
|
- },
|
|
|
- lockBox: {
|
|
|
- flexDirection: 'row',
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- lockText: {
|
|
|
- fontSize: 12,
|
|
|
- color: '#666',
|
|
|
- },
|
|
|
- lockIcon: {
|
|
|
- width: 16,
|
|
|
- height: 16,
|
|
|
- marginLeft: 5,
|
|
|
- },
|
|
|
- cellBody: {
|
|
|
- flexDirection: 'row',
|
|
|
- },
|
|
|
- goodsImgBg: {
|
|
|
- width: 65,
|
|
|
- height: 65,
|
|
|
- justifyContent: 'center',
|
|
|
- alignItems: 'center',
|
|
|
- marginRight: 10,
|
|
|
- },
|
|
|
- goodsImg: {
|
|
|
- width: 60,
|
|
|
- height: 60,
|
|
|
- },
|
|
|
- goodsInfo: {
|
|
|
- flex: 1,
|
|
|
- justifyContent: 'space-between',
|
|
|
- },
|
|
|
- goodsName: {
|
|
|
- fontSize: 14,
|
|
|
- color: '#333',
|
|
|
- fontWeight: 'bold',
|
|
|
- },
|
|
|
- goodsSource: {
|
|
|
- fontSize: 12,
|
|
|
- color: '#999',
|
|
|
- },
|
|
|
- emptyBox: {
|
|
|
- marginTop: 100,
|
|
|
- alignItems: 'center',
|
|
|
- },
|
|
|
- emptyText: {
|
|
|
- color: '#999',
|
|
|
- },
|
|
|
+ container: { flex: 1, backgroundColor: '#1a1a2e' },
|
|
|
+ background: { flex: 1 },
|
|
|
+ headerBg: { position: 'absolute', top: 0, left: 0, width: '100%', height: 160 },
|
|
|
+ header: { position: 'absolute', top: 0, left: 0, right: 0, zIndex: 100, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 10, paddingBottom: 10 },
|
|
|
+ backBtn: { width: 40, height: 40, justifyContent: 'center', alignItems: 'center' },
|
|
|
+ backIcon: { fontSize: 32, color: '#fff', fontWeight: 'bold' },
|
|
|
+ title: { color: '#fff', fontSize: 16, fontWeight: 'bold' },
|
|
|
+ placeholder: { width: 40 },
|
|
|
+ content: { flex: 1 },
|
|
|
+ mainTabs: { flexDirection: 'row', justifyContent: 'space-around', paddingVertical: 10 },
|
|
|
+ mainTabItem: { alignItems: 'center', paddingHorizontal: 15 },
|
|
|
+ mainTabText: { color: '#aaa', fontSize: 14 },
|
|
|
+ mainTabTextActive: { color: '#fff', fontWeight: 'bold', fontSize: 16 },
|
|
|
+ mainTabLine: { width: 20, height: 3, backgroundColor: '#fff', marginTop: 5, borderRadius: 2 },
|
|
|
+ levelTabs: { flexDirection: 'row', paddingHorizontal: 10, paddingVertical: 8 },
|
|
|
+ levelTabItem: { paddingHorizontal: 12, paddingVertical: 6, marginRight: 8, borderRadius: 15, backgroundColor: 'rgba(255,255,255,0.1)' },
|
|
|
+ levelTabItemActive: { backgroundColor: '#FC7D2E' },
|
|
|
+ levelTabText: { color: '#aaa', fontSize: 12 },
|
|
|
+ levelTabTextActive: { color: '#fff' },
|
|
|
+ listContent: { paddingHorizontal: 10, paddingBottom: 150 },
|
|
|
+ cell: { width: '100%', minHeight: 154, marginBottom: 10, padding: 15 },
|
|
|
+ cellHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.1)', paddingBottom: 10, marginBottom: 10 },
|
|
|
+ headerLeft: { flexDirection: 'row', alignItems: 'center' },
|
|
|
+ checkBox: { width: 18, height: 18, borderWidth: 2, borderColor: '#000', backgroundColor: '#fff', marginRight: 10, justifyContent: 'center', alignItems: 'center' },
|
|
|
+ checkBoxChecked: { backgroundColor: '#000' },
|
|
|
+ checkIcon: { color: '#fff', fontSize: 12 },
|
|
|
+ levelTitle: { fontSize: 16, fontWeight: 'bold' },
|
|
|
+ lockBox: { flexDirection: 'row', alignItems: 'center' },
|
|
|
+ lockText: { fontSize: 12, color: '#666' },
|
|
|
+ lockIcon: { width: 16, height: 16, marginLeft: 5 },
|
|
|
+ cellBody: { flexDirection: 'row' },
|
|
|
+ goodsImgBg: { width: 65, height: 65, justifyContent: 'center', alignItems: 'center', marginRight: 10 },
|
|
|
+ goodsImg: { width: 55, height: 55 },
|
|
|
+ goodsInfo: { flex: 1, justifyContent: 'space-between' },
|
|
|
+ goodsName: { fontSize: 14, color: '#333', fontWeight: 'bold' },
|
|
|
+ goodsSource: { fontSize: 12, color: '#999' },
|
|
|
+ bottomBar: { position: 'absolute', bottom: 0, left: 0, right: 0, backgroundColor: 'rgba(0,0,0,0.8)', paddingHorizontal: 15, paddingTop: 10, alignItems: 'center' },
|
|
|
+ bottomInfoText: { color: '#fff', fontSize: 14, marginBottom: 10 },
|
|
|
+ bottomInfoCount: { fontWeight: 'bold', fontSize: 18 },
|
|
|
+ bottomBtn: { width: 260 },
|
|
|
+ bottomBtnBg: { width: 260, height: 60, justifyContent: 'center', alignItems: 'center' },
|
|
|
+ bottomBtnText: { color: '#000', fontSize: 16, fontWeight: 'bold' },
|
|
|
+ emptyBox: { marginTop: 100, alignItems: 'center' },
|
|
|
+ emptyText: { color: '#999', fontSize: 14 },
|
|
|
+ // 已提货样式
|
|
|
+ pickupTip: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#1FA4FF', padding: 8, marginBottom: 10, borderRadius: 4 },
|
|
|
+ pickupTipIcon: { fontSize: 14, marginRight: 6 },
|
|
|
+ pickupTipText: { flex: 1, color: '#fff', fontSize: 12 },
|
|
|
+ pickupCell: { width: '100%', marginBottom: 10, padding: 15 },
|
|
|
+ pickupTop: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.05)', paddingBottom: 10 },
|
|
|
+ pickupTime: { fontSize: 12, color: '#666' },
|
|
|
+ pickupStatus: { fontSize: 12, fontWeight: 'bold' },
|
|
|
+ pickupTimeout: { fontSize: 11, color: '#ff6b00', marginTop: 5 },
|
|
|
+ pickupAddress: { flexDirection: 'row', alignItems: 'flex-start', paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: 'rgba(0,0,0,0.05)' },
|
|
|
+ locationIcon: { fontSize: 16, marginRight: 10 },
|
|
|
+ addressInfo: { flex: 1 },
|
|
|
+ addressName: { fontSize: 14, color: '#333', fontWeight: 'bold' },
|
|
|
+ addressDetail: { fontSize: 12, color: '#666', marginTop: 4 },
|
|
|
+ pickupGoodsBox: { backgroundColor: '#f8f8f8', borderRadius: 6, padding: 10, marginVertical: 10 },
|
|
|
+ pickupGoodsList: { flexDirection: 'row' },
|
|
|
+ pickupGoodsItem: { width: 79, height: 103, backgroundColor: '#fff', borderRadius: 6, marginRight: 8, alignItems: 'center', justifyContent: 'center', position: 'relative', borderWidth: 1, borderColor: '#eaeaea' },
|
|
|
+ pickupGoodsImg: { width: 73, height: 85 },
|
|
|
+ pickupGoodsCount: { position: 'absolute', top: 0, right: 0, backgroundColor: '#ff6b00', borderRadius: 2, paddingHorizontal: 4, paddingVertical: 2 },
|
|
|
+ pickupGoodsCountText: { color: '#fff', fontSize: 10, fontWeight: 'bold' },
|
|
|
+ pickupOrderRow: { flexDirection: 'row', alignItems: 'center', borderTopWidth: 1, borderTopColor: 'rgba(0,0,0,0.05)', paddingTop: 14 },
|
|
|
+ pickupOrderLabel: { fontSize: 12, color: '#666' },
|
|
|
+ pickupOrderNo: { flex: 1, fontSize: 12, color: '#333', textAlign: 'right' },
|
|
|
+ copyBtn: { backgroundColor: '#1FA4FF', borderRadius: 4, paddingHorizontal: 6, paddingVertical: 4, marginLeft: 5 },
|
|
|
+ copyBtnText: { color: '#fff', fontSize: 12 },
|
|
|
+ pickupInfoRow: { flexDirection: 'row', alignItems: 'center', paddingTop: 10 },
|
|
|
+ pickupInfoLabel: { fontSize: 12, color: '#666' },
|
|
|
+ pickupInfoValue: { fontSize: 12, color: '#333' },
|
|
|
+ pickupBottom: { flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end', paddingTop: 10 },
|
|
|
+ pickupExpressAmount: { fontSize: 12, color: '#333' },
|
|
|
+ priceText: { color: '#ff6b00' },
|
|
|
+ expressBtn: { backgroundColor: '#1FA4FF', borderRadius: 12, paddingHorizontal: 10, paddingVertical: 6, marginLeft: 15 },
|
|
|
+ expressBtnText: { color: '#fff', fontSize: 12 },
|
|
|
});
|