feat(index)
This commit is contained in:
parent
0f33f63459
commit
7df7b70516
7
src/components/ChangeBackground.tsx
Normal file
7
src/components/ChangeBackground.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { createGlobalStyle } from "styled-components";
|
||||||
|
|
||||||
|
export const ChangeBackground = createGlobalStyle<{ url?: string }>`
|
||||||
|
body:before {
|
||||||
|
${({ url }) => url ? `background: url(${url});` : ''}
|
||||||
|
}
|
||||||
|
`
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { Card, Divider, Select, Space, Spin, Statistic, Typography } from 'antd';
|
import { Card, Divider, Flex, Select, Space, Statistic, Switch, Typography } from 'antd';
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useRequest } from 'ahooks';
|
import { useRequest } from 'ahooks';
|
||||||
import { clubs } from './clubList';
|
import { clubs } from './clubList';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import type { IEventInfo } from '../../types';
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import type { IEventInfo } from '../../types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onGameClick?: (info: IEventInfo) => void;
|
onGameClick?: (info: IEventInfo) => void;
|
||||||
@ -14,14 +14,18 @@ export const GameSelector: React.FC<Props> = props => {
|
|||||||
const requestEvents = useRequest<IEventInfo[], [string]>(
|
const requestEvents = useRequest<IEventInfo[], [string]>(
|
||||||
async (clubId: string) => (await fetch(`/api/events/${clubId}`)).json()
|
async (clubId: string) => (await fetch(`/api/events/${clubId}`)).json()
|
||||||
, { manual: true })
|
, { manual: true })
|
||||||
const [gameList, setGameList] = useState<IEventInfo[]>([]);
|
const [gameList, setGameList] = useState<(IEventInfo & { finished: boolean })[]>([]);
|
||||||
const [isEmpty, setIsEmpty] = useState(false);
|
const [isEmpty, setIsEmpty] = useState(false);
|
||||||
const [clubId, setClubId] = useState(clubs[0].clubId);
|
const [clubId, setClubId] = useState(clubs[0].clubId);
|
||||||
|
const [showFinished, setShowFinished] = useState(false);
|
||||||
const handleClubChange = useCallback(async (clubId: string) => {
|
const handleClubChange = useCallback(async (clubId: string) => {
|
||||||
const list = await requestEvents.runAsync(clubId);
|
const list = await requestEvents.runAsync(clubId);
|
||||||
const activeList = list.filter(e => !e.info.join('').includes('已结束'));
|
const activeList = list.map(e => ({
|
||||||
|
...e,
|
||||||
|
finished: e.info.join('').includes('已结束'),
|
||||||
|
}));
|
||||||
setGameList(activeList);
|
setGameList(activeList);
|
||||||
setIsEmpty(activeList.length === 0);
|
setIsEmpty(activeList.filter(e => !e.finished).length === 0);
|
||||||
}, []);
|
}, []);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const clubId = clubs[0].clubId;
|
const clubId = clubs[0].clubId;
|
||||||
@ -29,6 +33,11 @@ export const GameSelector: React.FC<Props> = props => {
|
|||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<Space orientation='vertical' style={{ width: '100%' }}>
|
<Space orientation='vertical' style={{ width: '100%' }}>
|
||||||
|
<Flex vertical gap={12} justify='center'>
|
||||||
|
<Space style={{ alignItems: 'self-end' }}>
|
||||||
|
<Typography.Text>已结束的比赛</Typography.Text>
|
||||||
|
<Switch checkedChildren='显示' unCheckedChildren='隐藏' checked={showFinished} onChange={e => setShowFinished(e)} />
|
||||||
|
</Space>
|
||||||
<Select
|
<Select
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
placeholder={'请选择俱乐部'}
|
placeholder={'请选择俱乐部'}
|
||||||
@ -37,10 +46,25 @@ export const GameSelector: React.FC<Props> = props => {
|
|||||||
options={clubs.map(e => ({ label: e.name, value: e.clubId }))}
|
options={clubs.map(e => ({ label: e.name, value: e.clubId }))}
|
||||||
onChange={handleClubChange}
|
onChange={handleClubChange}
|
||||||
/>
|
/>
|
||||||
<Divider />
|
</Flex>
|
||||||
{isEmpty && (<Typography.Text type='secondary'>没有未开始的比赛</Typography.Text>)}
|
<Divider>{isEmpty && (<Typography.Text type='secondary'>没有未开始的比赛</Typography.Text>)}</Divider>
|
||||||
<Spin spinning={requestEvents.loading}>
|
<Flex wrap gap={12} justify='center'>
|
||||||
{gameList.map(e => {
|
{gameList
|
||||||
|
.filter(e => showFinished || !e.finished)
|
||||||
|
.map(e => <EventCard key={e.matchId} eventInfo={e} onGameClick={props.onGameClick} />)
|
||||||
|
}
|
||||||
|
</Flex>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EventCardProps {
|
||||||
|
eventInfo: IEventInfo & { finished: boolean };
|
||||||
|
onGameClick?: (info: IEventInfo) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function EventCard(props: EventCardProps) {
|
||||||
|
const { eventInfo: e } = props;
|
||||||
const { y, M, D } = /^(?<y>\d{4})年(?<M>\d+)月(?<D>\d+)日/.exec(e.title)?.groups ?? {} as { y: string; M: string; D: string};
|
const { y, M, D } = /^(?<y>\d{4})年(?<M>\d+)月(?<D>\d+)日/.exec(e.title)?.groups ?? {} as { y: string; M: string; D: string};
|
||||||
const hm = /(?<hm>\d+:\d+)\b/.exec(e.info.join('\n'))?.groups?.hm ?? '10:00';
|
const hm = /(?<hm>\d+:\d+)\b/.exec(e.info.join('\n'))?.groups?.hm ?? '10:00';
|
||||||
const day = dayjs(`${y}-${M}-${D} ${hm}`);
|
const day = dayjs(`${y}-${M}-${D} ${hm}`);
|
||||||
@ -49,23 +73,21 @@ export const GameSelector: React.FC<Props> = props => {
|
|||||||
key={e.matchId}
|
key={e.matchId}
|
||||||
title={e.title}
|
title={e.title}
|
||||||
hoverable
|
hoverable
|
||||||
|
style={{ width: '100%' }}
|
||||||
onClick={() => props.onGameClick?.(e)}
|
onClick={() => props.onGameClick?.(e)}
|
||||||
>
|
>
|
||||||
<Typography.Text type='success'>{e.title}</Typography.Text>
|
<Typography.Text type={e.finished ? undefined : 'success'}>{e.title}</Typography.Text>
|
||||||
|
<Statistic.Timer
|
||||||
|
type={e.finished ? 'countup' : 'countdown'}
|
||||||
|
value={day.toDate().getTime()}
|
||||||
|
format={`距离比赛${e.finished ? '结束' : '开始'}: DD 天${e.finished ? '' : ' HH 时'}`}
|
||||||
|
styles={{ content: e.finished ? { color: 'gray' } : {} }}
|
||||||
|
/>
|
||||||
{e.info.map(e => (
|
{e.info.map(e => (
|
||||||
<div key={e}>
|
<div key={e}>
|
||||||
<Typography.Text type='secondary'>{e}</Typography.Text>
|
<Typography.Text type='secondary'>{e}</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<Statistic.Timer
|
|
||||||
type='countdown'
|
|
||||||
value={day.toDate().getTime()}
|
|
||||||
format='距离比赛开始: DD 天 HH 时 mm 分 ss 秒'
|
|
||||||
/>
|
|
||||||
</Card>
|
</Card>
|
||||||
);
|
)
|
||||||
})}
|
|
||||||
</Spin>
|
|
||||||
</Space>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@ -5,13 +5,7 @@ import { HomeOutlined } from "@ant-design/icons";
|
|||||||
|
|
||||||
import User from "../components/User";
|
import User from "../components/User";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { createGlobalStyle } from "styled-components";
|
import { ChangeBackground } from "../components/ChangeBackground";
|
||||||
|
|
||||||
const BodyBG = createGlobalStyle<{ url?: string }>`
|
|
||||||
body:before {
|
|
||||||
${({ url }) => url ? `background: url(${url});` : ''}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
function Honor(props: { honors?: XCXProfile['honors'] }) {
|
function Honor(props: { honors?: XCXProfile['honors'] }) {
|
||||||
if (!props.honors?.length) return null;
|
if (!props.honors?.length) return null;
|
||||||
@ -81,7 +75,7 @@ export default function ProfilePage() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<BodyBG url={profile?.realpic} />
|
<ChangeBackground url={profile?.realpic} />
|
||||||
<FloatButton icon={<HomeOutlined />} onClick={() => navigate('/')} />
|
<FloatButton icon={<HomeOutlined />} onClick={() => navigate('/')} />
|
||||||
<Flex vertical align="center" style={{ padding: 24 }}>
|
<Flex vertical align="center" style={{ padding: 24 }}>
|
||||||
<Avatar src={profile?.realpic} size={128} />
|
<Avatar src={profile?.realpic} size={128} />
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user