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 { useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { EventCard } from "./EventCard";
import { useRequest } from "ahooks";
interface Props {
events: IEventInfo[];
loading?: boolean;
pagination?: {
page: number;
total: number;
onPageChange: (page: number) => void;
};
clubId: string;
}
export const ClubEvenList = (props: Props) => {
const [showAll, setShowAll] = useState(false);
const visibleEvents = useMemo(() => {
if (showAll) return props.events;
return props.events.filter(e => !e.isFinished);
}, [showAll, props.events]);
useEffect(() => {
if (!showAll && props.pagination && props.pagination.page > 1) {
props.pagination.onPageChange(1); // Reset page
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 [page, setPage] = useState(1);
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 (
<Spin spinning={props.loading}>
<>
<Divider>
<Space>
<Typography.Text type="secondary"></Typography.Text>
@ -32,28 +52,37 @@ export const ClubEvenList = (props: Props) => {
</Space>
</Divider>
<Flex wrap vertical gap={12} justify="center" align="center">
{showAll && props.pagination ? (
{showAll ? (
<Pagination
total={props.pagination.total}
current={props.pagination.page}
onChange={props.pagination.onPageChange}
showQuickJumper
total={pagination.total}
current={pagination.page}
onChange={pagination.onPageChange}
showSizeChanger={false}
/>) : 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 (
<div key={e.matchId} style={{ width: '100%', maxWidth: 600 }}>
<EventCard eventInfo={e} />
</div>
);
}) : <Empty description="暂无活动" />}
{showAll && props.pagination ? (
}) : <Empty description="暂无活动" />}</>)}
{showAll ? (
<Pagination
total={props.pagination.total}
current={props.pagination.page}
onChange={props.pagination.onPageChange}
showQuickJumper
total={pagination.total}
current={pagination.page}
onChange={pagination.onPageChange}
showSizeChanger={false}
/>) : null}
</Flex>
</Spin>
</>
);
}

View File

@ -1,33 +1,16 @@
import { Flex, Select, Space } from 'antd';
import type React from 'react';
import { useRequest } from 'ahooks';
import { clubs } from './clubList';
import { useCallback, useEffect, useState } from 'react';
import type { IEventInfo } from '../../types';
import { useCallback, useState } from 'react';
import { ClubEvenList } from '../ClubEventList';
interface Props {
}
export const GameSelector: React.FC<Props> = props => {
export const GameSelector: React.FC<Props> = () => {
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) => {
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 (
<Space orientation='vertical' style={{ width: '100%' }}>
@ -41,18 +24,7 @@ export const GameSelector: React.FC<Props> = props => {
onChange={handleClubChange}
/>
</Flex>
<ClubEvenList
events={requestEvents.data?.data ?? []}
loading={requestEvents.loading}
pagination={{
total: requestEvents.data?.total ?? 0,
page,
onPageChange: page => {
setPage(page);
requestEvents.runAsync(clubId, page);
}
}}
/>
<ClubEvenList clubId={clubId} />
</Space>
);
}

View File

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