171 lines
6.3 KiB
TypeScript
171 lines
6.3 KiB
TypeScript
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>
|
||
);
|
||
}
|