|
|
@@ -0,0 +1,90 @@
|
|
|
+// src/components/CouponList.tsx
|
|
|
+import React, { useState, useEffect } from 'react';
|
|
|
+import { useInfiniteScroll } from './useInfiniteScroll';
|
|
|
+
|
|
|
+// 优惠券数据类型
|
|
|
+interface CouponItem {
|
|
|
+ id: number;
|
|
|
+ discount: string; // 优惠金额(如 $15 OFF)
|
|
|
+ desc: string; // 优惠描述
|
|
|
+ valid: string; // 有效期
|
|
|
+}
|
|
|
+
|
|
|
+const CouponList: React.FC = () => {
|
|
|
+ // 初始化优惠券数据
|
|
|
+ const [coupons, setCoupons] = useState<CouponItem[]>([
|
|
|
+ { id: 1, discount: '$15 OFF', desc: 'For All Products', valid: 'Valid:6/2/2026-12/31/2026' },
|
|
|
+ { id: 2, discount: '$10 OFF', desc: 'For All Products', valid: 'Valid:6/2/2026-12/31/2026' },
|
|
|
+ ]);
|
|
|
+ const [hasMore, setHasMore] = useState(true); // 模拟:初始有更多数据,加载2次后无更多
|
|
|
+
|
|
|
+ // 加载更多优惠券(模拟异步请求)
|
|
|
+ const loadMoreCoupons = async () => {
|
|
|
+ return new Promise<void>((resolve) => {
|
|
|
+ setTimeout(() => {
|
|
|
+ if (coupons.length >= 6) { // 模拟最多加载6条
|
|
|
+ setHasMore(false);
|
|
|
+ resolve();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 模拟新增数据
|
|
|
+ const newCoupons = [
|
|
|
+ {
|
|
|
+ id: coupons.length + 1,
|
|
|
+ discount: `$${5 * (coupons.length + 1)} OFF`,
|
|
|
+ desc: 'For All Products',
|
|
|
+ valid: 'Valid:6/2/2026-12/31/2026'
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ setCoupons(prev => [...prev, ...newCoupons]);
|
|
|
+ resolve();
|
|
|
+ }, 800);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 复用懒加载Hook
|
|
|
+ const { containerRef, isLoading } = useInfiniteScroll({
|
|
|
+ loadMore: loadMoreCoupons,
|
|
|
+ hasMore,
|
|
|
+ });
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ ref={containerRef}
|
|
|
+ className="h-[calc(100vh-80px)] overflow-auto px-2 py-4"
|
|
|
+ >
|
|
|
+ {/* 优惠券列表 */}
|
|
|
+ <div className="space-y-4">
|
|
|
+ {coupons.map((item) => (
|
|
|
+ <div
|
|
|
+ key={item.id}
|
|
|
+ className="flex items-center bg-pink-100 rounded-lg overflow-hidden"
|
|
|
+ >
|
|
|
+ {/* 优惠信息区 */}
|
|
|
+ <div className="flex-1 p-4 text-pink-600">
|
|
|
+ <h3 className="text-2xl font-bold">{item.discount}</h3>
|
|
|
+ <p className="text-sm mt-1">{item.desc}</p>
|
|
|
+ <p className="text-xs mt-2 text-pink-500">{item.valid}</p>
|
|
|
+ </div>
|
|
|
+ {/* 分割线 */}
|
|
|
+ <div className="w-0.5 bg-pink-300 h-16 relative">
|
|
|
+ <div className="absolute top-0 left-[-4px] w-2 h-2 bg-white border border-pink-300 rounded-full"></div>
|
|
|
+ <div className="absolute bottom-0 left-[-4px] w-2 h-2 bg-white border border-pink-300 rounded-full"></div>
|
|
|
+ </div>
|
|
|
+ {/* 使用按钮区 */}
|
|
|
+ <div className="px-6 py-4 font-bold text-black">
|
|
|
+ Use Now
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 加载状态/无更多提示 */}
|
|
|
+ <div className="text-center mt-4 text-gray-400 text-sm">
|
|
|
+ {isLoading ? 'Loading...' : hasMore ? '' : 'No More Data'}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default CouponList;
|