diff --git a/src/app/record/page.tsx b/src/app/record/page.tsx index 9c51f7f..a578573 100755 --- a/src/app/record/page.tsx +++ b/src/app/record/page.tsx @@ -1,26 +1,116 @@ +'use client'; + +import { useState, useEffect, useCallback, useRef } from 'react'; import RecordCard from './components/RecordCard'; import { getRecordPagingAPI } from '@/api/record'; import { getAuthorDataAPI } from '@/api/user'; import { Record } from '@/types/app/record'; import { User } from '@/types/app/user'; -import Pagination from '@/components/Pagination'; import Empty from '@/components/Empty'; import Show from '@/components/Show'; +import Loading from '@/components/Loading'; import { getWebConfigDataAPI } from '@/api/config'; import { Theme } from '@/types/app/config'; -interface Props { - searchParams: Promise<{ page: number }>; -} +export default () => { + const [records, setRecords] = useState([]); + const [user, setUser] = useState({} as User); + const [theme, setTheme] = useState({} as Theme); + const [totalPages, setTotalPages] = useState(1); + const [loading, setLoading] = useState(false); + const [hasMore, setHasMore] = useState(true); + const [initialLoading, setInitialLoading] = useState(true); + const currentPageRef = useRef(1); -export default async (props: Props) => { - const searchParams = await props.searchParams; - const page = searchParams.page || 1; + // 获取记录列表 + const fetchRecordList = useCallback(async (page: number, append: boolean = false) => { + setLoading(true); + try { + const { data: recordData } = (await getRecordPagingAPI({ pagination: { page, size: 8 } })) || { + data: {} as Paginate, + }; - const { data: user } = (await getAuthorDataAPI()) || { data: {} as User }; - const { data: record } = (await getRecordPagingAPI({ pagination: { page, size: 8 } })) || { data: {} as Paginate }; - const themeResponse = await getWebConfigDataAPI<{ value: Theme }>('theme'); - const theme = themeResponse?.data?.value || ({} as Theme); + if (recordData.result && recordData.result.length > 0) { + if (append) { + setRecords((prev) => [...prev, ...recordData.result]); + } else { + setRecords(recordData.result); + } + setTotalPages(recordData.pages || 1); + setHasMore(page < (recordData.pages || 1)); + currentPageRef.current = page; + } else { + setHasMore(false); + } + } catch (error) { + console.error('获取记录列表失败:', error); + setHasMore(false); + } finally { + setLoading(false); + setInitialLoading(false); + } + }, []); + + // 初始加载:获取用户信息、主题配置和第一页记录 + useEffect(() => { + const fetchInitialData = async () => { + try { + // 并行获取用户信息、主题配置和第一页记录 + const [userResponse, themeResponse] = await Promise.all([getAuthorDataAPI(), getWebConfigDataAPI<{ value: Theme }>('theme')]); + + if (userResponse?.data) { + setUser(userResponse.data); + } + if (themeResponse?.data?.value) { + setTheme(themeResponse.data.value); + } + + // 获取第一页记录 + setRecords([]); + setHasMore(true); + setInitialLoading(true); + currentPageRef.current = 1; + await fetchRecordList(1, false); + } catch (error) { + console.error('获取初始数据失败:', error); + setInitialLoading(false); + } + }; + fetchInitialData(); + }, [fetchRecordList]); + + // 滚动监听 + useEffect(() => { + const handleScroll = () => { + // 如果正在加载或没有更多数据,则不处理 + if (loading || !hasMore) return; + + // 检查是否滚动到底部(距离底部100px时触发) + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + const windowHeight = window.innerHeight; + const documentHeight = document.documentElement.scrollHeight; + + if (scrollTop + windowHeight >= documentHeight - 100) { + const nextPage = currentPageRef.current + 1; + if (nextPage <= totalPages) { + fetchRecordList(nextPage, true); + } + } + }; + + // 使用防抖优化滚动事件 + let timeoutId: NodeJS.Timeout; + const debouncedHandleScroll = () => { + clearTimeout(timeoutId); + timeoutId = setTimeout(handleScroll, 200); + }; + + window.addEventListener('scroll', debouncedHandleScroll); + return () => { + window.removeEventListener('scroll', debouncedHandleScroll); + clearTimeout(timeoutId); + }; + }, [hasMore, loading, totalPages, fetchRecordList]); return ( <> @@ -35,25 +125,39 @@ export default async (props: Props) => {

{theme?.record_info}

-
- {!!record?.result?.length && - record?.result.map((item) => ( - - ))} + {initialLoading ? ( +
+ +
+ ) : ( + <> +
+ {!!records?.length && records.map((item) => )} - - - -
+ + + +
- {record?.total && } + {/* 懒加载指示器 */} + {loading && records.length > 0 && ( +
+
+ + + + + 正在加载... +
+
+ )} + {!hasMore && records.length > 0 && ( +
+
没有更多内容了
+
+ )} + + )}