- 新增事件总线式toast通知系统 - 新增头像、阅读进度、404/500错误页等基础组件 - 重构页脚、布局、分页、点赞按钮等现有组件 - 新增用户个人页、通知中心、帖子编辑/发布/置顶/推荐管理功能 - 优化登录注册页面样式与交互 - 修复路径类型文件引用,更新站点元信息与字体配置
91 lines
2.8 KiB
TypeScript
91 lines
2.8 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { useRouter, useSearchParams } from "next/navigation";
|
||
import Link from "next/link";
|
||
import { MessagesSquare } from "lucide-react";
|
||
import { apiLogin } from "@/lib/api";
|
||
|
||
export default function LoginPage() {
|
||
const router = useRouter();
|
||
const searchParams = useSearchParams();
|
||
const redirect = searchParams.get("redirect") || "/";
|
||
const [username, setUsername] = useState("");
|
||
const [password, setPassword] = useState("");
|
||
const [error, setError] = useState("");
|
||
const [loading, setLoading] = useState(false);
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
setError("");
|
||
setLoading(true);
|
||
try {
|
||
const res = await apiLogin(username, password);
|
||
// 登录成功后后端通过 Set-Cookie 写入 HttpOnly JWT,前端无需存储 token
|
||
if (res.user) {
|
||
router.push(redirect);
|
||
} else {
|
||
setError(res.error || "登录失败");
|
||
}
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
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)" }}
|
||
>
|
||
<MessagesSquare 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">{error}</div>}
|
||
<div>
|
||
<label className="field-label">用户名</label>
|
||
<input
|
||
type="text"
|
||
value={username}
|
||
onChange={(e) => setUsername(e.target.value)}
|
||
required
|
||
autoFocus
|
||
className="field"
|
||
placeholder="testuser"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="field-label">密码</label>
|
||
<input
|
||
type="password"
|
||
value={password}
|
||
onChange={(e) => setPassword(e.target.value)}
|
||
required
|
||
className="field"
|
||
placeholder="••••••"
|
||
/>
|
||
</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="/register" className="ml-1 font-semibold" style={{ color: "var(--accent)" }}>
|
||
立即注册
|
||
</Link>
|
||
</p>
|
||
</div>
|
||
);
|
||
}
|