98 lines
4.2 KiB
TypeScript
98 lines
4.2 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { BrainCircuit } from 'lucide-react';
|
|
import { cn } from '../../lib/utils';
|
|
|
|
interface LoginProps {
|
|
onLoginSuccess: (token: string) => void;
|
|
}
|
|
|
|
export function Login({ onLoginSuccess }: LoginProps) {
|
|
const [username, setUsername] = useState('admin');
|
|
const [password, setPassword] = useState('123456');
|
|
const [error, setError] = useState('');
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
const response = await fetch('/api/login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
onLoginSuccess(data.token);
|
|
} else {
|
|
const errData = await response.json();
|
|
setError(errData.error || '登录失败');
|
|
}
|
|
} catch (err) {
|
|
setError('网络异常,无法连接到后端验证');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="flex h-screen w-full items-center justify-center bg-[#0a0a0a] text-gray-200">
|
|
<div className="absolute inset-0 z-0 opacity-10 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-cyan-500 via-[#0a0a0a] to-transparent pointer-events-none"></div>
|
|
|
|
<div className="relative z-10 w-full max-w-md p-8 bg-[#111] border border-white/5 rounded-2xl shadow-2xl scale-in shadow-black/50">
|
|
<div className="flex flex-col items-center mb-8">
|
|
<div className="w-16 h-16 bg-white rounded-2xl flex items-center justify-center text-cyan-500 shadow-lg shadow-cyan-500/20 mb-4 overflow-hidden border border-white/10">
|
|
<img src="/Logo.png" alt="Logo" className="w-full h-full object-cover" />
|
|
</div>
|
|
<h1 className="text-2xl font-bold text-white tracking-wider mb-2">欢迎登录协同工作站</h1>
|
|
<p className="text-sm text-gray-500">AI 智能切分与多模态数据标注系统</p>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
<div>
|
|
<label className="block text-xs font-medium text-gray-400 uppercase tracking-widest mb-2">账号</label>
|
|
<input
|
|
type="text"
|
|
value={username}
|
|
onChange={(e) => setUsername(e.target.value)}
|
|
className="w-full bg-[#1a1a1a] border border-white/10 rounded-lg px-4 py-3 text-sm focus:outline-none focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/50 transition-all font-mono"
|
|
placeholder="输入账号"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-xs font-medium text-gray-400 uppercase tracking-widest mb-2">密码</label>
|
|
<input
|
|
type="password"
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
className="w-full bg-[#1a1a1a] border border-white/10 rounded-lg px-4 py-3 text-sm focus:outline-none focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/50 transition-all font-mono"
|
|
placeholder="输入密码"
|
|
/>
|
|
</div>
|
|
|
|
{error && <div className="text-red-400 text-sm font-medium p-3 bg-red-400/10 rounded-lg border border-red-500/20 text-center">{error}</div>}
|
|
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading}
|
|
className={cn(
|
|
"w-full py-3.5 rounded-lg flex items-center justify-center gap-2 transition-all shadow-lg font-bold tracking-wider text-sm",
|
|
isLoading ? "bg-cyan-500/50 cursor-not-allowed" : "bg-cyan-500 hover:bg-cyan-400 text-black shadow-cyan-500/20 hover:shadow-cyan-500/40"
|
|
)}
|
|
>
|
|
{isLoading ? '验证中...' : '安全登录'}
|
|
</button>
|
|
</form>
|
|
|
|
<div className="mt-8 pt-6 border-t border-white/5 text-center px-4">
|
|
<p className="text-[10px] text-gray-600">系统受准入受控环境保护。所有流转数据与标注资产将进行端到端加密与访问溯源审计。</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|