feat(club): add club type filtering and optimize event list display

- Add 'Club' vs 'Points Club' filter in `ClubSearchTable` using Radio buttons.
- Update `searchClub` request to accept and pass `clubType` parameter to the backend API.
- Refactor logic in `ClubEventList` to determine "Show All" button visibility based on whether all items are finished or not finished, rather than just a fixed count.
- Add custom CSS (styled-components) to limit the height of the game selector drawer for better UX.
- Update backend service `KaiqiuService` to handle the new query parameters for filtering clubs by type.
This commit is contained in:
kyuuseiryuu 2026-03-14 00:11:42 +09:00
parent 99fd5778df
commit aef89d2341
4 changed files with 45 additions and 13 deletions

View File

@ -42,8 +42,9 @@ export const ClubEvenList = (props: Props) => {
const id = setTimeout(async () => {
const data = await requestEvents.runAsync(props.clubId, 1).then(res => res.data);
setPage(1);
if (data.length === 10) {
setShowAll(data.every(e => !e.isFinished));
if (data.length) {
const isAllFinishedOrAllNotFinished = data.every(e => e.isFinished) || data.every(e => !e.isFinished);
setShowAll(isAllFinishedOrAllNotFinished);
setShowAllDiable(data.every(e => !e.isFinished));
}
}, 100);

View File

@ -1,5 +1,5 @@
import { useRequest } from "ahooks";
import { Avatar, Button, Flex, Input, Space, Table } from "antd";
import { Avatar, Button, Flex, Input, Radio, Space, Table } from "antd";
import { useCallback, useState } from "react";
import type { ClubInfo } from "../types";
import { EyeOutlined, StarFilled, StarOutlined } from "@ant-design/icons";
@ -14,23 +14,44 @@ interface Props {
export const ClubSearchTable = (props: Props) => {
const [searchKey, setSearchKey] = useState('');
const searchClub = useRequest<{ clubs: ClubInfo[], total: number }, [string, number]>(async (searchKey: string, page = 1) => {
const resp: { clubs: ClubInfo[], total: number } = await fetch(`/api/club/find?key=${searchKey}&page=${page}`).then(e => e.json());
const searchClub = useRequest<{ clubs: ClubInfo[], total: number }, [string, number, number]>(async (
searchKey: string,
page = 1,
clubType = 0,
) => {
const resp: { clubs: ClubInfo[], total: number } = await fetch(
`/api/club/find?key=${searchKey}&page=${page}${clubType ? '&normalClub=1' : ''}`
).then(e => e.json());
return resp;
}, { manual: true });
const [page, setPage] = useState(1);
const handleSearch = useCallback((searchKey: string) => {
searchClub.runAsync(searchKey, 1);
}, [searchKey]);
const [clubType, setClubType] = useState(0);
const handleSearch = useCallback((searchKey: string, clubType: number) => {
setPage(1);
searchClub.runAsync(searchKey, 1, clubType);
}, []);
return (
<Flex vertical gap={12} justify="center" align="center">
<Radio.Group
optionType="button"
value={clubType}
onChange={e => {
setClubType(e.target.value);
if (!searchKey) return;
handleSearch(searchKey, e.target.value);
}}
options={[
{ label: '积分俱乐部', value: 0 },
{ label: '俱乐部', value: 1 },
]}
/>
<Input.Search
allowClear
onSearch={e => {
setSearchKey(e);
handleSearch(e);
handleSearch(e, clubType);
}}
onPressEnter={() => handleSearch(searchKey)}
onPressEnter={() => handleSearch(searchKey, clubType)}
value={searchKey}
onChange={e => setSearchKey(e.target.value)}
/>
@ -46,7 +67,8 @@ export const ClubSearchTable = (props: Props) => {
total: searchClub.data?.total,
onChange: (page) => {
setPage(page);
searchClub.runAsync(searchKey, page);
searchClub.runAsync(searchKey, page, clubType);
console.debug('onPageChange', { searchKey, page, clubType });
}
}}
>

View File

@ -3,9 +3,10 @@ import type React from 'react';
import { clubs } from './clubList';
import { useCallback, useEffect, useState } from 'react';
import { ClubEvenList } from '../ClubEventList';
import { DeleteOutlined, PlusOutlined, SearchOutlined } from '@ant-design/icons';
import { DeleteOutlined, SearchOutlined } from '@ant-design/icons';
import { useClubStore } from '../../store/useClubStore';
import { ClubSearchTable } from '../ClubSearchTable';
import { createGlobalStyle } from 'styled-components';
interface Props {
}
@ -14,6 +15,12 @@ const defaultOptions = clubs.map(e => ({
value: e.clubId,
}));
const ChangeDrawerHeight = createGlobalStyle`
.ant-drawer-content-wrapper:has(div.search-table) {
max-height: 720px;
}
`;
export const GameSelector: React.FC<Props> = () => {
const [options, setOptions] = useState<{ label: React.ReactNode, value: string }[]>(defaultOptions);
const [clubId, setClubId] = useState<string>(clubs[0]?.clubId ?? '');
@ -43,6 +50,7 @@ export const GameSelector: React.FC<Props> = () => {
}, [clubStore]);
return (
<Space orientation='vertical' style={{ width: '100%' }}>
<ChangeDrawerHeight />
<Flex gap={12} justify='center' align='center'>
<Select
style={{ width: '100%' }}
@ -60,6 +68,7 @@ export const GameSelector: React.FC<Props> = () => {
</Flex>
<ClubEvenList clubId={clubId} />
<Drawer
className='search-table'
open={searchOpen}
placement='bottom'
size={"80vh"}

View File

@ -8,7 +8,7 @@ export class KaiqiuService {
public static async findClub(name: string, page = 1, normalClub?: boolean) {
const searchKey = encodeURIComponent(name);
const url = `${this.#baseURL}/home/space.php?province=&city=&searchkey=${searchKey}&searchsubmit=%E6%90%9C%E7%B4%A2&searchmode=1&do=mtag&view=hot&page=${page}`;
const url = `${this.#baseURL}/home/space.php?province=&city=&searchkey=${searchKey}&searchsubmit=%E6%90%9C%E7%B4%A2&searchmode=1&do=mtag&view=hot&${normalClub ? 'page2': 'page'}=${page}`;
const html = await fetch(url, {
headers: htmlRequestHeaders,
}).then(res => res.text());