feat(components, page): club events list pagination in it's self

This commit is contained in:
kyuuseiryuu 2026-03-13 12:10:04 +09:00
parent d323e1d925
commit 457fc8595d
3 changed files with 66 additions and 63 deletions

View File

@ -1,30 +1,50 @@
import { Divider, Empty, Flex, Pagination, Space, Spin, Switch, Typography } from "antd"; import { Divider, Empty, Flex, Pagination, Skeleton, Space, Spin, Switch, Typography } from "antd";
import type { IEventInfo } from "../types"; import type { IEventInfo } from "../types";
import { useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import { EventCard } from "./EventCard"; import { EventCard } from "./EventCard";
import { useRequest } from "ahooks";
interface Props { interface Props {
events: IEventInfo[]; clubId: string;
loading?: boolean;
pagination?: {
page: number;
total: number;
onPageChange: (page: number) => void;
};
} }
export const ClubEvenList = (props: Props) => { export const ClubEvenList = (props: Props) => {
const [showAll, setShowAll] = useState(false); const [showAll, setShowAll] = useState(false);
const visibleEvents = useMemo(() => { const requestEvents = useRequest<{data: IEventInfo[]; total: number; }, [string, number]>(
if (showAll) return props.events; async (clubId: string, page = 1) => {
return props.events.filter(e => !e.isFinished); if (!clubId) return [];
}, [showAll, props.events]); return (await fetch(`/api/events/${clubId}?page=${page}`)).json()
useEffect(() => { }, { manual: true });
if (!showAll && props.pagination && props.pagination.page > 1) { const [page, setPage] = useState(1);
props.pagination.onPageChange(1); // Reset page const onPageChange = useCallback((page: number) => {
setPage(page);
requestEvents.runAsync(props.clubId, page);
}, [props.clubId]);
const pagination = useMemo(() => {
return {
page,
onPageChange,
total: requestEvents.data?.total ?? 0,
} }
}, [showAll, props]); }, [requestEvents.data?.total, page]);
const visibleEvents = useMemo(() => {
const events = requestEvents.data?.data || [];
if (showAll) return events;
return events.filter(e => !e.isFinished);
}, [showAll, requestEvents.data?.data]);
useEffect(() => {
if (!showAll && page > 1) {
onPageChange(1); // Reset page
}
}, [showAll, onPageChange]);
useEffect(() => {
const id = setTimeout(() => {
requestEvents.run(props.clubId, 1);
setPage(1);
}, 100);
return () => clearTimeout(id);
}, [props.clubId]);
return ( return (
<Spin spinning={props.loading}> <>
<Divider> <Divider>
<Space> <Space>
<Typography.Text type="secondary"></Typography.Text> <Typography.Text type="secondary"></Typography.Text>
@ -32,28 +52,37 @@ export const ClubEvenList = (props: Props) => {
</Space> </Space>
</Divider> </Divider>
<Flex wrap vertical gap={12} justify="center" align="center"> <Flex wrap vertical gap={12} justify="center" align="center">
{showAll && props.pagination ? ( {showAll ? (
<Pagination <Pagination
total={props.pagination.total} showQuickJumper
current={props.pagination.page} total={pagination.total}
onChange={props.pagination.onPageChange} current={pagination.page}
onChange={pagination.onPageChange}
showSizeChanger={false} showSizeChanger={false}
/>) : null} />) : null}
{visibleEvents.length ? visibleEvents.map(e => { {requestEvents.loading ? (
<Flex vertical gap={12} style={{ width: '100%', maxWidth: 600 }}>
<Skeleton.Button active style={{ height: 200 }} block size="large" />
<Skeleton.Button active style={{ height: 200 }} block size="large" />
<Skeleton.Button active style={{ height: 200 }} block size="large" />
</Flex>
) : (<>{visibleEvents.length ? visibleEvents.map(e => {
return ( return (
<div key={e.matchId} style={{ width: '100%', maxWidth: 600 }}> <div key={e.matchId} style={{ width: '100%', maxWidth: 600 }}>
<EventCard eventInfo={e} /> <EventCard eventInfo={e} />
</div> </div>
); );
}) : <Empty description="暂无活动" />} }) : <Empty description="暂无活动" />}</>)}
{showAll && props.pagination ? (
{showAll ? (
<Pagination <Pagination
total={props.pagination.total} showQuickJumper
current={props.pagination.page} total={pagination.total}
onChange={props.pagination.onPageChange} current={pagination.page}
onChange={pagination.onPageChange}
showSizeChanger={false} showSizeChanger={false}
/>) : null} />) : null}
</Flex> </Flex>
</Spin> </>
); );
} }

