feat(club-management): add club follow/unfollow functionality

Introduce a follow/unfollow feature for clubs in the ClubSummary component.
Users can now toggle their subscription status to a specific club, with
the state persisted locally using zustand.

Changes include:
- Added 'Follow' and 'Unfollow' buttons with corresponding star icons (StarOutlined/StarFilled).
- Updated ClubStore to manage a simplified list of clubs (id and name only).
- Refactored Data type in ClubSearchTable to use Pick<ClubInfo, 'id' | 'name'>.
- Fixed club name in GameSelector clubList to '东华乒乓球俱乐部'.
- Excluded follow buttons for club ID '47' (Donghua).

The implementation ensures a consistent UI state across the application
and persists user preferences.
This commit is contained in:
kyuuseiryuu 2026-03-26 13:44:32 +09:00
parent f1ca5cda75
commit 5927861af7
4 changed files with 31 additions and 8 deletions

View File

@ -19,7 +19,7 @@ enum ClubType {
, ,
} }
type Data = { clubs: ClubInfo[], total: number, page: number; }; type Data = { clubs: Pick<ClubInfo, 'id' | 'name'>[], total: number, page: number; };
const initData = { clubs: [], total: 0, page: 1 }; const initData = { clubs: [], total: 0, page: 1 };

View File

@ -5,7 +5,8 @@ import { useRequest } from "ahooks";
import type { ClubDetail } from "../types"; import type { ClubDetail } from "../types";
import { MapType, openWebMapRaw } from "../utils/front"; import { MapType, openWebMapRaw } from "../utils/front";
import type { ItemType } from "antd/es/menu/interface"; import type { ItemType } from "antd/es/menu/interface";
import { NotificationOutlined, PushpinOutlined } from "@ant-design/icons"; import { NotificationOutlined, PushpinOutlined, StarFilled, StarOutlined } from "@ant-design/icons";
import { useClubStore } from "../store/useClubStore";
interface Props { interface Props {
clubId: string; clubId: string;
@ -43,6 +44,7 @@ export const ClubSummary = (props: Props) => {
}, },
] ]
}, [info]); }, [info]);
const store = useClubStore();
if (requestClubSummary.data === null) return null; if (requestClubSummary.data === null) return null;
return ( return (
<div style={{ width: '100%' }}> <div style={{ width: '100%' }}>
@ -52,7 +54,29 @@ export const ClubSummary = (props: Props) => {
<Flex vertical align="center" justify="center"> <Flex vertical align="center" justify="center">
<ChangeBackground url={info?.img} /> <ChangeBackground url={info?.img} />
<Avatar src={info?.img} size={80} /> <Avatar src={info?.img} size={80} />
<Typography.Title level={2}>{info?.name}</Typography.Title> <Typography.Title level={3}>
<Flex gap={12}>
{info?.name}
{props.clubId === '47' ? null : (store.isFav(info?.id ?? '')) ? (
<Button
icon={<StarFilled style={{ color: 'yellow' }} />}
onClick={() => store.unFav(info?.id ?? '')}
>
</Button>
) : (
<Button
icon={<StarOutlined />}
onClick={() => store.add({
id: info?.id ?? '',
name: info?.name ?? '',
})}
>
</Button>
)}
</Flex>
</Typography.Title>
<Flex gap={12}> <Flex gap={12}>
{noArticle ? null : ( {noArticle ? null : (
<Button icon={<NotificationOutlined />} onClick={() => setIsArticleOpen(true)}></Button> <Button icon={<NotificationOutlined />} onClick={() => setIsArticleOpen(true)}></Button>

View File

@ -1,6 +1,6 @@
export const clubs = [ export const clubs = [
{ {
name: '东华', name: '东华乒乓球俱乐部',
clubId: '47', clubId: '47',
}, },
]; ];

View File

@ -1,17 +1,16 @@
import { create } from "zustand"; import { create } from "zustand";
import { persist, createJSONStorage } from 'zustand/middleware'; import { persist, createJSONStorage } from 'zustand/middleware';
import type { ClubInfo } from "../types";
interface Store { interface Store {
clubs: ClubInfo[]; clubs: { id: string; name: string }[];
add(club: ClubInfo): void; add(club: { id: string; name: string }): void;
unFav(id: string): void; unFav(id: string): void;
isFav(id: string): boolean; isFav(id: string): boolean;
} }
export const useClubStore = create(persist<Store>((set, get) => { export const useClubStore = create(persist<Store>((set, get) => {
return { return {
clubs: [], clubs: [],
add: (club: ClubInfo) => { add: (club: { id: string; name: string }) => {
set({ clubs: [...get().clubs, club] }); set({ clubs: [...get().clubs, club] });
}, },
unFav: (id: string) => { unFav: (id: string) => {