From d281a6c8040f2b34853207db9a0d7b2f6159e1b8 Mon Sep 17 00:00:00 2001
From: tone <3341154833@qq.com>
Date: Sat, 10 May 2025 12:08:04 +0800
Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=89=8D=E7=AB=AF=E7=9B=AE?=
=?UTF-8?q?=E5=BD=95=E7=BB=93=E6=9E=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../{ => (with-header-footer)}/blog/page.tsx | 0
.../app/(with-header-footer)/layout.tsx | 18 +
.../app/{ => (with-header-footer)}/page.tsx | 2 +-
.../resource/components/ResourceCard.tsx | 0
.../resource/page.tsx | 0
tone-page-web/app/components/Header.tsx | 2 +-
tone-page-web/app/console/account/page.tsx | 7 -
.../login/components/EmailLoginMode.tsx | 67 ++
.../console/login/components/LoginHeader.tsx | 12 +
.../login/components/PasswordLoginMode.tsx | 41 +
.../login/components/PhoneLoginMode.tsx | 67 ++
.../console/login}/components/login-bg.jpg | Bin
.../app/console/login/components/types.ts | 16 +
tone-page-web/app/console/login/page.tsx | 140 ++++
tone-page-web/app/console/page.tsx | 67 +-
tone-page-web/app/dashboard/page.tsx | 55 ++
tone-page-web/app/layout.tsx | 2 -
tone-page-web/components/app-sidebar.tsx | 175 +++++
tone-page-web/components/login-form.tsx | 326 --------
tone-page-web/components/nav-main.tsx | 73 ++
tone-page-web/components/nav-projects.tsx | 89 +++
tone-page-web/components/nav-user.tsx | 114 +++
tone-page-web/components/team-switcher.tsx | 91 +++
tone-page-web/components/ui/breadcrumb.tsx | 109 +++
tone-page-web/components/ui/collapsible.tsx | 33 +
tone-page-web/components/ui/dropdown-menu.tsx | 257 +++++++
tone-page-web/components/ui/separator.tsx | 28 +
tone-page-web/components/ui/sheet.tsx | 139 ++++
tone-page-web/components/ui/sidebar.tsx | 726 ++++++++++++++++++
tone-page-web/components/ui/skeleton.tsx | 13 +
tone-page-web/components/ui/tooltip.tsx | 61 ++
tone-page-web/hooks/use-mobile.ts | 19 +
tone-page-web/lib/api/auth/index.ts | 1 +
tone-page-web/lib/api/auth/login.ts | 53 ++
tone-page-web/lib/api/fetcher.ts | 27 +
tone-page-web/lib/api/index.ts | 2 +
tone-page-web/lib/api/verification/index.ts | 1 +
tone-page-web/lib/api/verification/send.ts | 15 +
tone-page-web/next.config.ts | 8 +
tone-page-web/package.json | 4 +
tone-page-web/pnpm-lock.yaml | 404 ++++++++++
41 files changed, 2909 insertions(+), 355 deletions(-)
rename tone-page-web/app/{ => (with-header-footer)}/blog/page.tsx (100%)
create mode 100644 tone-page-web/app/(with-header-footer)/layout.tsx
rename tone-page-web/app/{ => (with-header-footer)}/page.tsx (96%)
rename tone-page-web/app/{ => (with-header-footer)}/resource/components/ResourceCard.tsx (100%)
rename tone-page-web/app/{ => (with-header-footer)}/resource/page.tsx (100%)
delete mode 100644 tone-page-web/app/console/account/page.tsx
create mode 100644 tone-page-web/app/console/login/components/EmailLoginMode.tsx
create mode 100644 tone-page-web/app/console/login/components/LoginHeader.tsx
create mode 100644 tone-page-web/app/console/login/components/PasswordLoginMode.tsx
create mode 100644 tone-page-web/app/console/login/components/PhoneLoginMode.tsx
rename tone-page-web/{ => app/console/login}/components/login-bg.jpg (100%)
create mode 100644 tone-page-web/app/console/login/components/types.ts
create mode 100644 tone-page-web/app/console/login/page.tsx
create mode 100644 tone-page-web/app/dashboard/page.tsx
create mode 100644 tone-page-web/components/app-sidebar.tsx
delete mode 100644 tone-page-web/components/login-form.tsx
create mode 100644 tone-page-web/components/nav-main.tsx
create mode 100644 tone-page-web/components/nav-projects.tsx
create mode 100644 tone-page-web/components/nav-user.tsx
create mode 100644 tone-page-web/components/team-switcher.tsx
create mode 100644 tone-page-web/components/ui/breadcrumb.tsx
create mode 100644 tone-page-web/components/ui/collapsible.tsx
create mode 100644 tone-page-web/components/ui/dropdown-menu.tsx
create mode 100644 tone-page-web/components/ui/separator.tsx
create mode 100644 tone-page-web/components/ui/sheet.tsx
create mode 100644 tone-page-web/components/ui/sidebar.tsx
create mode 100644 tone-page-web/components/ui/skeleton.tsx
create mode 100644 tone-page-web/components/ui/tooltip.tsx
create mode 100644 tone-page-web/hooks/use-mobile.ts
create mode 100644 tone-page-web/lib/api/auth/index.ts
create mode 100644 tone-page-web/lib/api/auth/login.ts
create mode 100644 tone-page-web/lib/api/fetcher.ts
create mode 100644 tone-page-web/lib/api/index.ts
create mode 100644 tone-page-web/lib/api/verification/index.ts
create mode 100644 tone-page-web/lib/api/verification/send.ts
diff --git a/tone-page-web/app/blog/page.tsx b/tone-page-web/app/(with-header-footer)/blog/page.tsx
similarity index 100%
rename from tone-page-web/app/blog/page.tsx
rename to tone-page-web/app/(with-header-footer)/blog/page.tsx
diff --git a/tone-page-web/app/(with-header-footer)/layout.tsx b/tone-page-web/app/(with-header-footer)/layout.tsx
new file mode 100644
index 0000000..587bf07
--- /dev/null
+++ b/tone-page-web/app/(with-header-footer)/layout.tsx
@@ -0,0 +1,18 @@
+import Header from "../components/Header";
+import Footer from "../components/Footer";
+
+export default function LayoutWithHeaderFooter({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+ <>
+
+
+ {children}
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/tone-page-web/app/page.tsx b/tone-page-web/app/(with-header-footer)/page.tsx
similarity index 96%
rename from tone-page-web/app/page.tsx
rename to tone-page-web/app/(with-header-footer)/page.tsx
index 5fdb354..f549371 100644
--- a/tone-page-web/app/page.tsx
+++ b/tone-page-web/app/(with-header-footer)/page.tsx
@@ -1,5 +1,5 @@
'use client';
-import favicon from './favicon.ico';
+import favicon from '../favicon.ico';
import Image from 'next/image';
export default function Home() {
diff --git a/tone-page-web/app/resource/components/ResourceCard.tsx b/tone-page-web/app/(with-header-footer)/resource/components/ResourceCard.tsx
similarity index 100%
rename from tone-page-web/app/resource/components/ResourceCard.tsx
rename to tone-page-web/app/(with-header-footer)/resource/components/ResourceCard.tsx
diff --git a/tone-page-web/app/resource/page.tsx b/tone-page-web/app/(with-header-footer)/resource/page.tsx
similarity index 100%
rename from tone-page-web/app/resource/page.tsx
rename to tone-page-web/app/(with-header-footer)/resource/page.tsx
diff --git a/tone-page-web/app/components/Header.tsx b/tone-page-web/app/components/Header.tsx
index 0875e94..6c153e7 100644
--- a/tone-page-web/app/components/Header.tsx
+++ b/tone-page-web/app/components/Header.tsx
@@ -24,7 +24,7 @@ export default function Header() {
{ name: '特恩(TONE)', href: '/' },
{ name: '资源', href: '/resource' },
{ name: '博客', href: '/blog' },
- { name: '控制台', href: '/console' },
+ { name: '控制台', href: '/console/login' },
]
return (
diff --git a/tone-page-web/app/console/account/page.tsx b/tone-page-web/app/console/account/page.tsx
deleted file mode 100644
index a9c8911..0000000
--- a/tone-page-web/app/console/account/page.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function Account() {
- return (
-
-
-
- )
-}
\ No newline at end of file
diff --git a/tone-page-web/app/console/login/components/EmailLoginMode.tsx b/tone-page-web/app/console/login/components/EmailLoginMode.tsx
new file mode 100644
index 0000000..15d2a27
--- /dev/null
+++ b/tone-page-web/app/console/login/components/EmailLoginMode.tsx
@@ -0,0 +1,67 @@
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp";
+import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
+import { useState, useCallback } from "react";
+import { toast } from "sonner";
+import LoginHeader from "./LoginHeader";
+import { SendCodeFormData } from "./types";
+import { Label } from "@/components/ui/label";
+
+export default function EmailLoginMode({ onSendCode }: { onSendCode: (data: SendCodeFormData) => Promise }) {
+ const [email, setEmail] = useState("");
+ const handleSendCode = useCallback(() => {
+ if (!email.trim().match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
+ toast.error('请输入正确的邮箱地址');
+ return;
+ }
+ onSendCode({
+ type: 'email',
+ email,
+ })
+ }, [email, onSendCode]);
+
+ return (
+ <>
+
+
+
+ setEmail(e.target.value)}
+ required />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/tone-page-web/app/console/login/components/LoginHeader.tsx b/tone-page-web/app/console/login/components/LoginHeader.tsx
new file mode 100644
index 0000000..ff55f7e
--- /dev/null
+++ b/tone-page-web/app/console/login/components/LoginHeader.tsx
@@ -0,0 +1,12 @@
+export default function LoginHeader() {
+ return (
+ <>
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/tone-page-web/app/console/login/components/PasswordLoginMode.tsx b/tone-page-web/app/console/login/components/PasswordLoginMode.tsx
new file mode 100644
index 0000000..2b0ee47
--- /dev/null
+++ b/tone-page-web/app/console/login/components/PasswordLoginMode.tsx
@@ -0,0 +1,41 @@
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import LoginHeader from "./LoginHeader";
+import { Label } from "@/components/ui/label"
+
+export default function PasswordLoginMode({ forgetPassword }: { forgetPassword: () => void }) {
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/tone-page-web/app/console/login/components/PhoneLoginMode.tsx b/tone-page-web/app/console/login/components/PhoneLoginMode.tsx
new file mode 100644
index 0000000..771c1f9
--- /dev/null
+++ b/tone-page-web/app/console/login/components/PhoneLoginMode.tsx
@@ -0,0 +1,67 @@
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp";
+import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
+import { useState, useCallback } from "react";
+import { toast } from "sonner";
+import LoginHeader from "./LoginHeader";
+import { SendCodeFormData } from "./types";
+import { Label } from "@/components/ui/label"
+
+export default function PhoneLoginMode({ onSendCode }: { onSendCode: (data: SendCodeFormData) => Promise }) {
+ const [phone, setPhone] = useState("");
+ const handleSendCode = useCallback(() => {
+ if (phone.trim().length !== 11) {
+ toast.error('请输入正确的手机号');
+ return;
+ }
+ onSendCode({
+ type: 'phone',
+ phone,
+ })
+ }, [phone, onSendCode]);
+
+ return (
+ <>
+
+
+
+ setPhone(e.target.value)}
+ required />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/tone-page-web/components/login-bg.jpg b/tone-page-web/app/console/login/components/login-bg.jpg
similarity index 100%
rename from tone-page-web/components/login-bg.jpg
rename to tone-page-web/app/console/login/components/login-bg.jpg
diff --git a/tone-page-web/app/console/login/components/types.ts b/tone-page-web/app/console/login/components/types.ts
new file mode 100644
index 0000000..8a8a754
--- /dev/null
+++ b/tone-page-web/app/console/login/components/types.ts
@@ -0,0 +1,16 @@
+export type SubmitMode = 'password' | 'phone' | 'email';
+export type LoginFormData = {
+ type: SubmitMode;
+ account?: string;
+ password?: string;
+ phone?: string;
+ email?: string;
+ code?: string;
+}
+
+export type SendCodeMode = 'phone' | 'email';
+export type SendCodeFormData = {
+ type: SendCodeMode;
+ phone?: string;
+ email?: string;
+}
\ No newline at end of file
diff --git a/tone-page-web/app/console/login/page.tsx b/tone-page-web/app/console/login/page.tsx
new file mode 100644
index 0000000..bdc3384
--- /dev/null
+++ b/tone-page-web/app/console/login/page.tsx
@@ -0,0 +1,140 @@
+'use client';
+import { authApi, verificationApi } from "@/lib/api";
+import { useRouter } from "next/navigation";
+import { toast } from "sonner";
+import Header from "@/app/components/Header";
+import Footer from "@/app/components/Footer";
+import { Button } from "@/components/ui/button";
+import { Card, CardContent } from "@/components/ui/card";
+import { cn } from "@/lib/utils";
+import { KeyRound, Phone, Mail } from "lucide-react";
+import EmailLoginMode from "./components/EmailLoginMode";
+import PasswordLoginMode from "./components/PasswordLoginMode";
+import PhoneLoginMode from "./components/PhoneLoginMode";
+import { LoginFormData, SendCodeFormData, SubmitMode } from "./components/types";
+import { useCallback, useState } from "react";
+import LoginBG from './components/login-bg.jpg';
+import Image from "next/image";
+
+export default function Login() {
+ const router = useRouter();
+ const [loginMode, setLoginMode] = useState('password');
+
+ const handleForgetPassword = useCallback(() => {
+ toast.warning('开发中,敬请期待!暂时可通过发送邮件至网站管理员进行密码重置。');
+ }, []);
+
+ const handleSendCode = async (data: SendCodeFormData) => {
+ const res = await verificationApi.send({
+ type: 'login',
+ targetType: data.type,
+ phone: data.phone,
+ email: data.email,
+ })
+
+ if (res.statusCode === 200) {
+ toast.success('验证码已发送,请注意查收');
+ return true;
+ } else {
+ toast.error(res.message || '验证码发送失败,请稍后再试');
+ return false;
+ }
+ }
+
+ const handleSubmit = async (data: LoginFormData) => {
+ const res = await authApi.login({
+ ...data,
+ });
+
+ if (res.statusCode === 200 && res.data) {
+ toast.success('登录成功');
+ localStorage.setItem('token', res.data.token);
+ router.replace('/console');
+ return true;
+ } else {
+ toast.error(res.message || '登录失败,请稍后再试');
+ return false;
+ }
+ }
+
+ 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 714b9df..240524a 100644
--- a/tone-page-web/app/console/page.tsx
+++ b/tone-page-web/app/console/page.tsx
@@ -1,21 +1,52 @@
-'use client';
+import { AppSidebar } from "@/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/components/ui/breadcrumb"
+import { Separator } from "@/components/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/components/ui/sidebar"
-import { LoginForm, LoginFormData } from "@/components/login-form";
-
-const handleSubmit = async (data: LoginFormData) => {
- console.log(data)
-}
-
-const handleSendCode = async (data: LoginFormData) => {
- console.log(data)
-}
-
-export default function Console() {
+export default function Page() {
return (
-
+
+
+
+
+
+
+
)
-}
\ No newline at end of file
+}
diff --git a/tone-page-web/app/dashboard/page.tsx b/tone-page-web/app/dashboard/page.tsx
new file mode 100644
index 0000000..18a85b1
--- /dev/null
+++ b/tone-page-web/app/dashboard/page.tsx
@@ -0,0 +1,55 @@
+import { AppSidebar } from "@/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/components/ui/breadcrumb"
+import { Separator } from "@/components/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/components/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/tone-page-web/app/layout.tsx b/tone-page-web/app/layout.tsx
index 7fd9a77..fa61fc0 100644
--- a/tone-page-web/app/layout.tsx
+++ b/tone-page-web/app/layout.tsx
@@ -35,12 +35,10 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
-
{children}
-