From 2c5320347376c556027aaf8439f037b53b16d1b3 Mon Sep 17 00:00:00 2001 From: tone <3341154833@qq.com> Date: Fri, 25 Apr 2025 23:23:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=99=BB=E5=BD=95=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tone-page-web/app/console/account/page.tsx | 7 + tone-page-web/app/console/page.tsx | 8 +- tone-page-web/app/login/page.tsx | 11 + tone-page-web/components/login-form.tsx | 270 +++++++++++++++++++++ tone-page-web/components/ui/card.tsx | 92 +++++++ tone-page-web/components/ui/input-otp.tsx | 77 ++++++ tone-page-web/components/ui/input.tsx | 21 ++ tone-page-web/components/ui/label.tsx | 24 ++ tone-page-web/package.json | 2 + tone-page-web/pnpm-lock.yaml | 39 +++ 10 files changed, 550 insertions(+), 1 deletion(-) create mode 100644 tone-page-web/app/console/account/page.tsx create mode 100644 tone-page-web/app/login/page.tsx create mode 100644 tone-page-web/components/login-form.tsx create mode 100644 tone-page-web/components/ui/card.tsx create mode 100644 tone-page-web/components/ui/input-otp.tsx create mode 100644 tone-page-web/components/ui/input.tsx create mode 100644 tone-page-web/components/ui/label.tsx diff --git a/tone-page-web/app/console/account/page.tsx b/tone-page-web/app/console/account/page.tsx new file mode 100644 index 0000000..a9c8911 --- /dev/null +++ b/tone-page-web/app/console/account/page.tsx @@ -0,0 +1,7 @@ +export default function Account() { + return ( +
+ +
+ ) +} \ No newline at end of file diff --git a/tone-page-web/app/console/page.tsx b/tone-page-web/app/console/page.tsx index 4c06567..18f6828 100644 --- a/tone-page-web/app/console/page.tsx +++ b/tone-page-web/app/console/page.tsx @@ -1,5 +1,11 @@ +import { LoginForm } from "@/components/login-form"; + export default function Console() { return ( -
+
+
+ +
+
) } \ No newline at end of file diff --git a/tone-page-web/app/login/page.tsx b/tone-page-web/app/login/page.tsx new file mode 100644 index 0000000..45da691 --- /dev/null +++ b/tone-page-web/app/login/page.tsx @@ -0,0 +1,11 @@ +import { LoginForm } from "@/components/login-form" + +export default function LoginPage() { + return ( +
+
+ +
+
+ ) +} diff --git a/tone-page-web/components/login-form.tsx b/tone-page-web/components/login-form.tsx new file mode 100644 index 0000000..f70d3f9 --- /dev/null +++ b/tone-page-web/components/login-form.tsx @@ -0,0 +1,270 @@ +'use client'; +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Card, CardContent } from "@/components/ui/card" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { KeyRound, Phone, Mail } from "lucide-react" +import { useState } from "react" +import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp" +import { InputOTP, InputOTPGroup, InputOTPSlot } from "./ui/input-otp"; + +function LoginHeader() { + return ( + <> +
+

欢迎回来

+

+ 登陆到您的账户 +

