aura-web/src/pages/LoginPage.tsx

171 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { useState } from 'react';
import { Form, Input, Button, Tabs, App as AntApp, Alert } from 'antd';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../store/auth';
export default function LoginPage() {
const [tab, setTab] = useState<'login' | 'register'>('login');
const navigate = useNavigate();
const [params] = useSearchParams();
const next = params.get('next') || '/';
const { login, register } = useAuth();
const { message } = AntApp.useApp();
const [loading, setLoading] = useState(false);
const onLogin = async (values: any) => {
setLoading(true);
try {
await login(values.email, values.password);
message.success('登录成功');
navigate(next, { replace: true });
} catch (e: any) {
message.error(e?.response?.data?.error ?? e?.message ?? '登录失败');
} finally {
setLoading(false);
}
};
const onRegister = async (values: any) => {
setLoading(true);
try {
await register({
email: values.email,
password: values.password,
name: values.name,
inviteCode: values.inviteCode || undefined
});
message.success('注册成功,已自动登录');
navigate(next, { replace: true });
} catch (e: any) {
message.error(e?.response?.data?.error ?? e?.message ?? '注册失败');
} finally {
setLoading(false);
}
};
return (
<div className="login-page">
<div className="login-deco login-deco-1" />
<div className="login-deco login-deco-2" />
<div className="login-content">
<div className="login-brand-panel">
<div className="login-brand-header">
<div className="login-brand-mark">A</div>
<span className="login-brand-name">Agent Studio</span>
</div>
<h1 className="login-title">
<br />
<span className="login-title-highlight"> AI </span>
</h1>
<p className="login-subtitle">
AI
</p>
<div className="login-features">
{[
{ icon: '✦', text: '多模型即插即用' },
{ icon: '✦', text: '知识库 + RAG 检索' },
{ icon: '✦', text: '可分享智能体' }
].map((it) => (
<div key={it.text} className="login-feature-item">
<span className="login-feature-icon">{it.icon}</span>
{it.text}
</div>
))}
</div>
</div>
<div className="login-form-panel">
<div className="login-card">
<div className="login-card-header">
<h2 className="login-card-title"></h2>
<div className="login-card-subtitle">使</div>
</div>
<Tabs
activeKey={tab}
onChange={(k) => setTab(k as any)}
items={[
{
key: 'login',
label: '登录',
children: (
<Form layout="vertical" onFinish={onLogin} className="login-form">
<Form.Item
name="email"
label="邮箱"
rules={[{ required: true, type: 'email', message: '请填写合法邮箱' }]}
>
<Input placeholder="you@example.com" size="large" autoFocus />
</Form.Item>
<Form.Item name="password" label="密码" rules={[{ required: true }]}>
<Input.Password placeholder="••••••" size="large" />
</Form.Item>
<Button
type="primary"
htmlType="submit"
loading={loading}
block
size="large"
className="login-submit-btn"
>
</Button>
</Form>
)
},
{
key: 'register',
label: '注册',
children: (
<Form layout="vertical" onFinish={onRegister} className="login-form">
<Alert
className="login-register-alert"
type="info"
showIcon
message="第一个注册的用户自动成为管理员;之后需要邀请码"
/>
<Form.Item name="email" label="邮箱" rules={[{ required: true, type: 'email' }]}>
<Input placeholder="you@example.com" size="large" />
</Form.Item>
<Form.Item name="name" label="昵称" rules={[{ required: true }]}>
<Input placeholder="张三" size="large" />
</Form.Item>
<Form.Item
name="password"
label="密码"
rules={[{ required: true, min: 6, message: '至少 6 位' }]}
>
<Input.Password placeholder="••••••" size="large" />
</Form.Item>
<Form.Item name="inviteCode" label="邀请码(可选)">
<Input placeholder="如果你是被邀请的用户" size="large" />
</Form.Item>
<Button
type="primary"
htmlType="submit"
loading={loading}
block
size="large"
className="login-submit-btn"
>
</Button>
</Form>
)
}
]}
/>
<div className="login-footer"></div>
</div>
</div>
</div>
</div>
);
}