From e2b47681a3f7b63f567f9084c755156451df6ed4 Mon Sep 17 00:00:00 2001 From: zetaloop Date: Fri, 20 Feb 2026 14:17:26 +0800 Subject: [PATCH] feat: login/register pages, login dialog, homepage with game categories and player/shop cards Use standardSchemaResolver instead of zodResolver to work around Zod v4 type incompatibility with @hookform/resolvers. --- app/(auth)/login/page.tsx | 64 +++++++++++++- app/(auth)/register/page.tsx | 88 ++++++++++++++++++- app/(main)/page.tsx | 159 ++++++++++++++++++++++++++++++++--- components/login-dialog.tsx | 91 ++++++++++++++++++++ next-env.d.ts | 2 +- package.json | 1 + pnpm-lock.yaml | 8 ++ 7 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 components/login-dialog.tsx diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index bb7cd40..eb63f70 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -1,8 +1,66 @@ +"use client" + +import { standardSchemaResolver } from "@hookform/resolvers/standard-schema" +import { Gamepad2 } from "lucide-react" +import Link from "next/link" +import { useRouter } from "next/navigation" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { useAuthStore } from "@/store/auth" + +const loginSchema = z.object({ + phone: z.string().min(11, "请输入正确的手机号"), + password: z.string().min(6, "密码至少6位"), +}) + export default function LoginPage() { + const router = useRouter() + const { login } = useAuthStore() + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: standardSchemaResolver(loginSchema), + }) + + const onSubmit = async (_data: z.infer) => { + await new Promise((r) => setTimeout(r, 500)) + login() + router.push("/") + } + return ( -
-

登录

-

登录你的聚玩账号

+
+
+ +

登录聚玩

+

输入你的账号信息

+
+
+
+ + + {errors.phone &&

{errors.phone.message}

} +
+
+ + + {errors.password &&

{errors.password.message}

} +
+ +
+

+ 还没有账号?{" "} + + 立即注册 + +

) } diff --git a/app/(auth)/register/page.tsx b/app/(auth)/register/page.tsx index 8fc450e..cc32ed3 100644 --- a/app/(auth)/register/page.tsx +++ b/app/(auth)/register/page.tsx @@ -1,8 +1,90 @@ +"use client" + +import { standardSchemaResolver } from "@hookform/resolvers/standard-schema" +import { Gamepad2 } from "lucide-react" +import Link from "next/link" +import { useRouter } from "next/navigation" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { useAuthStore } from "@/store/auth" + +const registerSchema = z + .object({ + nickname: z.string().min(2, "昵称至少2个字符"), + phone: z.string().min(11, "请输入正确的手机号"), + password: z.string().min(6, "密码至少6位"), + confirmPassword: z.string(), + }) + .refine((data) => data.password === data.confirmPassword, { + message: "两次密码不一致", + path: ["confirmPassword"], + }) + export default function RegisterPage() { + const router = useRouter() + const { login } = useAuthStore() + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: standardSchemaResolver(registerSchema), + }) + + const onSubmit = async (_data: z.infer) => { + await new Promise((r) => setTimeout(r, 500)) + login() + router.push("/") + } + return ( -
-

注册

-

创建你的聚玩账号

+
+
+ +

注册聚玩

+

创建你的聚玩账号

+
+
+
+ + + {errors.nickname &&

{errors.nickname.message}

} +
+
+ + + {errors.phone &&

{errors.phone.message}

} +
+
+ + + {errors.password &&

{errors.password.message}

} +
+
+ + + {errors.confirmPassword && ( +

{errors.confirmPassword.message}

+ )} +
+ +
+

+ 已有账号?{" "} + + 立即登录 + +

) } diff --git a/app/(main)/page.tsx b/app/(main)/page.tsx index 8e270ac..39f4bb0 100644 --- a/app/(main)/page.tsx +++ b/app/(main)/page.tsx @@ -1,22 +1,153 @@ +import { ArrowRight, Search, ShoppingBag, Star } from "lucide-react" +import Link from "next/link" +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" +import { mockGames, mockPlayers, mockShops } from "@/lib/mock-data" + export default function HomePage() { return ( -
-