View File

@ -1,33 +1,16 @@
import { Flex, Select, Space } from 'antd'; import { Flex, Select, Space } from 'antd';
import type React from 'react'; import type React from 'react';
import { useRequest } from 'ahooks';
import { clubs } from './clubList'; import { clubs } from './clubList';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useState } from 'react';
import type { IEventInfo } from '../../types';
import { ClubEvenList } from '../ClubEventList'; import { ClubEvenList } from '../ClubEventList';
interface Props { interface Props {
} }
export const GameSelector: React.FC<Props> = props => { export const GameSelector: React.FC<Props> = () => {
const [clubId, setClubId] = useState<string>(clubs[0]?.clubId ?? ''); const [clubId, setClubId] = useState<string>(clubs[0]?.clubId ?? '');
const requestEvents = useRequest<{data: IEventInfo[]; total: number; }, [string, number]>(
async (clubId: string, page = 1) => {
if (!clubId) return [];
return (await fetch(`/api/events/${clubId}?page=${page}`)).json()
}, { manual: true });
const handleClubChange = useCallback(async (id: string) => { const handleClubChange = useCallback(async (id: string) => {
setClubId(id); setClubId(id);
requestEvents.runAsync(id, page);
}, []);
const [page, setPage] = useState(1);
useEffect(() => {
const id = setTimeout(() => {
const clubId = clubs?.[0]?.clubId;
if (!clubId) return;
requestEvents.run(clubId, 1);
}, 100);
return () => clearTimeout(id);
}, []); }, []);
return ( return (
<Space orientation='vertical' style={{ width: '100%' }}> <Space orientation='vertical' style={{ width: '100%' }}>
@ -41,18 +24,7 @@ export const GameSelector: React.FC<Props> = props => {
onChange={handleClubChange} onChange={handleClubChange}
/> />
</Flex> </Flex>
<ClubEvenList <ClubEvenList clubId={clubId} />
events={requestEvents.data?.data ?? []}
loading={requestEvents.loading}
pagination={{
total: requestEvents.data?.total ?? 0,
page,
onPageChange: page => {
setPage(page);
requestEvents.runAsync(clubId, page);
}
}}
/>
</Space> </Space>
); );
} }

View File

@ -1,14 +1,16 @@
import { Avatar, Button, Drawer, Typography } from "antd"; import { Avatar, Button, Drawer, Typography } from "antd";
import { useLoaderData } from "react-router"; import { useLoaderData } from "react-router";
import type { ClubInfo, IEventInfo } from "../types"; import type { ClubInfo, IEventInfo } from "../types";
import { useState } from "react"; import { useCallback, useState } from "react";
import { ChangeBackground } from "../components/ChangeBackground"; import { ChangeBackground } from "../components/ChangeBackground";
import { ClubEvenList } from "../components/ClubEventList"; import { ClubEvenList } from "../components/ClubEventList";
import { useRequest } from "ahooks";
export const ClubEventsPage = () => { export const ClubEventsPage = () => {
const { info, events } = useLoaderData<{ const { info, events, total } = useLoaderData<{
info: ClubInfo; info: ClubInfo;
events: IEventInfo[]; events: IEventInfo[];
total: number;
}>(); }>();
const [isArticleOpen, setIsArticleOpen] = useState(false); const [isArticleOpen, setIsArticleOpen] = useState(false);
@ -18,7 +20,7 @@ export const ClubEventsPage = () => {
<Avatar src={info.img} size={80} /> <Avatar src={info.img} size={80} />
<Typography.Title>{info.name}</Typography.Title> <Typography.Title>{info.name}</Typography.Title>
<Button onClick={() => setIsArticleOpen(true)}></Button> <Button onClick={() => setIsArticleOpen(true)}></Button>
<ClubEvenList events={events} /> <ClubEvenList clubId={info.id} />
<Drawer size={'60vh'} open={isArticleOpen} onClose={() => setIsArticleOpen(false)} placement="bottom"> <Drawer size={'60vh'} open={isArticleOpen} onClose={() => setIsArticleOpen(false)} placement="bottom">
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<Typography.Paragraph style={{ whiteSpace: 'pre', textWrap: 'auto' }}>{info.article}</Typography.Paragraph> <Typography.Paragraph style={{ whiteSpace: 'pre', textWrap: 'auto' }}>{info.article}</Typography.Paragraph>