diff --git a/app/screens/ProductDetailScreen.tsx b/app/screens/ProductDetailScreen.tsx
index dacfc39..dff6dc3 100644
--- a/app/screens/ProductDetailScreen.tsx
+++ b/app/screens/ProductDetailScreen.tsx
@@ -29,7 +29,7 @@ import HeartRedIcon from "../components/HeartIconRed";
import ShareIcon from "../components/ShareIcon";
import { Image as ExpoImage, ImageBackground } from "expo-image";
import useUserStore from "../store/user";
-import { getSubjectTransLanguage,getSkuTransLanguage } from "../utils/languageUtils";
+import { getSubjectTransLanguage } from "../utils/languageUtils";
import { useTranslation } from "react-i18next";
import { getBurialPointData } from "../store/burialPoint";
import useBurialPointStore from "../store/burialPoint";
@@ -609,17 +609,7 @@ export const ProductDetailScreen = () => {
{
setIsHeartRed(!isHeartRed);
- Alert.alert("收藏成功", "是否我的查看收藏", [
- {
- text: "取消",
- onPress: () => console.log("取消 pressed"),
- style: "cancel",
- },
- {
- text: "确定",
- onPress: () => console.log("确定 pressed"),
- },
- ]);
+ productApi.collectProduct(route.params.offer_id);
}}
>
{isHeartRed ? (
diff --git a/app/screens/function/Collection.tsx b/app/screens/function/Collection.tsx
index 6815376..b9ddd01 100644
--- a/app/screens/function/Collection.tsx
+++ b/app/screens/function/Collection.tsx
@@ -1,4 +1,4 @@
-import React from "react";
+import React, { useState, useEffect, useCallback } from "react";
import {
View,
Text,
@@ -9,34 +9,52 @@ import {
SafeAreaView,
StatusBar,
Platform,
+ ActivityIndicator,
+ RefreshControl,
+ Alert,
} from "react-native";
import widthUtils from "../../utils/widthUtils";
import fontSize from "../../utils/fontsizeUtils";
import BackIcon from "../../components/BackIcon";
import { useNavigation } from "@react-navigation/native";
+import { productApi } from "../../services/api/productApi";
+// 收藏商品项组件
const FavoriteItem = ({
- image,
- title,
- price,
+ favoriteItem,
+ onDelete,
}: {
- image: string;
- title: string;
- price: number;
+ favoriteItem: any;
+ onDelete: (favoriteId: number) => void;
}) => {
+ const product = favoriteItem.product;
+
return (
-
+
- {title}
+ {product.subject_trans || product.subject}
+
+
+ {product.currency}{product.min_price}
+ {product.vip_discount > 0 && (
+
+ {product.currency}{product.original_min_price}
+
+ )}
- ¥{price}
加入购物车
-
+ onDelete(favoriteItem.favorite_id)}
+ >
删除
@@ -47,57 +65,149 @@ const FavoriteItem = ({
export const Collection = () => {
const navigation = useNavigation();
- const data = [
- {
- id: "1",
- image: "https://img.alicdn.com/imgextra/i1/1234567890/O1CN01Item1.jpg",
- title: "韩版宽松休闲卫衣女春秋款薄款学生ins潮",
- price: 89.0,
- },
- {
- id: "2",
- image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
- title: "小米无线蓝牙耳机半入耳式 轻巧便携",
- 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,
- },
- {
- id: "4",
- image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
- title: "小米无线蓝牙耳机半入耳式 轻巧便携",
- price: 129.0,
- },
- {
- id: "5",
- image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
- title: "小米无线蓝牙耳机半入耳式 轻巧便携",
- price: 129.0,
- },
- {
- id: "6",
- image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
- title: "小米无线蓝牙耳机半入耳式 轻巧便携",
- price: 129.0,
- },
- {
- id: "7",
- image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
- title: "小米无线蓝牙耳机半入耳式 轻巧便携",
- price: 129.0,
- },
- {
- id: "82",
- image: "https://img.alicdn.com/imgextra/i2/2234567890/O1CN01Item2.jpg",
- title: "小米无线蓝牙耳机半入耳式 轻巧便携",
- price: 129.0,
- },
- // 可以继续添加更多商品
- ];
+
+ // 状态管理
+ const [favorites, setFavorites] = useState([]);
+ 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: "删除",
+ style: "destructive",
+ onPress: async () => {
+ try {
+ // 这里需要调用删除收藏的API,如果API没有提供,先在前端删除
+ setFavorites(prev => prev.filter(item => item.favorite_id !== favoriteId));
+ setTotal(prev => prev - 1);
+ } catch (error) {
+ console.error("删除收藏失败:", error);
+ Alert.alert("错误", "删除失败,请重试");
+ }
+ }
+ }
+ ]
+ );
+ }, []);
+
+ // 滚动事件处理
+ const handleScroll = useCallback((event: any) => {
+ const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
+ const paddingToBottom = 20;
+
+ if (layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom) {
+ handleLoadMore();
+ }
+ }, [handleLoadMore]);
+
+ // 渲染加载指示器
+ const renderLoadingIndicator = () => (
+
+
+ 加载中...
+
+ );
+
+ // 渲染底部加载更多指示器
+ const renderFooter = () => {
+ if (loadingMore) {
+ return (
+
+
+ 加载更多...
+
+ );
+ }
+
+ if (!hasMore && favorites.length > 0) {
+ return (
+
+ 没有更多数据了
+
+ );
+ }
+
+ return null;
+ };
+
+ // 渲染空状态
+ const renderEmptyState = () => (
+
+ 暂无收藏商品
+ 去逛逛,收藏喜欢的商品吧
+
+ );
return (
@@ -110,14 +220,43 @@ export const Collection = () => {
>
- 我的收藏
+ 我的收藏 ({total})
-
- {data.map((item) => (
-
- ))}
-
+
+ {loading ? (
+ renderLoadingIndicator()
+ ) : (
+
+ }
+ onScroll={handleScroll}
+ scrollEventThrottle={16}
+ >
+ {favorites.length === 0 ? (
+ renderEmptyState()
+ ) : (
+ <>
+ {favorites.map((item) => (
+
+ ))}
+ {renderFooter()}
+ >
+ )}
+
+ )}
);
@@ -172,6 +311,8 @@ const styles = StyleSheet.create({
backgroundColor: "#fff",
borderBottomWidth: 1,
borderBottomColor: "#eee",
+ marginBottom: 8,
+ borderRadius: 8,
},
image: {
width: widthUtils(100, 100).width,
@@ -193,29 +334,75 @@ const styles = StyleSheet.create({
color: "#f40",
fontSize: fontSize(16),
marginBottom: 10,
+ fontWeight: "600",
+ },
+ originalPrice: {
+ color: "#999",
+ fontSize: fontSize(14),
+ textDecorationLine: "line-through",
+ marginLeft: 8,
},
actions: {
flexDirection: "row",
gap: 8,
},
btn: {
- paddingVertical: 4,
- paddingHorizontal: 10,
- borderRadius: 3,
- borderWidth: 1,
+ flex: 1,
+ paddingVertical: 6,
+ paddingHorizontal: 12,
+ borderRadius: 4,
+ alignItems: "center",
},
cart: {
- borderColor: "#ff5000",
+ backgroundColor: "#ff5100",
},
delete: {
- borderColor: "#ccc",
+ backgroundColor: "#f5f5f5",
+ borderWidth: 1,
+ borderColor: "#ddd",
},
cartText: {
- fontSize: fontSize(12),
- color: "#ff5000",
+ color: "#fff",
+ fontSize: fontSize(14),
},
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),
+ 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",
},
});
diff --git a/app/services/api/productApi.ts b/app/services/api/productApi.ts
index abd7f74..1931a6a 100644
--- a/app/services/api/productApi.ts
+++ b/app/services/api/productApi.ts
@@ -193,6 +193,40 @@ export interface HotTerms {
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 = {
// 搜索商品
@@ -218,6 +252,20 @@ export interface HotTerms {
return apiService.post('/api/search/image_search/?user_id='+data.user_id,{
image_base64: data.image_base64
});
+ },
+ // 收藏商品
+ collectProduct: (offer_id: string) => {
+ return apiService.post(`/api/favorites/`,{
+ offer_id:offer_id
+ });
+ },
+ // 收藏商品列表
+ getCollectProductList: (page:number,page_size:number) => {
+ return apiService.get(`/api/favorites/?page=${page}&page_size=${page_size}`);
+ },
+ // 删除收藏商品
+ deleteCollectProduct: (offer_id: number) => {
+ return apiService.delete(`/api/favorites/${offer_id}/`);
}
}