聚玩

-

游戏陪玩服务平台

-
-
-

游戏分类

-

按游戏浏览陪玩服务

+
+
+

找到你的游戏搭档

+

+ 专业陪玩、代练、上分,覆盖主流游戏,安全可靠 +

+
+ +
-
-

推荐打手

-

热门打手推荐

+
+ +
+
+

游戏分类

+
-
-

热门店铺

-

优质店铺推荐

+
+ {mockGames.map((game) => ( + + {game.icon} + {game.name} + + ))}
-
+
+ +
+
+

推荐打手

+ +
+
+ {mockPlayers.map((player) => ( + + + + + {player.user.nickname[0]} + +
+ {player.user.nickname} +
+
+ + {player.rating} +
+ {player.totalOrders} 单 + + {player.status === "available" + ? "可接单" + : player.status === "busy" + ? "忙碌" + : "离线"} + +
+
+
+ +
+ {player.tags.map((tag) => ( + + {tag} + + ))} +
+ {player.shopName && ( +

所属店铺: {player.shopName}

+ )} +
+ + + +
+ ))} +
+
+ +
+
+

热门店铺

+ +
+
+ {mockShops.map((shop) => ( + + + {shop.name} +

{shop.description}

+
+ +
+
+ + {shop.rating} +
+
+ + {shop.totalOrders} 单 +
+ {shop.playerCount} 名打手 +
+
+ + + +
+ ))} +
+
) } diff --git a/components/login-dialog.tsx b/components/login-dialog.tsx new file mode 100644 index 0000000..1ea657f --- /dev/null +++ b/components/login-dialog.tsx @@ -0,0 +1,91 @@ +"use client" + +import { standardSchemaResolver } from "@hookform/resolvers/standard-schema" +import { useRouter } from "next/navigation" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { Button } from "@/components/ui/button" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { useAuthStore } from "@/store/auth" + +const loginSchema = z.object({ + phone: z.string().min(11, "请输入正确的手机号"), + password: z.string().min(6, "密码至少6位"), +}) + +interface LoginDialogProps { + open: boolean + onOpenChange: (open: boolean) => void +} + +export function LoginDialog({ open, onOpenChange }: LoginDialogProps) { + const router = useRouter() + const { login } = useAuthStore() + const { + register, + handleSubmit, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: standardSchemaResolver(loginSchema), + }) + + const onSubmit = async (_data: z.infer) => { + await new Promise((r) => setTimeout(r, 500)) + login() + onOpenChange(false) + } + + return ( + + + + 登录聚玩 + 请先登录以继续操作 + +
+
+ + + {errors.phone &&

{errors.phone.message}

} +
+
+ + + {errors.password && ( +

{errors.password.message}

+ )} +
+
+ + +
+
+
+
+ ) +} diff --git a/next-env.d.ts b/next-env.d.ts index 9edff1c..7a70f65 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,6 @@ /// /// -import "./.next/types/routes.d.ts"; +import "./.next/types/routes.d.ts" // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/package.json b/package.json index 5df8634..1825d78 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@hookform/resolvers": "^5.2.2", + "@standard-schema/spec": "^1.1.0", "@tanstack/react-query": "^5.90.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5374869..35f7d68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@hookform/resolvers': specifier: ^5.2.2 version: 5.2.2(react-hook-form@7.71.1(react@19.2.3)) + '@standard-schema/spec': + specifier: ^1.1.0 + version: 1.1.0 '@tanstack/react-query': specifier: ^5.90.21 version: 5.90.21(react@19.2.3) @@ -1317,6 +1320,9 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -4111,6 +4117,8 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} + '@standard-schema/spec@1.1.0': {} + '@standard-schema/utils@0.3.0': {} '@swc/helpers@0.5.15':