"use client" import {useEffect, useMemo, useState} from "react" import {useMutation, useQuery} from "@tanstack/react-query" import {useRouter, useSearchParams} from "next/navigation" import {UserPlus} from "lucide-react" import {toast} from "sonner" import Link from "next/link" import {useAuth} from "@/components/providers/auth-provider" import {Button} from "@/components/ui/button" import {Input} from "@/components/ui/input" import {Spinner} from "@/components/ui/spinner" import {Card, CardContent} from "@/components/ui/card" import services from "@/lib/services" import type {RegisterRequest} from "@/lib/services/auth/types" function getRedirectTarget(searchParams: ReturnType) { const callbackUrl = searchParams.get("callbackUrl") const storedRedirect = sessionStorage.getItem("redirect_after_login") return callbackUrl || storedRedirect || "/home" } function configBool(value: string | undefined, fallback: boolean) { if (value === undefined) return fallback return value === "true" } export function RegisterForm() { const router = useRouter() const searchParams = useSearchParams() const { setUser } = useAuth() const [username, setUsername] = useState("") const [password, setPassword] = useState("") const [nickname, setNickname] = useState("") const [email, setEmail] = useState("") const [code, setCode] = useState("") const [registerCooldown, setRegisterCooldown] = useState(0) const [errorMessage, setErrorMessage] = useState("") useEffect(() => { if (registerCooldown > 0) { const timer = setTimeout(() => setRegisterCooldown(registerCooldown - 1), 1000) return () => clearTimeout(timer) } }, [registerCooldown]) const publicConfigQuery = useQuery({ queryKey: ["public-config"], queryFn: () => services.config.getPublicConfig(), }) const redirectTarget = useMemo( () => getRedirectTarget(searchParams), [searchParams], ) const registrationEnabled = configBool(publicConfigQuery.data?.registration_enabled, true) && configBool(publicConfigQuery.data?.password_register_enabled, true) const emailRegisterEnabled = configBool(publicConfigQuery.data?.email_register_verification_enabled, false) // Redirect to login if registration is closed useEffect(() => { if (publicConfigQuery.isSuccess && !registrationEnabled) { toast.error("系统注册功能已关闭") router.replace("/login") } }, [publicConfigQuery.isSuccess, registrationEnabled, router]) const registerMutation = useMutation({ mutationFn: (req: RegisterRequest) => services.auth.register(req), onSuccess: (user) => { setUser(user) router.replace(redirectTarget) toast.success("注册并登录成功") }, onError: (error: Error) => { setErrorMessage(error.message || "注册失败,请重试") }, }) const sendRegisterCodeMutation = useMutation({ mutationFn: (targetEmail: string) => services.auth.sendEmailCode(targetEmail, "register"), onSuccess: () => { setRegisterCooldown(60) toast.success("验证码已发送至您的邮箱,请查收") }, onError: (error: Error) => { toast.error(error.message || "发送验证码失败,请重试") }, }) const handleSendRegisterCode = () => { const trimmedEmail = email.trim() if (!trimmedEmail) { toast.error("请先输入邮箱地址") return } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ if (!emailRegex.test(trimmedEmail)) { toast.error("请输入有效的邮箱地址") return } sendRegisterCodeMutation.mutate(trimmedEmail) } const handleRegister = () => { setErrorMessage("") if (!username.trim() || !password) { toast.error("用户名和密码不能为空") return } if (password.length < 8) { toast.error("密码长度不能少于 8 位") return } if (emailRegisterEnabled) { if (!email.trim() || !code.trim()) { toast.error("邮箱和验证码不能为空") return } } registerMutation.mutate({ username: username.trim(), password, nickname: nickname.trim() || undefined, email: email.trim() || undefined, code: code.trim() || undefined, }) } if (publicConfigQuery.isPending) { return ( ) } if (!registrationEnabled) { return null } return (

创建账号

setUsername(e.target.value)} placeholder="用户名" autoComplete="username" onKeyDown={(e) => e.key === "Enter" && handleRegister()} /> setNickname(e.target.value)} placeholder="昵称(可选)" autoComplete="nickname" onKeyDown={(e) => e.key === "Enter" && handleRegister()} /> setPassword(e.target.value)} type="password" placeholder="密码(至少 8 位)" autoComplete="new-password" onKeyDown={(e) => e.key === "Enter" && handleRegister()} /> setEmail(e.target.value)} placeholder={emailRegisterEnabled ? "电子邮箱" : "电子邮箱(可选)"} autoComplete="email" onKeyDown={(e) => e.key === "Enter" && handleRegister()} /> {emailRegisterEnabled && (
setCode(e.target.value)} placeholder="6 位邮箱验证码" maxLength={6} className="flex-1" onKeyDown={(e) => e.key === "Enter" && handleRegister()} />
)}
{errorMessage ? (
{errorMessage}
) : null}
Already have an account?{" "} Sign in
) }