- Remove web-push and related sub-dependencies (asn1.js, http_ece, etc.) from package.json and bun.lock - Update AppBar z-index to 8 for proper layering - Enhance GameTable component: - Add Skeleton loading states for event names - Introduce EventName component with Link to event detail pages - Fix column widths and add row-span logic for event grouping - Enable horizontal scrolling for better mobile view - Add /api/match-summary/:matchId endpoint for frontend event name fetching - Rename getEventInfo to getMatchSummary in KaiqiuService for consistency
172 lines
5.2 KiB
TypeScript
172 lines
5.2 KiB
TypeScript
import { Button, Divider, Flex, Table as AntTable, Skeleton } from "antd";
|
|
import type { GamesData } from "../types";
|
|
import User from "./User";
|
|
import { useMemo, useState } from "react";
|
|
import { useRequest } from "ahooks";
|
|
import styled from "styled-components";
|
|
import { Link } from "react-router";
|
|
|
|
const Table = styled(AntTable<GamesData>)`
|
|
width: 100%;
|
|
min-width: 320px;
|
|
max-width: 600px;
|
|
.ant-table-content {
|
|
--ant-table-row-hover-bg: var(--ant-color-bg-container);
|
|
}
|
|
`;
|
|
|
|
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;
|
|
if (page === 0) return [];
|
|
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(() => (props.data?.length === 10 && 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"
|
|
scroll={{ x: 400 }}
|
|
footer={() => (
|
|
<Flex justify="center">
|
|
<Button
|
|
disabled={!hasMore}
|
|
loading={fetchNextPage.loading}
|
|
onClick={fetchNextPage.runAsync}
|
|
>加载更多</Button>
|
|
</Flex>
|
|
)}
|
|
>
|
|
<Table.Column width={60} fixed="left" align="center" dataIndex={''} render={(_, __, i) => i + 1} />
|
|
<Table.Column
|
|
width={100}
|
|
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
|
|
width={100}
|
|
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
|
|
width={40}
|
|
align="center"
|
|
dataIndex={'result1'}
|
|
render={(result1, { result2, uid1 }: GamesData) => {
|
|
const list = props.uid === uid1 ? [result1, result2] : [result2, result1];
|
|
return list.join(':');
|
|
}}
|
|
/>
|
|
<Table.Column
|
|
width={60}
|
|
align="center"
|
|
dataIndex={'score1'}
|
|
render={(score1, { uid1, score2 }: GamesData) => {
|
|
return props.uid === uid1 ? score1 : score2;
|
|
}}
|
|
/>
|
|
<Table.Column
|
|
align="center"
|
|
// fixed='right'
|
|
width={120}
|
|
dataIndex={'dateline'}
|
|
onCell={(data, index) => {
|
|
const firstIndex = displayData.findIndex(e => e.dateline === data.dateline);
|
|
const size = displayData.filter(e => e.dateline === data.dateline).length;
|
|
const show = firstIndex === index;
|
|
if (show) {
|
|
return { rowSpan: size, style: { borderTop: '1px solid #f0f0f' } };
|
|
}
|
|
return { rowSpan: 0 };
|
|
}}
|
|
render={(dateline: string, { eventid }: GamesData) => {
|
|
return (
|
|
<EventName id={eventid} />
|
|
);
|
|
}}
|
|
/>
|
|
</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>
|
|
);
|
|
}
|
|
|
|
function EventName(props: { id: string }) {
|
|
const req = useRequest<string, []>(async () => {
|
|
return fetch(`/api/match-summary/${props.id}`)
|
|
.then(res => res.json())
|
|
.then(data => data.title ?? '-');
|
|
}, { refreshDeps: [props.id], debounceWait: 300 });
|
|
return (
|
|
<Skeleton loading={req.loading}>
|
|
<Link
|
|
type="text"
|
|
to={`/event/${props.id}`}
|
|
target="_blank"
|
|
>
|
|
{req.data}
|
|
</Link>
|
|
</Skeleton>
|
|
);
|
|
} |