Compare commits
2 Commits
f37be8aded
...
8831ac456b
| Author | SHA1 | Date | |
|---|---|---|---|
| 8831ac456b | |||
| 4c12b609a0 |
104
src/components/GameTable.tsx
Normal file
104
src/components/GameTable.tsx
Normal file
@ -0,0 +1,104 @@
|
||||
import { Button, Divider, Flex, Table } from "antd";
|
||||
import type { GamesData } from "../types";
|
||||
import User from "./User";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRequest } from "ahooks";
|
||||
|
||||
interface Props {
|
||||
uid?: string;
|
||||
data?: GamesData[];
|
||||
}
|
||||
|
||||
function toTeams(data: GamesData) {
|
||||
const {
|
||||
uid1, uid11, username1, username11,
|
||||
uid2, uid22, username2, username22,
|
||||
result1, result2, ascore1, score1, score2,
|
||||
} = data;
|
||||
const isDoubles = Boolean(username11);
|
||||
const team1 = [{ uid: uid1, name: username1 }];
|
||||
const team2 = [{ uid: uid2, name: username2 }];
|
||||
if (isDoubles) {
|
||||
team1.push({ uid: uid11, name: username11 });
|
||||
team2.push({ uid: uid22, name: username22 });
|
||||
}
|
||||
return { team1, team2 };
|
||||
}
|
||||
|
||||
export function GameTable(props: Props) {
|
||||
const [displayData, setDisplayData] = useState(props.data ?? []);
|
||||
const [page, setPage] = useState(props.data?.length ? 1 :0);
|
||||
const fetchNextPage = useRequest<GamesData[], []>(async () => {
|
||||
const newPage = page + 1;
|
||||
const resp = await fetch(`/api/user/${props.uid}/games?page=${newPage}`);
|
||||
const nextPageData: GamesData[] = await resp.json();
|
||||
setDisplayData(data => [...data, ...nextPageData])
|
||||
setPage(newPage);
|
||||
return nextPageData;
|
||||
}, { manual: true, refreshDeps: [page] });
|
||||
const hasMore = useMemo(() => page < 2 || (fetchNextPage.data?.length || 0) === 10, [page, fetchNextPage]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider>最近战绩</Divider>
|
||||
<Table
|
||||
loading={fetchNextPage.loading}
|
||||
rowKey={e => e.gameid}
|
||||
showHeader={false}
|
||||
pagination={false}
|
||||
dataSource={displayData}
|
||||
size="small"
|
||||
style={{ width: '100%', maxWidth: 400 }}
|
||||
footer={() => (
|
||||
<Flex justify="center">
|
||||
<Button
|
||||
disabled={!hasMore}
|
||||
loading={fetchNextPage.loading}
|
||||
onClick={fetchNextPage.runAsync}
|
||||
>加载更多</Button>
|
||||
</Flex>
|
||||
)}
|
||||
>
|
||||
<Table.Column align="center" dataIndex={''} render={(_, __, i) => i + 1} />
|
||||
<Table.Column
|
||||
dataIndex='gameid'
|
||||
align="center"
|
||||
render={((_, record: GamesData) => {
|
||||
const { uid1 } = record;
|
||||
const { team1, team2 } = toTeams(record);
|
||||
return (
|
||||
<Flex gap={48} justify="center">
|
||||
<PlayerTeam idNames={props.uid !== uid1 ? team1 : team2} />
|
||||
</Flex>
|
||||
);
|
||||
})}
|
||||
/>
|
||||
<Table.Column
|
||||
align="center"
|
||||
dataIndex={'result1'}
|
||||
render={(result1, { result2, uid1 }: GamesData) => {
|
||||
const list = props.uid === uid1 ? [result1, result2] : [result2, result1];
|
||||
return list.join(':');
|
||||
}}
|
||||
/>
|
||||
<Table.Column
|
||||
dataIndex={'score1'}
|
||||
render={(score1, { uid1, score2 }: GamesData) => {
|
||||
return props.uid === uid1 ? score1 : score2;
|
||||
}}
|
||||
/>
|
||||
<Table.Column dataIndex={'dateline'} />
|
||||
</Table>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function PlayerTeam(props: {
|
||||
idNames: { uid: string; name: string; }[]
|
||||
}) {
|
||||
return (
|
||||
<Flex vertical align="center" justify="center">
|
||||
{props.idNames.map(e => (<div key={e.uid}><User {...e} /></div>))}
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
@ -12,7 +12,7 @@ import { ConfigProvider, Spin, theme } from "antd";
|
||||
import { createBrowserRouter, RouterProvider } from "react-router";
|
||||
import ProfilePage from "./page/ProfilePage";
|
||||
import EventPage from "./page/EventPage";
|
||||
import type { MatchInfo } from "./types";
|
||||
import type { BasePlayer, MatchInfo, XCXMember } from "./types";
|
||||
import { Outlet, useNavigation } from "react-router";
|
||||
|
||||
const elem = document.getElementById("root")!;
|
||||
@ -32,7 +32,15 @@ const route = createBrowserRouter([
|
||||
path: 'event/:matchId',
|
||||
loader: async ({ params }) => {
|
||||
const info: MatchInfo = await (await fetch(`/api/match/${params.matchId}`)).json();
|
||||
const members = await (await fetch(`/api/match/${params.matchId}/${info.itemId}`)).json();
|
||||
const members = info.itemId
|
||||
? await (await fetch(`/api/match/${params.matchId}/${info.itemId}`)).json()
|
||||
: info.players.map((e, i) => ({
|
||||
number: i + 1,
|
||||
uid: e.uid,
|
||||
name: e.name,
|
||||
score: e.score,
|
||||
realname: e.name,
|
||||
} as XCXMember));
|
||||
return { info, members };
|
||||
},
|
||||
Component: EventPage,
|
||||
|
||||
@ -41,6 +41,15 @@ const server = serve({
|
||||
return Response.json(profile);
|
||||
},
|
||||
},
|
||||
"/api/user/:uid/games": {
|
||||
async GET(req) {
|
||||
const uid = req.params.uid;
|
||||
const search = new URL(req.url).searchParams;
|
||||
const page = Number(search.get('page')) ?? 0;
|
||||
const resp = await xcxApi.getGameList(uid, page);
|
||||
return Response.json(resp);
|
||||
},
|
||||
},
|
||||
"/api/user/:uid/tags": {
|
||||
async GET(req) {
|
||||
const uid = req.params.uid;
|
||||
|
||||
@ -4,6 +4,7 @@ import type { BasePlayer, MatchInfo, XCXMember } from "../types";
|
||||
import { Typography } from "antd";
|
||||
import { HomeOutlined } from "@ant-design/icons";
|
||||
import { useMemo } from "react";
|
||||
import { useTitle } from "ahooks";
|
||||
|
||||
export default function EventPage() {
|
||||
const {
|
||||
@ -20,6 +21,7 @@ export default function EventPage() {
|
||||
const basePlayers = useMemo<BasePlayer[]>(() => {
|
||||
return members.map(e => ({ ...e, name: e.realname } as BasePlayer))
|
||||
}, [members]);
|
||||
useTitle(game.title, { restoreOnUnmount: true });
|
||||
return (
|
||||
<div style={{ width: '100%', padding: 10, boxSizing: 'border-box' }}>
|
||||
<Typography.Title level={3}>
|
||||
|
||||
@ -7,6 +7,8 @@ import User from "../components/User";
|
||||
import React from "react";
|
||||
import { ChangeBackground } from "../components/ChangeBackground";
|
||||
import UserTags from "../components/Tags";
|
||||
import { GameTable } from "../components/GameTable";
|
||||
import { useTitle } from "ahooks";
|
||||
|
||||
function Honor(props: { honors?: XCXProfile['honors'] }) {
|
||||
if (!props.honors?.length) return null;
|
||||
@ -82,6 +84,7 @@ export default function ProfilePage() {
|
||||
const profile = useLoaderData<XCXProfile | null>();
|
||||
console.debug('profile', profile);
|
||||
const navigate = useNavigate();
|
||||
useTitle(`${profile?.username}${profile?.realname ? ` (${profile?.realname})` : ''} 个人详情`, { restoreOnUnmount: true });
|
||||
return (
|
||||
<>
|
||||
<ChangeBackground url={profile?.realpic} />
|
||||
@ -127,6 +130,7 @@ export default function ProfilePage() {
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Honor honors={profile?.honors} />
|
||||
<GameTable uid={profile?.uid} data={profile?.games.data} />
|
||||
</Flex>
|
||||
</>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user