47 lines
3.0 KiB
TypeScript
47 lines
3.0 KiB
TypeScript
'use client'
|
|
import { useState } from 'react'
|
|
|
|
export function LoginForm() {
|
|
const [username, setUsername] = useState('')
|
|
const [password, setPassword] = useState('')
|
|
const [error, setError] = useState('')
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
async function handleSubmit(e: React.FormEvent) {
|
|
e.preventDefault(); setError(''); setLoading(true)
|
|
try {
|
|
const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) })
|
|
const data = await res.json()
|
|
if (!res.ok) { setError(data.error || '登录失败'); return }
|
|
// 直接从 URL 读取 redirect 参数,避免 Suspense/闭包导致的值捕获问题
|
|
const params = new URLSearchParams(window.location.search)
|
|
const redirect = params.get('redirect')
|
|
const dest = (redirect && redirect.startsWith('/')) ? redirect : '/dashboard'
|
|
window.location.href = dest
|
|
} catch { setError('网络错误,请重试') }
|
|
finally { setLoading(false) }
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-slate-50 dark:bg-slate-950 px-4">
|
|
<div className="w-full max-w-sm bg-white dark:bg-slate-900 rounded-lg border border-blue-200/50 dark:border-blue-500/20 shadow-lg p-8">
|
|
<h1 className="text-2xl font-bold text-center mb-6 text-slate-900 dark:text-white">资产管理系统</h1>
|
|
{error && <div className="mb-4 p-3 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-600 dark:text-red-400 text-sm">{error}</div>}
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">用户名</label>
|
|
<input type="text" value={username} onChange={e => setUsername(e.target.value)} placeholder="请输入用户名"
|
|
className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" required />
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1">密码</label>
|
|
<input type="password" value={password} onChange={e => setPassword(e.target.value)} placeholder="请输入密码"
|
|
className="w-full px-3 py-2 rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-white placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" required />
|
|
</div>
|
|
<button type="submit" disabled={loading} className="w-full py-2 px-4 rounded-lg bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-medium transition-colors duration-200">{loading ? '登录中...' : '登录'}</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|