- components: add FavButton for player favoriting - store: create useFavPlayerStore for managing favorites - pages: add FavePlayersPage for displaying favorites - components: add MenuButtons for navigation controls - ui: update App and ProfilePage for new features - meta: update index.html title to "我的开球网"
152 lines
5.4 KiB
TypeScript
152 lines
5.4 KiB
TypeScript
import { Link, useLoaderData, useNavigate } from "react-router";
|
||
import type { XCXProfile } from "../types/profile";
|
||
import { Avatar, Descriptions, Divider, Flex, FloatButton, Image, Typography } from "antd";
|
||
import { HomeOutlined } from "@ant-design/icons";
|
||
|
||
import User from "../components/User";
|
||
import React, { useMemo } from "react";
|
||
import { ChangeBackground } from "../components/ChangeBackground";
|
||
import UserTags from "../components/Tags";
|
||
import { GameTable } from "../components/GameTable";
|
||
import { useTitle } from "ahooks";
|
||
import { FavButton } from "../components/FavButton";
|
||
|
||
function Honor(props: { honors?: XCXProfile['honors'] }) {
|
||
if (!props.honors?.length) return null;
|
||
return (
|
||
<>
|
||
<Divider>荣誉</Divider>
|
||
<Flex vertical gap={12}>
|
||
{props.honors?.map(honor => {
|
||
return (
|
||
<Flex key={honor.hid} gap={12} style={{ width: '100%' }}>
|
||
<Image style={{ mixBlendMode: 'multiply' }} src={honor.honor} />
|
||
<Link to={`/event/${honor.eventid}`}>
|
||
<Typography.Text>
|
||
{honor.subject}
|
||
</Typography.Text>
|
||
</Link>
|
||
</Flex>
|
||
);
|
||
})}
|
||
</Flex>
|
||
</>
|
||
);
|
||
}
|
||
|
||
function Raket(props: { profile?: XCXProfile | null }) {
|
||
const {
|
||
qiupaitype = '',
|
||
zhengshoutype = '',
|
||
fanshoutype = '',
|
||
qiupai = '',
|
||
zhengshou = '',
|
||
fanshou = '',
|
||
} = props.profile || {};
|
||
if ([qiupaitype, zhengshoutype, fanshoutype].every(e => !e)) {
|
||
return null;
|
||
}
|
||
return (
|
||
<>
|
||
<Divider>装备</Divider>
|
||
<Descriptions>
|
||
<Descriptions.Item label="底板">
|
||
{qiupaitype}
|
||
<Typography.Text type="secondary" style={{ marginLeft: 4 }}>({qiupai})</Typography.Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="正手">
|
||
{zhengshoutype}
|
||
<Typography.Text type="secondary" style={{ marginLeft: 4 }}>({zhengshou})</Typography.Text>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="反手">
|
||
{fanshoutype}
|
||
<Typography.Text type="secondary" style={{ marginLeft: 4 }}>({fanshou})</Typography.Text>
|
||
</Descriptions.Item>
|
||
</Descriptions>
|
||
</>
|
||
);
|
||
}
|
||
|
||
function PlayerList(props: { title: string; names?: string[]; uids?: string[] }) {
|
||
const { names = [], uids = [] } = props;
|
||
if (!names.length) return null;
|
||
return (
|
||
<>
|
||
<Typography.Title level={5}>{props.title}</Typography.Title>
|
||
<Flex vertical align="center">
|
||
{names.map((e, i) => (
|
||
<User key={e} name={e} uid={uids?.[i] ?? ''} />
|
||
))}
|
||
</Flex>
|
||
</>
|
||
);
|
||
}
|
||
|
||
export default function ProfilePage() {
|
||
const { profile, uid } = useLoaderData<{ profile: XCXProfile; uid: string }>();
|
||
console.debug('profile', profile);
|
||
const navigate = useNavigate();
|
||
useTitle(`${profile?.username}${profile?.realname ? ` (${profile?.realname})` : ''} 个人详情`, { restoreOnUnmount: true });
|
||
const tags = useMemo<React.ReactNode[]>(() =>
|
||
[profile?.province, profile?.sex, profile?.bg ?? '', ...Object.values(profile?.allCities ?? [])]
|
||
.filter(Boolean)
|
||
, [profile]);
|
||
return (
|
||
<>
|
||
<ChangeBackground url={profile?.realpic} />
|
||
<FloatButton icon={<HomeOutlined />} onClick={() => navigate('/')} />
|
||
<Flex vertical align="center" style={{ padding: 24 }}>
|
||
<Avatar src={profile?.realpic} size={128} />
|
||
<Typography.Title level={2}>
|
||
<Flex justify="center">
|
||
<span>{profile?.username}</span>
|
||
<FavButton
|
||
user={{
|
||
uid,
|
||
score: profile.score,
|
||
name: profile.username,
|
||
realname: profile.realname,
|
||
avatar: profile.realpic
|
||
}}
|
||
/>
|
||
</Flex>
|
||
</Typography.Title>
|
||
<Typography.Text>姓名:{profile?.realname}</Typography.Text>
|
||
<Typography.Text>积分:{profile?.score}</Typography.Text>
|
||
<Typography.Text style={{ textAlign: 'center' }}>
|
||
{(tags.length > 0 && tags.reduce((a, b) => (<>{a}<Divider orientation="vertical" />{b}</>)))}
|
||
</Typography.Text>
|
||
<Divider>简介</Divider>
|
||
<Typography.Paragraph>
|
||
{profile?.description}
|
||
</Typography.Paragraph>
|
||
<UserTags uid={uid} />
|
||
<Raket profile={profile} />
|
||
<Flex wrap gap={24} justify="center">
|
||
<Flex vertical align="center">
|
||
<PlayerList title="交手前三名" names={profile?.TopPlayerUsernameScore} />
|
||
</Flex>
|
||
<Flex vertical align="center">
|
||
<PlayerList title="战胜前三名" names={profile?.Top3OfBeatUsernameScore} />
|
||
</Flex>
|
||
<Flex vertical align="center">
|
||
<PlayerList title="战胜的男子" names={profile?.Top3ManOfBeatUsernameScore} />
|
||
</Flex>
|
||
<Flex vertical align="center">
|
||
<PlayerList title="战胜的女子" names={profile?.Top3WomanOfBeatUsernameScore} />
|
||
</Flex>
|
||
</Flex>
|
||
<Flex gap={24}>
|
||
<Flex vertical align="center">
|
||
<PlayerList title="福星" names={profile?.FuXing?.names} uids={profile?.FuXing?.uids} />
|
||
</Flex>
|
||
<Flex vertical align="center">
|
||
<PlayerList title="苦主" names={profile?.KuZhu?.names} uids={profile?.KuZhu?.uids} />
|
||
</Flex>
|
||
</Flex>
|
||
<Honor honors={profile?.honors} />
|
||
<GameTable uid={uid} data={profile?.games?.data} />
|
||
</Flex>
|
||
</>
|
||
);
|
||
} |