184 lines
6.4 KiB
TypeScript
184 lines
6.4 KiB
TypeScript
"use client"
|
||
|
||
import { useState } from "react"
|
||
import { useRouter } from "next/navigation"
|
||
import Link from "next/link"
|
||
import { useStore } from "@/lib/store"
|
||
import { ChevronLeft, Phone, User, Hash } from "lucide-react"
|
||
|
||
export default function LoginPage() {
|
||
const router = useRouter()
|
||
const { login, register } = useStore()
|
||
const [mode, setMode] = useState<"login" | "register">("login")
|
||
const [phone, setPhone] = useState("")
|
||
const [code, setCode] = useState("")
|
||
const [nickname, setNickname] = useState("")
|
||
const [referralCode, setReferralCode] = useState("")
|
||
const [error, setError] = useState("")
|
||
const [loading, setLoading] = useState(false)
|
||
|
||
const handleSubmit = async () => {
|
||
setError("")
|
||
setLoading(true)
|
||
|
||
try {
|
||
// 管理员登录(使用 code 作为密码,调用后台 API 并写 Cookie)
|
||
if (phone.toLowerCase() === "admin") {
|
||
try {
|
||
const res = await fetch("/api/admin", {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ username: phone, password: code }),
|
||
credentials: "include",
|
||
})
|
||
const data = await res.json()
|
||
if (res.ok && data.success) {
|
||
router.push("/admin")
|
||
return
|
||
}
|
||
} catch {
|
||
// fallthrough to error
|
||
}
|
||
setError("管理员密码错误")
|
||
return
|
||
}
|
||
|
||
if (mode === "login") {
|
||
if (!code.trim()) {
|
||
setError("请输入密码")
|
||
return
|
||
}
|
||
const success = await login(phone, code)
|
||
if (success) {
|
||
router.push("/")
|
||
} else {
|
||
setError("密码错误或用户不存在")
|
||
}
|
||
} else {
|
||
if (!nickname.trim()) {
|
||
setError("请输入昵称")
|
||
return
|
||
}
|
||
if (!code.trim()) {
|
||
setError("请设置密码(至少 6 位)")
|
||
return
|
||
}
|
||
if (code.trim().length < 6) {
|
||
setError("密码至少 6 位")
|
||
return
|
||
}
|
||
const success = await register(phone, nickname, code, referralCode || undefined)
|
||
if (success) {
|
||
router.push("/")
|
||
} else {
|
||
setError("该手机号已注册")
|
||
}
|
||
}
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-black text-white flex flex-col">
|
||
{/* 顶部导航 */}
|
||
<header className="flex items-center px-4 py-3">
|
||
<button
|
||
onClick={() => router.back()}
|
||
className="w-9 h-9 rounded-full bg-[#1c1c1e] flex items-center justify-center"
|
||
>
|
||
<ChevronLeft className="w-5 h-5 text-gray-400" />
|
||
</button>
|
||
</header>
|
||
|
||
{/* 主内容 */}
|
||
<main className="flex-1 px-6 pt-8">
|
||
<h1 className="text-2xl font-bold mb-2">{mode === "login" ? "登录" : "注册"}</h1>
|
||
<p className="text-gray-500 text-sm mb-8">
|
||
{mode === "login" ? "登录后查看购买记录和收益" : "注册后开始阅读真实商业故事"}
|
||
</p>
|
||
|
||
<div className="space-y-4">
|
||
{/* 手机号 */}
|
||
<div className="relative">
|
||
<Phone className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||
<input
|
||
type="tel"
|
||
value={phone}
|
||
onChange={(e) => setPhone(e.target.value)}
|
||
placeholder="手机号"
|
||
className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50"
|
||
/>
|
||
</div>
|
||
|
||
{/* 昵称(注册时显示) */}
|
||
{mode === "register" && (
|
||
<div className="relative">
|
||
<User className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||
<input
|
||
type="text"
|
||
value={nickname}
|
||
onChange={(e) => setNickname(e.target.value)}
|
||
placeholder="昵称"
|
||
className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50"
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{/* 密码 */}
|
||
<div className="relative">
|
||
<Hash className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||
<input
|
||
type="password"
|
||
value={code}
|
||
onChange={(e) => setCode(e.target.value)}
|
||
placeholder={mode === "login" ? "密码" : "设置密码(至少 6 位)"}
|
||
className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50"
|
||
/>
|
||
</div>
|
||
|
||
{/* 邀请码(注册时显示) */}
|
||
{mode === "register" && (
|
||
<div className="relative">
|
||
<Hash className="absolute left-4 top-1/2 -translate-y-1/2 w-5 h-5 text-gray-500" />
|
||
<input
|
||
type="text"
|
||
value={referralCode}
|
||
onChange={(e) => setReferralCode(e.target.value)}
|
||
placeholder="邀请码(选填)"
|
||
className="w-full pl-12 pr-4 py-3.5 bg-[#1c1c1e] rounded-xl text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#30d158]/50"
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{/* 错误提示 */}
|
||
{error && <p className="text-red-500 text-sm">{error}</p>}
|
||
|
||
{/* 提交按钮 */}
|
||
<button
|
||
onClick={handleSubmit}
|
||
disabled={loading || !phone}
|
||
className="w-full py-3.5 bg-[#30d158] text-white font-medium rounded-xl active:scale-[0.98] transition-transform disabled:opacity-50"
|
||
>
|
||
{loading ? "处理中..." : mode === "login" ? "登录" : "注册"}
|
||
</button>
|
||
|
||
{/* 忘记密码 / 切换模式 */}
|
||
<div className="text-center space-y-2">
|
||
{mode === "login" && (
|
||
<div>
|
||
<Link href="/login/forgot" className="text-[#30d158] text-sm">
|
||
忘记密码?
|
||
</Link>
|
||
</div>
|
||
)}
|
||
<button onClick={() => setMode(mode === "login" ? "register" : "login")} className="text-[#30d158] text-sm block mx-auto">
|
||
{mode === "login" ? "没有账号?去注册" : "已有账号?去登录"}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
)
|
||
}
|