import React, { useState, useCallback, useRef } from 'react'; import { View, Text } from '@tarojs/components'; import Taro, { useDidShow, useReachBottom, usePullDownRefresh } from '@tarojs/taro'; import { listProducts } from '../../services/points'; import type { PointsProduct } from '../../services/points'; import { useAuthStore } from '../../stores/auth'; import { usePointsStore } from '../../stores/points'; import Loading from '../../components/Loading'; import { useElderClass } from '../../hooks/useElderClass'; import './index.scss'; const PRODUCT_TYPE_TABS = [ { key: '', label: '全部' }, { key: 'physical', label: '实物', char: '物' }, { key: 'service', label: '服务券', char: '券' }, { key: 'privilege', label: '权益', char: '权' }, ]; const TYPE_BG: Record = { physical: 'type-physical', service: 'type-service', privilege: 'type-privilege', }; export default function Mall() { const { currentPatient, loadPatients } = useAuthStore(); const { account, checkinStatus, refresh: refreshPoints, doCheckin } = usePointsStore(); const [products, setProducts] = useState([]); const [productType, setProductType] = useState(''); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [checkinLoading, setCheckinLoading] = useState(false); const [noProfile, setNoProfile] = useState(false); const loadingRef = useRef(false); const modeClass = useElderClass(); const fetchProducts = useCallback( async (pageNum: number, type: string, isRefresh = false) => { if (loadingRef.current) return; loadingRef.current = true; setLoading(true); try { const res = await listProducts({ page: pageNum, page_size: 10, product_type: type || undefined, }); const list = res.data || []; if (isRefresh) { setProducts(list); } else { setProducts((prev) => [...prev, ...list]); } setTotal(res.total); setPage(pageNum); } catch { Taro.showToast({ title: '加载失败', icon: 'none' }); } finally { loadingRef.current = false; setLoading(false); } }, [], ); const loadAll = useCallback( async (type?: string) => { const t = type !== undefined ? type : productType; if (!currentPatient) { // 先尝试从服务端加载患者列表 await loadPatients(); const updated = useAuthStore.getState().currentPatient; if (!updated) { setNoProfile(true); return; } } setNoProfile(false); await Promise.all([refreshPoints(), fetchProducts(1, t, true)]); }, [currentPatient, loadPatients, refreshPoints, fetchProducts, productType], ); useDidShow(() => { Taro.setNavigationBarTitle({ title: '积分商城' }); loadAll(); }); usePullDownRefresh(() => { loadAll().finally(() => { Taro.stopPullDownRefresh(); }); }); useReachBottom(() => { if (!loading && products.length < total) { fetchProducts(page + 1, productType); } }); const handleCheckin = async () => { if (checkinLoading || checkinStatus?.checked_in_today) return; setCheckinLoading(true); try { const ok = await doCheckin(); if (ok) { Taro.showToast({ title: '签到成功', icon: 'success', duration: 2000 }); } } catch (err) { Taro.showToast({ title: err instanceof Error ? err.message : '签到失败', icon: 'none', }); } finally { setCheckinLoading(false); } }; const handleTabChange = (key: string) => { setProductType(key); fetchProducts(1, key, true); }; const handleProductClick = (item: PointsProduct) => { if (item.stock <= 0) { Taro.showToast({ title: '已兑完', icon: 'none' }); return; } Taro.navigateTo({ url: `/pages/pkg-mall/exchange/index?product_id=${item.id}` }); }; const balance = account?.balance ?? 0; if (noProfile) { return ( 请先完善个人档案 建档后即可使用积分商城、签到等功能 Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' })}> 去建档 ); } return ( {/* 积分余额卡片 */} 当前积分 {checkinLoading ? '...' : checkinStatus?.checked_in_today ? '已签到' : '签到'} {balance.toLocaleString()} {checkinStatus && checkinStatus.consecutive_days > 0 && ( 已连续签到 {checkinStatus.consecutive_days} 天 )} {/* 商品类型切换 */} {PRODUCT_TYPE_TABS.map((tab) => ( handleTabChange(tab.key)} > {tab.label} ))} {/* 商品列表 */} {products.length === 0 && !loading ? ( 暂无商品 更多好物即将上架 ) : ( {products.map((item) => ( handleProductClick(item)}> {item.product_type === 'physical' ? '物' : item.product_type === 'service' ? '券' : '权'} {item.name} P {item.points_cost} {item.stock <= 0 ? ( 已兑完 ) : item.stock <= 10 ? ( 仅剩{item.stock}件 ) : null} ))} {loading && } {!loading && products.length >= total && total > 0 && ( )} )} ); }