refactor: 前端登陆页新实现

This commit is contained in:
2025-12-16 22:56:34 +08:00
parent 1cd663aa0c
commit 70bcb8015c
2 changed files with 83 additions and 63 deletions

View File

@@ -2,16 +2,24 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import LoginHeader from "./LoginHeader"; import LoginHeader from "./LoginHeader";
import { Label } from "@/components/ui/label" import { Label } from "@/components/ui/label"
import { FormEvent, useCallback } from "react";
import { toast } from "sonner";
import { AuthAPI } from "@/lib/api/client";
import { handleAPIError } from "@/lib/api/common";
export default function PasswordLoginMode() {
const handleForgetPassword = useCallback(() => {
toast.warning('开发中,敬请期待!暂时可通过发送邮件至网站管理员进行密码重置。');
}, []);
export default function PasswordLoginMode({ forgetPassword }: { forgetPassword: () => void }) {
return ( return (
<> <>
<LoginHeader /> <LoginHeader />
<div className="grid gap-3"> <div className="grid gap-3">
<Label htmlFor="email">//</Label> <Label htmlFor="password-login-mode-identifier">//</Label>
<Input <Input
id="password-login-mode-account" id="password-login-mode-identifier"
name="account" name="identifier"
type="text" type="text"
placeholder="电子邮箱/手机号/账号" placeholder="电子邮箱/手机号/账号"
required required
@@ -19,9 +27,9 @@ export default function PasswordLoginMode({ forgetPassword }: { forgetPassword:
</div> </div>
<div className="grid gap-3"> <div className="grid gap-3">
<div className="flex items-center h-4"> <div className="flex items-center h-4">
<Label htmlFor="password"></Label> <Label htmlFor="password-login-mode-password"></Label>
<a <a
onClick={forgetPassword} onClick={handleForgetPassword}
className="ml-auto text-sm underline-offset-2 hover:underline cursor-pointer" className="ml-auto text-sm underline-offset-2 hover:underline cursor-pointer"
> >
@@ -39,3 +47,10 @@ export default function PasswordLoginMode({ forgetPassword }: { forgetPassword:
</> </>
) )
} }
export async function handleSubmit(formData: FormData) {
const identifier = formData.get('identifier')?.toString() || '';
const password = formData.get('password')?.toString() || '';
return AuthAPI.loginByPassword(identifier, password)
}

View File

@@ -1,5 +1,5 @@
'use client'; 'use client';
import { authApi, verificationApi } from "@/lib/api"; // import { authApi, verificationApi } from "@/lib/api";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { toast } from "sonner"; import { toast } from "sonner";
import Header from "@/components/Header"; import Header from "@/components/Header";
@@ -7,7 +7,7 @@ import Footer from "@/components/Footer";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { KeyRound, Phone, Mail } from "lucide-react"; import { KeyRound, Phone, FileKey2 } from "lucide-react";
import EmailLoginMode from "./components/EmailLoginMode"; import EmailLoginMode from "./components/EmailLoginMode";
import PasswordLoginMode from "./components/PasswordLoginMode"; import PasswordLoginMode from "./components/PasswordLoginMode";
import PhoneLoginMode from "./components/PhoneLoginMode"; import PhoneLoginMode from "./components/PhoneLoginMode";
@@ -15,56 +15,53 @@ import { LoginFormData, SendCodeFormData, SubmitMode } from "./components/types"
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import LoginBG from './components/login-bg.jpg'; import LoginBG from './components/login-bg.jpg';
import Image from "next/image"; import Image from "next/image";
import { ApiError } from "@/lib/api/fetcher"; import { handleAPIError } from "@/lib/api/common";
// import { ApiError } from "@/lib/api/fetcher";
export default function Login() { export default function Login() {
const router = useRouter(); const router = useRouter();
const [loginMode, setLoginMode] = useState<SubmitMode>('password'); const [loginMode, setLoginMode] = useState<SubmitMode>('password');
const handleForgetPassword = useCallback(() => { // const handleSendCode = async (data: SendCodeFormData) => {
toast.warning('开发中,敬请期待!暂时可通过发送邮件至网站管理员进行密码重置。'); // try {
}, []); // const res = await verificationApi.send({
// type: 'login',
// targetType: data.type,
// phone: data.phone,
// email: data.email,
// })
const handleSendCode = async (data: SendCodeFormData) => { // if (res) {
try { // toast.success('验证码已发送,请注意查收');
const res = await verificationApi.send({ // return true;
type: 'login', // } else {
targetType: data.type, // throw new Error();
phone: data.phone, // }
email: data.email, // } catch (error) {
}) // toast.error((error as ApiError).message || '验证码发送失败,请稍后再试');
// return false;
// }
// }
if (res) { // const handleSubmit = async (data: LoginFormData) => {
toast.success('验证码已发送,请注意查收'); // try {
return true; // const res = await authApi.login({
} else { // ...data,
throw new Error(); // });
}
} catch (error) {
toast.error((error as ApiError).message || '验证码发送失败,请稍后再试');
return false;
}
}
const handleSubmit = async (data: LoginFormData) => { // if (res.token) {
try { // toast.success('登录成功');
const res = await authApi.login({ // localStorage.setItem('token', res.token);
...data, // router.replace('/console');
}); // return true;
// } else {
if (res.token) { // throw new Error();
toast.success('登录成功'); // }
localStorage.setItem('token', res.token); // } catch (error) {
router.replace('/console'); // toast.error((error as ApiError).message || '登录失败,请稍后再试');
return true; // return false;
} else { // }
throw new Error(); // }
}
} catch (error) {
toast.error((error as ApiError).message || '登录失败,请稍后再试');
return false;
}
}
return ( return (
<> <>
@@ -74,23 +71,31 @@ export default function Login() {
<div className={cn("flex flex-col gap-6")}> <div className={cn("flex flex-col gap-6")}>
<Card className="overflow-hidden p-0"> <Card className="overflow-hidden p-0">
<CardContent className="grid p-0 md:grid-cols-2"> <CardContent className="grid p-0 md:grid-cols-2">
<form className="p-6 md:p-8" onSubmit={(e) => { <form className="p-6 md:p-8" onSubmit={async e => {
e.preventDefault(); e.preventDefault();
const formData = new FormData(e.currentTarget); const formData = new FormData(e.currentTarget);
handleSubmit({
type: loginMode, let handler = (await (async () => {
account: formData.get('account')?.toString(), if (loginMode === 'password') {
password: formData.get('password')?.toString(), return import('./components/PasswordLoginMode');
phone: formData.get('phone')?.toString(), }
email: formData.get('email')?.toString(), })())?.handleSubmit;
code: formData.get('code')?.toString(), if (!handler) {
return toast.error('登陆状态异常');
}
handler(formData).then((user) => {
localStorage.setItem('user_profile', JSON.stringify(user));
// to main page
}, (e) => {
handleAPIError(e, ({ message }) => toast.error(message))
}) })
}}> }}>
<div className="flex flex-col gap-6"> <div className="flex flex-col gap-6">
{loginMode === 'password' ? <PasswordLoginMode forgetPassword={handleForgetPassword} /> : null} {loginMode === 'password' ? <PasswordLoginMode /> : null}
{loginMode === 'phone' ? <PhoneLoginMode onSendCode={handleSendCode} /> : null} {/* {loginMode === 'phone' ? <PhoneLoginMode onSendCode={handleSendCode} /> : null} */}
{loginMode === 'email' ? <EmailLoginMode onSendCode={handleSendCode} /> : null} {/* {loginMode === 'email' ? <EmailLoginMode onSendCode={handleSendCode} /> : null} */}
<div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t"> <div className="after:border-border relative text-center text-sm after:absolute after:inset-0 after:top-1/2 after:z-0 after:flex after:items-center after:border-t">
<span className="bg-card text-muted-foreground relative z-10 px-2"> <span className="bg-card text-muted-foreground relative z-10 px-2">
@@ -105,7 +110,7 @@ export default function Login() {
<Phone /> <Phone />
</Button> </Button>
<Button variant={loginMode === 'email' ? 'default' : 'outline'} type="button" className="w-full" onClick={() => setLoginMode('email')}> <Button variant={loginMode === 'email' ? 'default' : 'outline'} type="button" className="w-full" onClick={() => setLoginMode('email')}>
<Mail /> <FileKey2 />
</Button> </Button>
</div> </div>
@@ -142,7 +147,7 @@ export default function Login() {
</div> </div>
</div > </div >
</div> </div>
</div> </div >
<Footer /> <Footer />
</> </>
) )