+
+ + ) +} + +function RegisterHeader() { + return ( + <> +
+

欢迎注册

+

+ 注册您的账号 +

+
+ + ) +} + +function PasswordMode() { + return ( + <> + +
+ + +
+
+
+ + + 忘记密码? + +
+ +
+ + + ) +} + +function PhoneMode() { + return ( + <> + +
+ + +
+
+
+ +
+
+ + + + + + + + + + + +
+
+ + + ) +} + +function EmailMode() { + return ( + <> + +
+ + +
+
+
+ +
+
+ + + + + + + + + + + +
+
+ + + ) +} + +function RegisterMode() { + return ( + <> + +
+ + +
+
+ + +
+
+
+ +
+
+ + + + + + + + + + + +
+
+ + + ) +} + +export function LoginForm({ + className, + ...props +}: React.ComponentProps<"div">) { + const [loginMode, setLoginMode] = useState<'password' | 'phone' | 'email' | 'register'>('password'); + + return ( +
+ + +
+
+ + {loginMode === 'password' ? : null} + {loginMode === 'phone' ? : null} + {loginMode === 'email' ? : null} + {loginMode === 'register' ? : null} + + { + loginMode !== 'register' && ( + <> +
+ + 或者使用 + +
+
+ + + +
+ + ) + } + + { + loginMode === 'register' ? ( + <> + + + ) : ( + <> +
+ 还没有账号?{" "} + setLoginMode('register')}> + 注册 + +
+ + ) + } +
+
+
+ Image +
+
+
+
+ 登录即表示您同意我们的{" "} + + 服务条款 + {" "} + 和{" "} + + 隐私政策 + +
+
+ ) +} diff --git a/tone-page-web/components/ui/card.tsx b/tone-page-web/components/ui/card.tsx new file mode 100644 index 0000000..d05bbc6 --- /dev/null +++ b/tone-page-web/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/tone-page-web/components/ui/input-otp.tsx b/tone-page-web/components/ui/input-otp.tsx new file mode 100644 index 0000000..614f70e --- /dev/null +++ b/tone-page-web/components/ui/input-otp.tsx @@ -0,0 +1,77 @@ +"use client" + +import * as React from "react" +import { OTPInput, OTPInputContext } from "input-otp" +import { MinusIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function InputOTP({ + className, + containerClassName, + ...props +}: React.ComponentProps & { + containerClassName?: string +}) { + return ( + + ) +} + +function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function InputOTPSlot({ + index, + className, + ...props +}: React.ComponentProps<"div"> & { + index: number +}) { + const inputOTPContext = React.useContext(OTPInputContext) + const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {} + + return ( +
+ {char} + {hasFakeCaret && ( +
+
+
+ )} +
+ ) +} + +function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) { + return ( +
+ +
+ ) +} + +export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } diff --git a/tone-page-web/components/ui/input.tsx b/tone-page-web/components/ui/input.tsx new file mode 100644 index 0000000..03295ca --- /dev/null +++ b/tone-page-web/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/tone-page-web/components/ui/label.tsx b/tone-page-web/components/ui/label.tsx new file mode 100644 index 0000000..fb5fbc3 --- /dev/null +++ b/tone-page-web/components/ui/label.tsx @@ -0,0 +1,24 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" + +import { cn } from "@/lib/utils" + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Label } diff --git a/tone-page-web/package.json b/tone-page-web/package.json index a972e1f..f05df0a 100644 --- a/tone-page-web/package.json +++ b/tone-page-web/package.json @@ -12,11 +12,13 @@ "@radix-ui/react-avatar": "^1.1.7", "@radix-ui/react-dialog": "^1.1.11", "@radix-ui/react-hover-card": "^1.1.11", + "@radix-ui/react-label": "^2.1.4", "@radix-ui/react-navigation-menu": "^1.2.10", "@radix-ui/react-popover": "^1.1.11", "@radix-ui/react-slot": "^1.2.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "input-otp": "^1.4.2", "lucide-react": "^0.503.0", "next": "15.3.1", "next-themes": "^0.4.6", diff --git a/tone-page-web/pnpm-lock.yaml b/tone-page-web/pnpm-lock.yaml index 553c4c2..3000700 100644 --- a/tone-page-web/pnpm-lock.yaml +++ b/tone-page-web/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@radix-ui/react-hover-card': specifier: ^1.1.11 version: 1.1.11(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-label': + specifier: ^2.1.4 + version: 2.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-navigation-menu': specifier: ^1.2.10 version: 1.2.10(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -32,6 +35,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + input-otp: + specifier: ^1.4.2 + version: 1.4.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) lucide-react: specifier: ^0.503.0 version: 0.503.0(react@19.1.0) @@ -515,6 +521,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-label@2.1.4': + resolution: {integrity: sha512-wy3dqizZnZVV4ja0FNnUhIWNwWdoldXrneEyUcVtLYDAt8ovGS4ridtMAOGgXBBIfggL4BOveVWsjXDORdGEQg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-navigation-menu@1.2.10': resolution: {integrity: sha512-kGDqMVPj2SRB1vJmXN/jnhC66REAXNyDmDRubbbmJ+360zSIJUDmWGMKIJOf72PHMwPENrbtJVb3CMAUJDjEIA==} peerDependencies: @@ -1481,6 +1500,12 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + input-otp@1.4.2: + resolution: {integrity: sha512-l3jWwYNvrEa6NTCt7BECfCm48GvwuZzkoeG3gBL2w4CHeOXW3eKFmf9UNYkNfYc3mxMrthMnxjIE07MT0zLBQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -2581,6 +2606,15 @@ snapshots: optionalDependencies: '@types/react': 19.1.2 + '@radix-ui/react-label@2.1.4(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.0(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.2 + '@types/react-dom': 19.1.2(@types/react@19.1.2) + '@radix-ui/react-navigation-menu@1.2.10(@types/react-dom@19.1.2(@types/react@19.1.2))(@types/react@19.1.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -3673,6 +3707,11 @@ snapshots: imurmurhash@0.1.4: {} + input-otp@1.4.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + internal-slot@1.1.0: dependencies: es-errors: 1.3.0