Compare commits

..

No commits in common. '20fd71f3bfe15329e93023a2299b5e3bb405086c' and '9a3148bce7766c7efda4d025e63865022452fa57' have entirely different histories.

  1. 14
      app/screens/ProductDetailScreen.tsx
  2. 333
      app/screens/function/Collection.tsx
  3. 48
      app/services/api/productApi.ts

14
app/screens/ProductDetailScreen.tsx

@ -29,7 +29,7 @@ import HeartRedIcon from "../components/HeartIconRed";
import ShareIcon from "../components/ShareIcon"; import ShareIcon from "../components/ShareIcon";
import { Image as ExpoImage, ImageBackground } from "expo-image"; import { Image as ExpoImage, ImageBackground } from "expo-image";
import useUserStore from "../store/user"; import useUserStore from "../store/user";
import { getSubjectTransLanguage } from "../utils/languageUtils"; import { getSubjectTransLanguage,getSkuTransLanguage } from "../utils/languageUtils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { getBurialPointData } from "../store/burialPoint"; import { getBurialPointData } from "../store/burialPoint";
import useBurialPointStore from "../store/burialPoint"; import useBurialPointStore from "../store/burialPoint";
@ -610,7 +610,17 @@ export const ProductDetailScreen = () => {
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
setIsHeartRed(!isHeartRed); setIsHeartRed(!isHeartRed);
productApi.collectProduct(route.params.offer_id); Alert.alert("收藏成功", "是否我的查看收藏", [
{
text: "取消",
onPress: () => console.log("取消 pressed"),
style: "cancel",
},
{
text: "确定",
onPress: () => console.log("确定 pressed"),
},
]);
}} }}
> >
{isHeartRed ? ( {isHeartRed ? (

333
app/screens/function/Collection.tsx

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback } from "react"; import React from "react";
import { import {
View, View,
Text, Text,
@ -9,52 +9,34 @@ import {
SafeAreaView, SafeAreaView,
StatusBar, StatusBar,
Platform, Platform,
ActivityIndicator,
RefreshControl,
Alert,
} from "react-native"; } from "react-native";
import widthUtils from "../../utils/widthUtils"; import widthUtils from "../../utils/widthUtils";
import fontSize from "../../utils/fontsizeUtils"; import fontSize from "../../utils/fontsizeUtils";
import BackIcon from "../../components/BackIcon"; import BackIcon from "../../components/BackIcon";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import { productApi } from "../../services/api/productApi";
// 收藏商品项组件
const FavoriteItem = ({ const FavoriteItem = ({
favoriteItem, image,
onDelete, title,
price,
}: { }: {
favoriteItem: any; image: string;
onDelete: (favoriteId: number) => void; title: string;
price: number;
}) => { }) => {
const product = favoriteItem.product;
return ( return (
<View style={styles.item}> <View style={styles.item}>
<Image <Image source={{ uri: image }} style={styles.image} />
source={{ uri: product.product_image_urls[0] || 'https://via.placeholder.com/100' }}
style={styles.image}
/>
<View style={styles.info}> <View style={styles.info}>
<Text style={styles.title} numberOfLines={2}> <Text style={styles.title} numberOfLines={2}>
{product.subject_trans || product.subject} {title}
</Text>
<Text style={styles.price}>
{product.currency}{product.min_price}
{product.vip_discount > 0 && (
<Text style={styles.originalPrice}>
{product.currency}{product.original_min_price}
</Text>
)}
</Text> </Text>
<Text style={styles.price}>{price}</Text>
<View style={styles.actions}> <View style={styles.actions}>
<TouchableOpacity style={[styles.btn, styles.cart]}> <TouchableOpacity style={[styles.btn, styles.cart]}>
<Text style={styles.cartText}></Text> <Text style={styles.cartText}></Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity style={[styles.btn, styles.delete]}>
style={[styles.btn, styles.delete]}
onPress={() => onDelete(favoriteItem.favorite_id)}
>
<Text style={styles.deleteText}></Text> <Text style={styles.deleteText}></Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -65,149 +47,57 @@ const FavoriteItem = ({
export const Collection = () => { export const Collection = () => {
const navigation = useNavigation(); const navigation = useNavigation();
const data = [
// 状态管理
const [favorites, setFavorites] = useState<any[]>([]);
const [loading, setLoading] = useState(true);
const [loadingMore, setLoadingMore] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [currentPage, setCurrentPage] = useState(1);
const [total, setTotal] = useState(0);
const PAGE_SIZE = 10;
// 获取收藏列表
const fetchFavorites = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
try {
if (!isRefresh && page === 1) {
setLoading(true);
} else if (!isRefresh && page > 1) {
setLoadingMore(true);
}
const response = await productApi.getCollectProductList(page, PAGE_SIZE);
if (isRefresh || page === 1) {
setFavorites(response.items);
setCurrentPage(1);
} else {
// 避免重复添加数据
setFavorites(prev => {
const newItems = response.items.filter(
newItem => !prev.some(existingItem => existingItem.favorite_id === newItem.favorite_id)
);
return [...prev, ...newItems];
});
}
setTotal(response.total);
setHasMore(response.items.length === PAGE_SIZE && favorites.length + response.items.length < response.total);
if (!isRefresh && page > 1) {
setCurrentPage(page);
}
} catch (error) {
console.error("获取收藏列表失败:", error);
Alert.alert("错误", "获取收藏列表失败,请重试");
} finally {
setLoading(false);
setLoadingMore(false);
setRefreshing(false);
}
}, [favorites.length]);
// 初始化加载
useEffect(() => {
fetchFavorites(1);
}, []);
// 下拉刷新
const handleRefresh = useCallback(() => {
if (refreshing) return;
setRefreshing(true);
fetchFavorites(1, true);
}, [refreshing, fetchFavorites]);
// 触底加载更多
const handleLoadMore = useCallback(() => {
if (!hasMore || loadingMore || loading) return;
fetchFavorites(currentPage + 1);
}, [hasMore, loadingMore, loading, currentPage, fetchFavorites]);
// 删除收藏
const handleDelete = useCallback(async (favoriteId: number) => {
Alert.alert(
"确认删除",
"确定要删除这个收藏吗?",
[
{ text: "取消", style: "cancel" },
{ {
text: "删除", id: "1",
style: "destructive", image: "https://img.alicdn.com/imgextra/i1/1234567890/O1CN01Item1.jpg",
onPress: async () => { title: "韩版宽松休闲卫衣女春秋款薄款学生ins潮",
try { price: 89.0,
// 这里需要调用删除收藏的API,如果API没有提供,先在前端删除 },
setFavorites(prev => prev.filter(item => item.favorite_id !== favoriteId)); {
setTotal(prev => prev - 1); id: "2",
} catch (error) { image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
console.error("删除收藏失败:", error); title: "小米无线蓝牙耳机半入耳式 轻巧便携",
Alert.alert("错误", "删除失败,请重试"); price: 129.0,
} },
} {
} id: "3",
] image: "https://img.alicdn.com/imgextra/i3/3234567890/O1CN01Item3.jpg",
); title: "华为MateBook X Pro 2020款 13.9英寸全面屏笔记本电脑",
}, []); price: 10999.0,
},
// 滚动事件处理 {
const handleScroll = useCallback((event: any) => { id: "4",
const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
const paddingToBottom = 20; title: "小米无线蓝牙耳机半入耳式 轻巧便携",
price: 129.0,
if (layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom) { },
handleLoadMore(); {
} id: "5",
}, [handleLoadMore]); image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
title: "小米无线蓝牙耳机半入耳式 轻巧便携",
// 渲染加载指示器 price: 129.0,
const renderLoadingIndicator = () => ( },
<View style={styles.loadingContainer}> {
<ActivityIndicator size="large" color="#ff5100" /> id: "6",
<Text style={styles.loadingText}>...</Text> image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
</View> title: "小米无线蓝牙耳机半入耳式 轻巧便携",
); price: 129.0,
},
// 渲染底部加载更多指示器 {
const renderFooter = () => { id: "7",
if (loadingMore) { image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
return ( title: "小米无线蓝牙耳机半入耳式 轻巧便携",
<View style={styles.footerContainer}> price: 129.0,
<ActivityIndicator size="small" color="#ff5100" /> },
<Text style={styles.footerText}>...</Text> {
</View> id: "82",
); image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
} title: "小米无线蓝牙耳机半入耳式 轻巧便携",
price: 129.0,
if (!hasMore && favorites.length > 0) { },
return ( // 可以继续添加更多商品
<View style={styles.footerContainer}> ];
<Text style={styles.footerText}></Text>
</View>
);
}
return null;
};
// 渲染空状态
const renderEmptyState = () => (
<View style={styles.emptyContainer}>
<Text style={styles.emptyText}></Text>
<Text style={styles.emptySubtext}></Text>
</View>
);
return ( return (
<SafeAreaView style={styles.safeArea}> <SafeAreaView style={styles.safeArea}>
@ -220,43 +110,14 @@ export const Collection = () => {
> >
<BackIcon size={fontSize(22)} /> <BackIcon size={fontSize(22)} />
</TouchableOpacity> </TouchableOpacity>
<Text style={styles.titles}> ({total})</Text> <Text style={styles.titles}></Text>
<View style={styles.placeholder} /> <View style={styles.placeholder} />
</View> </View>
<ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
{loading ? ( {data.map((item) => (
renderLoadingIndicator() <FavoriteItem key={item.id} {...item} />
) : (
<ScrollView
style={styles.container}
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={handleRefresh}
colors={["#ff5100"]}
tintColor="#ff5100"
/>
}
onScroll={handleScroll}
scrollEventThrottle={16}
>
{favorites.length === 0 ? (
renderEmptyState()
) : (
<>
{favorites.map((item) => (
<FavoriteItem
key={item.favorite_id}
favoriteItem={item}
onDelete={handleDelete}
/>
))} ))}
{renderFooter()}
</>
)}
</ScrollView> </ScrollView>
)}
</View> </View>
</SafeAreaView> </SafeAreaView>
); );
@ -311,8 +172,6 @@ const styles = StyleSheet.create({
backgroundColor: "#fff", backgroundColor: "#fff",
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: "#eee", borderBottomColor: "#eee",
marginBottom: 8,
borderRadius: 8,
}, },
image: { image: {
width: widthUtils(100, 100).width, width: widthUtils(100, 100).width,
@ -334,75 +193,29 @@ const styles = StyleSheet.create({
color: "#f40", color: "#f40",
fontSize: fontSize(16), fontSize: fontSize(16),
marginBottom: 10, marginBottom: 10,
fontWeight: "600",
},
originalPrice: {
color: "#999",
fontSize: fontSize(14),
textDecorationLine: "line-through",
marginLeft: 8,
}, },
actions: { actions: {
flexDirection: "row", flexDirection: "row",
gap: 8, gap: 8,
}, },
btn: { btn: {
flex: 1, paddingVertical: 4,
paddingVertical: 6, paddingHorizontal: 10,
paddingHorizontal: 12, borderRadius: 3,
borderRadius: 4, borderWidth: 1,
alignItems: "center",
}, },
cart: { cart: {
backgroundColor: "#ff5100", borderColor: "#ff5000",
}, },
delete: { delete: {
backgroundColor: "#f5f5f5", borderColor: "#ccc",
borderWidth: 1,
borderColor: "#ddd",
}, },
cartText: { cartText: {
color: "#fff", fontSize: fontSize(12),
fontSize: fontSize(14), color: "#ff5000",
}, },
deleteText: { deleteText: {
color: "#666",
fontSize: fontSize(14),
},
loadingContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
paddingTop: 100,
},
loadingText: {
marginTop: 10,
color: "#666",
fontSize: fontSize(14),
},
footerContainer: {
paddingVertical: 20,
alignItems: "center",
justifyContent: "center",
},
footerText: {
color: "#999",
fontSize: fontSize(12), fontSize: fontSize(12),
marginTop: 5,
},
emptyContainer: {
flex: 1,
justifyContent: "center",
alignItems: "center",
paddingTop: 100,
},
emptyText: {
fontSize: fontSize(16),
color: "#666",
marginBottom: 8,
},
emptySubtext: {
fontSize: fontSize(14),
color: "#999", color: "#999",
}, },
}); });

48
app/services/api/productApi.ts

@ -193,40 +193,6 @@ export interface HotTerms {
terms: string[]; terms: string[];
} }
interface SuccessfulResponse {
total: number;
page: number;
page_size: number;
items: FavoriteItem[];
}
interface FavoriteItem {
favorite_id: number;
user_id: number;
offer_id: number;
create_time: string; // ISO 8601 date string
product: Favorite;
}
interface Favorite {
offer_id: number;
subject: string;
subject_trans: string;
subject_trans_en: string;
subject_trans_ar: string;
product_image_urls: string[];
min_price: number;
max_price: number;
original_min_price: number;
original_max_price: number;
currency: string;
vip_discount: number;
vip_level: number;
discount_percent: number;
sold_out: number;
}
// 搜索商品 // 搜索商品
export const productApi = { export const productApi = {
// 搜索商品 // 搜索商品
@ -252,20 +218,6 @@ interface Favorite {
return apiService.post<Products>('/api/search/image_search/?user_id='+data.user_id,{ return apiService.post<Products>('/api/search/image_search/?user_id='+data.user_id,{
image_base64: data.image_base64 image_base64: data.image_base64
}); });
},
// 收藏商品
collectProduct: (offer_id: string) => {
return apiService.post<Products>(`/api/favorites/`,{
offer_id:offer_id
});
},
// 收藏商品列表
getCollectProductList: (page:number,page_size:number) => {
return apiService.get<SuccessfulResponse>(`/api/favorites/?page=${page}&page_size=${page_size}`);
},
// 删除收藏商品
deleteCollectProduct: (offer_id: number) => {
return apiService.delete<Products>(`/api/favorites/${offer_id}/`);
} }
} }

Loading…
Cancel
Save