Files
jiang13-bbs/frontend/app/register/page.tsx
freefire dd7bd010b8 feat(admin): 完善站点设置、内容回收站与后台菜单分组
新增站点设置与内容管理(含已删除恢复/清除),用户页改用独有运营指标,并按概览/内容/成员与经济/站点重组侧栏。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-16 05:56:27 +08:00

151 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { UserPlus } from "lucide-react";
import { apiRegister, apiMe, apiGetSettings } from "@/lib/api";
import { hasAuthCookieHint } from "@/lib/cookies";
export default function RegisterPage() {
const router = useRouter();
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const [registerClosed, setRegisterClosed] = useState(false);
// 已登录用户不必再注册,直接回首页
useEffect(() => {
if (!hasAuthCookieHint()) return;
apiMe()
.then((res) => {
if (res.user) router.replace("/");
})
.catch(() => {});
}, [router]);
useEffect(() => {
apiGetSettings()
.then((s) => {
if (!s.allow_register) setRegisterClosed(true);
})
.catch(() => {});
}, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError("");
setLoading(true);
try {
const res = await apiRegister(username, email, password);
if (res.user) {
// 后端注册即签发登录态 cookie:直接进入社区,无需再手动登录
router.push("/");
router.refresh();
} else if (res.id) {
// 极端情况:注册成功但自动签发失败,引导去登录
router.push("/login");
} else {
const msg = res.error || "注册失败";
if (msg.includes("关闭注册")) setRegisterClosed(true);
setError(msg);
}
} finally {
setLoading(false);
}
};
if (registerClosed) {
return (
<div className="max-w-md mx-auto pt-8 sm:pt-14">
<div className="panel p-8 text-center">
<h1 className="text-lg font-extrabold" style={{ color: "var(--ink)" }}>
暂不开放注册
</h1>
<p className="meta mt-2 text-[13px]">站点当前已关闭新用户注册,已有账号可直接登录。</p>
<Link href="/login" className="btn btn-primary mt-6">
去登录
</Link>
</div>
</div>
);
}
return (
<div className="max-w-md mx-auto pt-8 sm:pt-14">
<div className="mb-7 text-center">
<span
className="inline-flex w-14 h-14 rounded-2xl items-center justify-center mb-4"
style={{ background: "var(--accent-soft)", color: "var(--accent)" }}
>
<UserPlus size={26} />
</span>
<h1 className="text-[28px] font-extrabold tracking-tight" style={{ color: "var(--ink)" }}>
创建账号
</h1>
<p className="text-sm mt-2" style={{ color: "var(--ink-2)" }}>
加入社区,开始你的第一帖
</p>
</div>
<form onSubmit={handleSubmit} className="panel p-6 sm:p-7 space-y-4">
{error && <div className="alert-error" role="alert">{error}</div>}
<div>
<label className="field-label" htmlFor="reg-username">用户名</label>
<input
id="reg-username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
required
minLength={3}
maxLength={32}
autoFocus
autoComplete="username"
className="field"
placeholder="3–32 位"
/>
</div>
<div>
<label className="field-label" htmlFor="reg-email">邮箱(可选)</label>
<input
id="reg-email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
autoComplete="email"
className="field"
placeholder="you@example.com"
/>
</div>
<div>
<label className="field-label" htmlFor="reg-password">密码</label>
<input
id="reg-password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
minLength={6}
autoComplete="new-password"
className="field"
placeholder="至少 6 位"
/>
</div>
<button type="submit" disabled={loading} className="btn btn-primary w-full justify-center mt-2">
{loading ? "注册中..." : "注册"}
</button>
</form>
<p className="meta text-center mt-5">
已有账号?
<Link href="/login" className="ml-1 font-semibold" style={{ color: "var(--accent)" }}>
去登录
</Link>
</p>
</div>
);
}