diff --git a/package.json b/package.json
index 90008f6..094ec72 100644
--- a/package.json
+++ b/package.json
@@ -12,22 +12,22 @@
"format": "prettier --write ."
},
"dependencies": {
- "alova": "^3.2.6",
+ "alova": "^3.2.7",
"chroma-js": "^3.1.2",
"clsx": "^2.1.1",
- "highlight.js": "^11.10.0",
+ "highlight.js": "^11.11.1",
"install": "^0.13.0",
- "katex": "^0.16.15",
+ "katex": "^0.16.20",
"luxon": "^3.5.0",
"nanoid": "^5.0.9",
"overlayscrollbars": "^2.10.1",
"overlayscrollbars-react": "^0.5.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
- "react-error-boundary": "^4.1.2",
- "react-markdown": "^9.0.1",
- "react-resize-detector": "^12.0.1",
- "react-router": "^7.0.2",
+ "react-error-boundary": "^5.0.0",
+ "react-markdown": "^9.0.3",
+ "react-resize-detector": "^12.0.2",
+ "react-router": "^7.1.1",
"react-transition-group": "^4.4.5",
"rehype-autolink-headings": "^7.1.0",
"rehype-highlight": "^7.0.1",
@@ -37,31 +37,31 @@
"remark-math": "^6.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.1",
- "sass": "^1.83.0",
- "zustand": "^5.0.2"
+ "sass": "^1.83.1",
+ "zustand": "^5.0.3"
},
"devDependencies": {
- "@eslint/js": "^9.17.0",
- "@iconify-json/solar": "^1.2.1",
- "@iconify-json/svg-spinners": "^1.2.1",
+ "@eslint/js": "^9.18.0",
+ "@iconify-json/solar": "^1.2.2",
+ "@iconify-json/svg-spinners": "^1.2.2",
"@svgr/core": "^8.1.0",
"@svgr/plugin-jsx": "^8.1.0",
- "@types/chroma-js": "^2.4.4",
+ "@types/chroma-js": "^3.1.0",
"@types/katex": "^0.16.7",
"@types/luxon": "^3.4.2",
- "@types/node": "^22.10.2",
- "@types/react": "^19.0.1",
- "@types/react-dom": "^19.0.2",
+ "@types/node": "^22.10.5",
+ "@types/react": "^19.0.6",
+ "@types/react-dom": "^19.0.3",
"@types/react-transition-group": "^4.4.12",
"@vitejs/plugin-react-swc": "^3.7.2",
- "eslint": "^9.17.0",
+ "eslint": "^9.18.0",
"eslint-plugin-react-hooks": "^5.1.0",
- "eslint-plugin-react-refresh": "^0.4.16",
- "globals": "^15.13.0",
+ "eslint-plugin-react-refresh": "^0.4.18",
+ "globals": "^15.14.0",
"prettier": "^3.4.2",
- "typescript": "^5.7.2",
- "typescript-eslint": "^8.18.0",
- "unplugin-icons": "^0.21.0",
- "vite": "^6.0.3"
+ "typescript": "^5.7.3",
+ "typescript-eslint": "^8.19.1",
+ "unplugin-icons": "^22.0.0",
+ "vite": "^6.0.7"
}
}
diff --git a/src/components/core/Avatar/Avatar.module.scss b/src/components/core/Avatar/Avatar.module.scss
index b378c48..6d42ca8 100644
--- a/src/components/core/Avatar/Avatar.module.scss
+++ b/src/components/core/Avatar/Avatar.module.scss
@@ -1,19 +1,22 @@
-$size: var(--avatar-size);
-$border-color: var(--avatar-border-color);
-
.root {
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
- border: 2px dashed $border-color;
- width: $size;
- height: $size;
+ border: 2px dashed var(--avatar-border-color);
+ width: var(--avatar-size);
+ height: var(--avatar-size);
}
.fallback {
overflow: hidden;
- color: $border-color;
+ color: var(--avatar-border-color);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 95%;
+ height: 95%;
+ font-size: 1.5rem;
}
.img {
diff --git a/src/components/core/Avatar/Avatar.tsx b/src/components/core/Avatar/Avatar.tsx
index 8ecf6b0..bf02858 100644
--- a/src/components/core/Avatar/Avatar.tsx
+++ b/src/components/core/Avatar/Avatar.tsx
@@ -1,5 +1,5 @@
import styles from "./Avatar.module.scss";
-import UserCircleBoldDuotone from "~icons/solar/user-circle-bold-duotone";
+import UserCircleBoldDuotone from "~icons/solar/user-circle-bold";
import useThemeColor from "@/hooks/useThemeColor";
import { ComponentProps, useState } from "react";
import { Box } from "../Box";
@@ -19,7 +19,9 @@ export function Avatar(props: AvatarProps) {
alt = "",
color = "primary",
size = "3rem",
- fallback = ,
+ fallback = (
+
+ ),
style,
...rest
} = props;
diff --git a/src/components/core/Button/Button.module.scss b/src/components/core/Button/Button.module.scss
index 1b7c8f4..f143420 100644
--- a/src/components/core/Button/Button.module.scss
+++ b/src/components/core/Button/Button.module.scss
@@ -29,6 +29,19 @@
border-color: var(--button-bg-color);
}
+ &[data-variant="tonal"] {
+ background-color: var(--bg-color);
+ border-color: var(--button-bg-color);
+ }
+
+ &[data-variant="subtle"] {
+ background-color: color-mix(
+ in srgb,
+ var(--button-bg-color) 30%,
+ #ffffff 10%
+ );
+ }
+
&[data-variant="ghost"] {
background-color: transparent;
box-shadow: none;
diff --git a/src/components/core/Button/Button.tsx b/src/components/core/Button/Button.tsx
index 7b8ee08..1131592 100644
--- a/src/components/core/Button/Button.tsx
+++ b/src/components/core/Button/Button.tsx
@@ -10,7 +10,8 @@ export interface ButtonProps extends ComponentPropsWithRef<"button"> {
width?: number | string;
height?: string;
color?: ThemeColor | string;
- variant?: "solid" | "outlined" | "ghost";
+ bgColor?: string;
+ variant?: "solid" | "outlined" | "tonal" | "subtle" | "ghost";
justify?: "start" | "center" | "end";
align?: "start" | "center" | "end";
radius?: string | number;
diff --git a/src/components/widgets/Navbar/Dropdown/Dropdown.tsx b/src/components/widgets/Navbar/Dropdown/Dropdown.tsx
index 0fa958d..f2995f2 100644
--- a/src/components/widgets/Navbar/Dropdown/Dropdown.tsx
+++ b/src/components/widgets/Navbar/Dropdown/Dropdown.tsx
@@ -1,13 +1,16 @@
import Sun2BoldDuotone from "~icons/solar/sun-2-bold-duotone";
import MoonBoldDuotone from "~icons/solar/moon-bold-duotone";
+import LogoutLineDuotone from "~icons/solar/logout-linear";
import { Avatar, Button, Stack } from "@/components/core";
import { Flex } from "@/components/core/Flex";
import { useThemeStore } from "@/stores/theme";
import { IconButton } from "@/components/core/IconButton";
import { useAuthStore } from "@/stores/auth";
+import { useNavigate } from "react-router";
export function Dropdown() {
const authStore = useAuthStore();
+ const navigate = useNavigate();
return (
-
+ {authStore?.user && (
+ }
+ onClick={() => authStore?.clear()}
+ >
+ 退出登录
+
+ )}
);
}
diff --git a/src/components/widgets/Navbar/Navbar.tsx b/src/components/widgets/Navbar/Navbar.tsx
index 744cc46..763ce9f 100644
--- a/src/components/widgets/Navbar/Navbar.tsx
+++ b/src/components/widgets/Navbar/Navbar.tsx
@@ -2,6 +2,7 @@ import PlanetBoldDuotone from "~icons/solar/planet-bold-duotone";
import Book2BoldDuotone from "~icons/solar/book-2-bold-duotone";
import FlagBoldDuotone from "~icons/solar/flag-bold-duotone";
import UsersGroupTwoRoundedBoldDuotone from "~icons/solar/users-group-two-rounded-bold-duotone";
+import UserBoldDuotone from "~icons/solar/user-circle-bold-duotone";
import SolarSettingsBoldDuotone from "~icons/solar/settings-bold-duotone";
import SolarRoundArrowLeftBoldDuotone from "~icons/solar/round-arrow-left-bold-duotone";
import StarFallMinimalistic2BoldDuotone from "~icons/solar/star-fall-minimalistic-2-bold-duotone";
@@ -23,10 +24,12 @@ import { Dropdown } from "./Dropdown";
import React from "react";
import { get } from "@/api/game";
import { Game } from "@/models/game";
+import { useAuthStore } from "@/stores/auth";
export function Navbar() {
const location = useLocation();
const { id } = useParams();
+ const authStore = useAuthStore();
const links = {
default: [
@@ -222,8 +225,18 @@ export function Navbar() {
ref={dropdownMenuButtonRef}
>
E>}
+ src={
+ authStore?.user
+ ? `/api/users/${authStore?.user?.id}/avatar`
+ : ""
+ }
+ fallback={
+
+ }
color={"transparent"}
/>
diff --git a/src/pages/challenges/_blocks/Empty/Empty.module.scss b/src/pages/challenges/_blocks/Empty/Empty.module.scss
new file mode 100644
index 0000000..bc3bd70
--- /dev/null
+++ b/src/pages/challenges/_blocks/Empty/Empty.module.scss
@@ -0,0 +1,8 @@
+.root {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 1.5em;
+ user-select: none;
+}
diff --git a/src/pages/challenges/_blocks/Empty/Empty.tsx b/src/pages/challenges/_blocks/Empty/Empty.tsx
new file mode 100644
index 0000000..b529932
--- /dev/null
+++ b/src/pages/challenges/_blocks/Empty/Empty.tsx
@@ -0,0 +1,17 @@
+import { Stack } from "@/components/core";
+import FlagBoldDuotone from "~icons/solar/flag-bold-duotone";
+import styles from "./Empty.module.scss";
+
+export function Empty() {
+ return (
+
+
+ 暂无题目
+
+ );
+}
diff --git a/src/pages/challenges/_blocks/Empty/index.tsx b/src/pages/challenges/_blocks/Empty/index.tsx
new file mode 100644
index 0000000..62db648
--- /dev/null
+++ b/src/pages/challenges/_blocks/Empty/index.tsx
@@ -0,0 +1 @@
+export { Empty } from "./Empty";
diff --git a/src/pages/challenges/index.tsx b/src/pages/challenges/index.tsx
index 0aab40e..69c5737 100644
--- a/src/pages/challenges/index.tsx
+++ b/src/pages/challenges/index.tsx
@@ -19,6 +19,7 @@ import { useResizeDetector } from "react-resize-detector";
import { LoadingOverlay } from "@/components/core/LoadingOverlay/LoadingOverlay";
import { Box } from "@/components/core/Box";
import { useSharedStore } from "@/stores/shared";
+import { Empty } from "./_blocks/Empty";
export function Index() {
const authStore = useAuthStore();
@@ -75,7 +76,6 @@ export function Index() {
}).then((res) => {
setChallengeStatus(res.data);
});
- setLoading(false);
}
useEffect(() => {
@@ -95,6 +95,7 @@ export function Index() {
if (challenges?.length) {
fetchChallengeStatus();
}
+ setLoading(false);
}, [challenges, sharedStore.refresh]);
return (
@@ -124,6 +125,9 @@ export function Index() {
onChange={setIdInput}
variant={"solid"}
width={"10%"}
+ style={{
+ minWidth: "125px",
+ }}
/>
}
@@ -168,20 +172,26 @@ export function Index() {
}}
ref={challengeGroupRef}
>
- {challenges?.map((challenge) => (
- {
- setSelectedChallenge(challenge);
- setSelectedChallengeStatus(
+ {challenges?.length ? (
+ challenges?.map((challenge) => (
+
- ))}
+ }
+ key={challenge?.id}
+ onClick={() => {
+ setSelectedChallenge(challenge);
+ setSelectedChallengeStatus(
+ challengeStatus?.[challenge.id!]
+ );
+ setModalOpen(true);
+ }}
+ />
+ ))
+ ) : (
+
+ )}
diff --git a/src/pages/games/_blocks/All/All.tsx b/src/pages/games/_blocks/All/All.tsx
index 38cc349..d8dba39 100644
--- a/src/pages/games/_blocks/All/All.tsx
+++ b/src/pages/games/_blocks/All/All.tsx
@@ -6,6 +6,7 @@ import { Game } from "@/models/game";
import { get } from "@/api/game";
import { GameCard } from "./GameCard";
import { useNavigate } from "react-router";
+import { Empty } from "../Empty";
export function All() {
const navigate = useNavigate();
@@ -65,14 +66,23 @@ export function All() {
搜索
-
- {games?.map((game) => (
- navigate(`/games/${game?.id}`)}
- />
- ))}
+
+ {games?.length ? (
+ games?.map((game) => (
+ navigate(`/games/${game?.id}`)}
+ />
+ ))
+ ) : (
+
+ )}
+
+ 暂无比赛
+
+ );
+}
diff --git a/src/pages/games/_blocks/Empty/index.tsx b/src/pages/games/_blocks/Empty/index.tsx
new file mode 100644
index 0000000..62db648
--- /dev/null
+++ b/src/pages/games/_blocks/Empty/index.tsx
@@ -0,0 +1 @@
+export { Empty } from "./Empty";
diff --git a/src/pages/games/_blocks/Recent/Recent.module.scss b/src/pages/games/_blocks/Recent/Recent.module.scss
index 37ea833..6d4f5e7 100644
--- a/src/pages/games/_blocks/Recent/Recent.module.scss
+++ b/src/pages/games/_blocks/Recent/Recent.module.scss
@@ -25,7 +25,7 @@
transform: translate(-50%, -50%);
padding: 1.5rem;
border-radius: 25px;
- background-color: light-dark(#0000000d, #ffffff0d);
+ background-color: light-dark(#0000000d, #ffffff00);
.info-section {
position: absolute;
@@ -39,9 +39,9 @@
box-sizing: border-box;
position: absolute;
border-radius: 35px;
- top: -2px;
- left: -2px;
- height: calc(12vh + 4px);
+ top: -3px;
+ left: -3px;
+ height: calc(12vh + 6px);
aspect-ratio: 1.25/1;
background-color: var(--color-primary);
border: 2.75px solid var(--color-primary);
diff --git a/src/pages/games/_blocks/Recent/Recent.tsx b/src/pages/games/_blocks/Recent/Recent.tsx
index d50f987..7ee5892 100644
--- a/src/pages/games/_blocks/Recent/Recent.tsx
+++ b/src/pages/games/_blocks/Recent/Recent.tsx
@@ -9,6 +9,7 @@ import CupBold from "~icons/solar/cup-bold";
import Planet2BoldDuotone from "~icons/solar/planet-2-bold-duotone";
import PlanetBoldDuotone from "~icons/solar/planet-bold-duotone";
import { useNavigate } from "react-router";
+import { Empty } from "../Empty";
export function Recent() {
const navigate = useNavigate();
@@ -70,194 +71,213 @@ export function Recent() {
return (
-
-
-
-
- 积分榜
-
-
-
- {scoreboard?.map((item, index) => (
-
-
-
- {getRankIcon(item?.game_team?.rank!)}
-
-
-
- {item?.game_team?.team?.name}
-
-
-
-
- {item?.game_team?.pts}
-
-
- ))}
-
-
-
- {!scoreboard?.length && (
-
-
- 暂无数据
-
- )}
-
-
-
-
-
-
-
-
-
-
-
- }
- height="100%"
- width="100%"
- radius={9999}
- style={{
- border: "2px solid white",
- backgroundColor: "transparent",
- }}
- />
-
-
-
+
+
+
- {games?.[index]?.title}
+ 积分榜
-
+
+ {scoreboard?.map((item, index) => (
+
+
+
+ {getRankIcon(
+ item?.game_team?.rank!
+ )}
+
+
+
+ {
+ item?.game_team?.team
+ ?.name
+ }
+
+
+
+
+ {item?.game_team?.pts}
+
+
+ ))}
+
+
+
+ {!scoreboard?.length && (
+
- {games?.[index]?.sketch}
-
-
-
-
-
- }
- height={"4rem"}
- width={"10rem"}
- radius={"9999px"}
- onClick={() =>
- navigate(`/games/${games?.[index]?.id}`)
- }
- >
- 进入
-
+
+ 暂无数据
+
+ )}
+
-
-
- {games?.map((_, i) => (
- {
- setIndex(i);
+
+
+
+
- ))}
-
-
-
+
+
+
+
+
+ }
+ height="100%"
+ width="100%"
+ radius={9999}
+ style={{
+ border: "2px solid white",
+ backgroundColor:
+ "transparent",
+ }}
+ />
+
+
+
+
+ {games?.[index]?.title}
+
+
+ {games?.[index]?.sketch}
+
+
+
+
+
+ }
+ height={"4rem"}
+ width={"10rem"}
+ radius={"9999px"}
+ onClick={() =>
+ navigate(`/games/${games?.[index]?.id}`)
+ }
+ >
+ 进入
+
+
+
+
+ {games?.map((_, i) => (
+ {
+ setIndex(i);
+ }}
+ />
+ ))}
+
+
+
+ >
+ ) : (
+
+ )}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index de056b8..7864fc4 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -115,13 +115,15 @@ ReactDOM.render(
123
- {/* */}
123
123
+
+ 123
+