'use client' import { useState, type FormEvent } from 'react' import { useRouter } from 'next/navigation' import { Lock, User, Loader2, Eye, EyeOff, ShieldCheck } from 'lucide-react' import { api } from '@/lib/api-client' import { login } from '@/lib/auth' import { ApiRequestError } from '@/lib/api-client' export default function LoginPage() { const router = useRouter() const [username, setUsername] = useState('') const [password, setPassword] = useState('') const [totpCode, setTotpCode] = useState('') const [showPassword, setShowPassword] = useState(false) const [needTotp, setNeedTotp] = useState(false) const [remember, setRemember] = useState(false) const [loading, setLoading] = useState(false) const [error, setError] = useState('') async function handleSubmit(e: FormEvent) { e.preventDefault() setError('') if (!username.trim()) { setError('请输入用户名') return } if (!password.trim()) { setError('请输入密码') return } setLoading(true) try { const res = await api.auth.login({ username: username.trim(), password, totp_code: totpCode.trim() || undefined, }) login(res.token, res.account) router.replace('/') } catch (err) { if (err instanceof ApiRequestError) { const msg = err.body.message || '' // 后端返回 "需要 TOTP" 时显示 TOTP 输入框 if (msg.includes('TOTP') || msg.includes('totp') || msg.includes('2FA') || msg.includes('验证码') || err.status === 403) { setNeedTotp(true) setError(msg || '请输入两步验证码') } else { setError(msg || '登录失败,请检查用户名和密码') } } else { setError('网络错误,请稍后重试') } } finally { setLoading(false) } } return (
{/* 左侧品牌区域 */}
{/* 装饰性背景 */}
{/* 品牌内容 */}

ZCLAW

AI Agent 管理平台

统一管理 AI 服务商、模型配置、API 密钥、用量监控与系统配置

{/* 右侧登录表单 */}
{/* 移动端 Logo */}

ZCLAW

AI Agent 管理平台

登录

输入您的账号信息以继续

{/* 用户名 */}
setUsername(e.target.value)} className="flex h-10 w-full rounded-md border border-input bg-transparent pl-10 pr-3 py-2 text-sm shadow-sm transition-colors duration-200 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" autoComplete="username" />
{/* 密码 */}
setPassword(e.target.value)} className="flex h-10 w-full rounded-md border border-input bg-transparent pl-10 pr-10 py-2 text-sm shadow-sm transition-colors duration-200 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" autoComplete="current-password" />
{/* TOTP 验证码 */} {needTotp && (
setTotpCode(e.target.value.replace(/\D/g, '').slice(0, 6))} maxLength={6} className="flex h-10 w-full rounded-md border border-input bg-transparent pl-10 pr-3 py-2 text-sm shadow-sm transition-colors duration-200 placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring tracking-widest" autoComplete="one-time-code" inputMode="numeric" />

请使用身份验证器 App(如 Google Authenticator)扫描二维码后生成的验证码

)} {/* 记住我 */}
setRemember(e.target.checked)} className="h-4 w-4 rounded border-input bg-transparent accent-primary cursor-pointer" />
{/* 错误信息 */} {error && (
{error}
)} {/* 登录按钮 */}
) }