import { useEffect, useRef, useState } from 'react'; import { message } from 'antd'; import { PointsMallAPI, PointsMallCategory, PointsMallOverview, PointsMallProduct, PointsMallProductsResponse } from '../../api'; import { MOCK_OVERVIEW, MOCK_PRODUCTS } from './mocks'; import type { ExchangeFormValues, SortKey } from './types'; export function usePointsMallPageLogic() { const [overviewLoading, setOverviewLoading] = useState(false); const [productsLoading, setProductsLoading] = useState(false); const [overview, setOverview] = useState(null); const [categories, setCategories] = useState([]); const [categoryId, setCategoryId] = useState('all'); const [q, setQ] = useState(''); const [sort, setSort] = useState('popular'); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(12); const [productsRes, setProductsRes] = useState(null); const [exchangeModalVisible, setExchangeModalVisible] = useState(false); const [confirmModalVisible, setConfirmModalVisible] = useState(false); const [selectedProduct, setSelectedProduct] = useState(null); const [exchangeQuantity, setExchangeQuantity] = useState(1); const [pendingOrderId, setPendingOrderId] = useState(null); const [pendingExpiresAt, setPendingExpiresAt] = useState(null); const [exchangeLoading, setExchangeLoading] = useState(false); const exchangePrepareInFlightRef = useRef(false); const loadOverview = async () => { setOverviewLoading(true); try { const [meRes, categoriesRes, announcementsRes, bannersRes, promoEntriesRes] = await Promise.allSettled([ PointsMallAPI.me(), PointsMallAPI.categories(), PointsMallAPI.announcements(), PointsMallAPI.banners(), PointsMallAPI.promoEntries(), ]); const rawMe = meRes.status === 'fulfilled' ? meRes.value : { points: 0, level: 'Lv.0' }; const me = { ...rawMe, points: Number((rawMe as any)?.points ?? 0), }; const loadedCats = categoriesRes.status === 'fulfilled' ? categoriesRes.value : MOCK_OVERVIEW.categories; const cats = loadedCats?.length ? loadedCats : MOCK_OVERVIEW.categories; const announcements = announcementsRes.status === 'fulfilled' ? announcementsRes.value : MOCK_OVERVIEW.announcements; const banners = bannersRes.status === 'fulfilled' ? bannersRes.value : MOCK_OVERVIEW.banners; const promoEntries = promoEntriesRes.status === 'fulfilled' ? promoEntriesRes.value : MOCK_OVERVIEW.promoEntries; setOverview({ me, categories: cats, announcements, banners, promoEntries }); setCategories(cats); if (!cats?.some((c) => c.id === categoryId) && cats?.[0]?.id) { setCategoryId(cats[0].id); } if (meRes.status === 'rejected') { message.error('获取Token信息失败,请稍后重试'); } } catch { message.error('获取Token信息失败,请稍后重试'); setOverview({ ...MOCK_OVERVIEW, me: { points: 0, level: 'Lv.0' } }); setCategories(MOCK_OVERVIEW.categories); } finally { setOverviewLoading(false); } }; const loadProducts = async () => { setProductsLoading(true); try { const res = await PointsMallAPI.products({ categoryId: categoryId === 'all' ? undefined : categoryId, q, sort, page, pageSize, }); if (Array.isArray(res.items) && res.items.length > 0) { setProductsRes(res); return; } const fallback = MOCK_PRODUCTS.filter((p) => (categoryId === 'all' ? true : p.categoryId === categoryId)).filter((p) => q ? (p.name + p.subtitle).toLowerCase().includes(q.toLowerCase()) : true ); setProductsRes({ page, pageSize, total: fallback.length, items: fallback.slice((page - 1) * pageSize, page * pageSize), }); } catch { const filtered = MOCK_PRODUCTS.filter((p) => (categoryId === 'all' ? true : p.categoryId === categoryId)).filter((p) => q ? (p.name + p.subtitle).toLowerCase().includes(q.toLowerCase()) : true ); setProductsRes({ page, pageSize, total: filtered.length, items: filtered.slice((page - 1) * pageSize, page * pageSize), }); } finally { setProductsLoading(false); } }; useEffect(() => { loadOverview(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { loadProducts(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [categoryId, q, sort, page, pageSize]); const banner = overview?.banners?.[0]; const promoEntries = overview?.promoEntries || []; const products = productsRes?.items || []; const total = productsRes?.total || 0; const userPoints = overview?.me?.points || 0; const totalSpentUSD = overview?.me?.totalSpentUSD; const handleExchangeClick = async (product: PointsMallProduct) => { setSelectedProduct(product); setExchangeQuantity(1); setConfirmModalVisible(true); }; const handleConfirmExchange = async () => { if (!selectedProduct) return; if (userPoints < selectedProduct.pointsPrice * exchangeQuantity) return; if (exchangePrepareInFlightRef.current) return; exchangePrepareInFlightRef.current = true; setExchangeLoading(true); try { const res = await PointsMallAPI.exchangePrepare(selectedProduct.id, exchangeQuantity); setPendingOrderId(res.orderId); setPendingExpiresAt(res.expiresAt || new Date(Date.now() + 30 * 60 * 1000).toISOString()); setConfirmModalVisible(false); setExchangeModalVisible(true); const remainingPoints = res.remainingPoints; if (typeof remainingPoints === 'number') { setOverview((prev) => { if (!prev) return prev; return { ...prev, me: { ...prev.me, points: remainingPoints } }; }); } message.success('已冻结Token并预扣库存,请继续填写收件信息'); } catch (e: any) { const msg = e?.response?.data?.message || e?.response?.data?.error || e?.message || '兑换失败,请稍后重试'; message.error(msg); } finally { setExchangeLoading(false); exchangePrepareInFlightRef.current = false; } }; const handleExchangeSubmit = async (values: ExchangeFormValues) => { if (!selectedProduct || !pendingOrderId) return; setExchangeLoading(true); try { const res = await PointsMallAPI.exchangeSubmitShipping(pendingOrderId, values); setExchangeModalVisible(false); setConfirmModalVisible(false); setPendingOrderId(null); setPendingExpiresAt(null); setSelectedProduct(null); setExchangeQuantity(1); const remainingPoints = res.remainingPoints; if (typeof remainingPoints === 'number') { setOverview((prev) => { if (!prev) return prev; return { ...prev, me: { ...prev.me, points: remainingPoints } }; }); } else { loadOverview(); } loadProducts(); message.success('兑换成功'); } catch (e: any) { const msg = e?.response?.data?.message || e?.response?.data?.error || e?.message || '提交收件信息失败,请稍后重试'; message.error(msg); } finally { setExchangeLoading(false); } }; return { overview, overviewLoading, categories, categoryId, q, sort, page, pageSize, productsLoading, products, total, userPoints, totalSpentUSD, banner, promoEntries, exchangeModalVisible, confirmModalVisible, selectedProduct, exchangeQuantity, pendingOrderId, pendingExpiresAt, exchangeLoading, setCategoryId, setQ, setSort, setPage, setPageSize, handleExchangeClick, handleConfirmExchange, setExchangeModalVisible, setConfirmModalVisible, setPendingOrderId, setPendingExpiresAt, setSelectedProduct, setExchangeQuantity, setOverview, handleExchangeSubmit, }; } export type PointsMallPageLogicOutput = ReturnType;