import { View, Text, Image } from "@tarojs/components"; import ListCard from "@/components/ListCard"; import ListLoadError from "@/components/ListLoadError"; import ListCardSkeleton from "@/components/ListCardSkeleton"; import { useReachBottom } from "@tarojs/taro"; import Taro from "@tarojs/taro"; import { useUserInfo, useUserActions, useLastTestResult } from "@/store/userStore"; import { NTRPTestEntryCard } from "@/components"; import { EvaluateScene } from "@/store/evaluateStore"; import { waitForAuthInit } from "@/utils/authInit"; import "./index.scss"; import { useRef, useEffect, useState, useMemo } from "react"; import { useDictionaryStore } from "@/store/dictionaryStore"; const ListContainer = (props) => { const { loading, isShowNoData, data = [], error, reload, // recommendList, loadMoreMatches, errorImg, emptyText, btnText, btnImg, style, collapse = false, defaultShowNum, evaluateFlag, listLoadErrorWrapperHeight, listLoadErrorWidth, listLoadErrorHeight, listLoadErrorScale, } = props; const timerRef = useRef(null); const loadingStartTimeRef = useRef(null); const skeletonTimerRef = useRef(null); const [showNumber, setShowNumber] = useState(0); const [showSkeleton, setShowSkeleton] = useState(false); const userInfo = useUserInfo(); const { fetchUserInfo, fetchLastTestResult } = useUserActions(); // 使用全局状态中的测试结果,避免重复调用接口 const lastTestResult = useLastTestResult(); const { bannerListImage, bannerDetailImage, bannerListIndex = 0 } = useDictionaryStore((s) => s.bannerDict) || {}; useReachBottom(() => { // 加载更多方法 if (loading) { return; } // timerRef.current = setTimeout(() => { loadMoreMatches(); // }, 500); }); useEffect(() => { setShowNumber(() => { return defaultShowNum === undefined ? data?.length : defaultShowNum; }); }, [data]); // 控制骨架屏显示逻辑 useEffect(() => { if (loading) { // 开始加载时记录时间 loadingStartTimeRef.current = Date.now(); // 延迟 300ms 后再显示骨架屏 skeletonTimerRef.current = setTimeout(() => { setShowSkeleton(true); }, 600); } else { // 加载完成,清除定时器并隐藏骨架屏 if (skeletonTimerRef.current) { clearTimeout(skeletonTimerRef.current); skeletonTimerRef.current = null; } setShowSkeleton(false); loadingStartTimeRef.current = null; } }, [loading]); useEffect(() => { return () => { if (timerRef.current) { clearTimeout(timerRef.current); } if (skeletonTimerRef.current) { clearTimeout(skeletonTimerRef.current); } }; }, []); // 获取测试结果,判断最近一个月是否有测试记录 useEffect(() => { const init = async () => { if (!evaluateFlag) return; // 先等待静默登录完成 await waitForAuthInit(); // 然后再获取用户信息 const userInfoId = userInfo && 'id' in userInfo ? userInfo.id : null; if (!userInfoId) { await fetchUserInfo(); return; // 等待下一次 useEffect 触发(此时 userInfo.id 已有值) } // 如果全局状态中没有测试结果,则调用接口(使用请求锁,多个组件同时调用时只会请求一次) if (!lastTestResult) { await fetchLastTestResult(); } }; init(); }, [evaluateFlag, userInfo, lastTestResult, fetchLastTestResult]); // 从全局状态中获取测试状态 const hasTestInLastMonth = lastTestResult?.has_test_in_last_month || false; if (error) { return ; } const renderSkeleton = () => { return ( <> {new Array(10).fill(0).map(() => { return ; })} ); }; // 插入 banner 卡片 function insertBannerCard(list) { if (!bannerListImage) return list; return [ ...list.slice(0, Number(bannerListIndex)), { type: "banner", banner_image_url: bannerListImage, banner_detail_url: bannerDetailImage }, ...list.slice(Number(bannerListIndex)) ]; } // 对于没有ntrp等级的用户每个月展示一次, 插在第二个位置后面 function insertEvaluateCard(list) { if (!evaluateFlag) return showNumber !== undefined ? list.slice(0, showNumber) : list; if (!list || list.length === 0) { return list; } // 如果最近一个月有测试记录,则不插入 card if (hasTestInLastMonth) { return showNumber !== undefined ? list.slice(0, showNumber) : list; } if (list.length <= 2) { return [...list, { type: "evaluateCard" }]; } const [item1, item2, ...rest] = list; let result = [ item1, item2, { type: "evaluateCard" }, ...(showNumber !== undefined ? rest.slice(0, showNumber - 3) : rest), ]; if (bannerListImage) { return insertBannerCard(result); } return result; } const memoizedList = useMemo( () => insertEvaluateCard(data), [evaluateFlag, data, hasTestInLastMonth, showNumber, bannerListImage, bannerDetailImage, bannerListIndex] ); // 渲染 banner 卡片 const renderBanner = (item, index) => { if (!item?.banner_image_url) { return null; } return ( { const target = item.banner_detail_url; if (target) { (Taro as any).navigateTo({ url: `/other_pages/bannerDetail/index?img=${encodeURIComponent(target)}`, }); } }} style={{ height: "100px", overflow: "hidden", borderRadius: "12px", backgroundImage: `url(${item.banner_image_url})`, backgroundSize: "cover", backgroundPosition: "center", backgroundRepeat: "no-repeat", }} > ); }; // 渲染列表 const renderList = () => { // 请求数据为空 if (isShowNoData) { return ( ); } // 渲染数据 return ( <> {memoizedList.map((match, index) => { if (match?.type === "banner") { return renderBanner(match, index); } if (match?.type === "evaluateCard") { return ( ); } return ; })} ); }; return ( {renderList()} {/* 显示骨架屏 - 只有在 loading 超过 300ms 时才显示 */} {loading && showSkeleton && renderSkeleton()} {/* 搜索结果较少,已为你推荐其他内容 {renderList(recommendList)} */} {/* 到底了 */} {collapse ? ( data?.length > defaultShowNum ? ( data?.length > showNumber ? ( { setShowNumber(data?.length); }} > 更多球局 ) : ( { setShowNumber(defaultShowNum); }} > 收起 ) ) : null ) : ( data?.length > 0 && 到底了 )} ); }; export default ListContainer;