补齐 LLM 子菜单路由,落地模型列表与供应商管理页面交互,同时引入功能与视觉回归用例以便后续页面持续验证。 Co-authored-by: Cursor <cursoragent@cursor.com>
217 lines
6.0 KiB
TypeScript
217 lines
6.0 KiB
TypeScript
import { LockOutlined, MobileOutlined, UserOutlined } from '@ant-design/icons';
|
||
import {
|
||
LoginFormPage,
|
||
ProConfigProvider,
|
||
ProFormCaptcha,
|
||
ProFormCheckbox,
|
||
ProFormText,
|
||
} from '@ant-design/pro-components';
|
||
import { Tabs, message, theme } from 'antd';
|
||
import { useState } from 'react';
|
||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||
|
||
import { login, getUserInfo } from '@/api/auth';
|
||
import { useUserStore } from '@/stores/user';
|
||
import type { LoginType } from '@/api/types/auth';
|
||
import type { LoginAccountParams } from '@/api/types/auth';
|
||
import logo from '@/assets/logo.png';
|
||
|
||
const LoginFrom = () => {
|
||
const [loginType, setLoginType] = useState<LoginType>('account');
|
||
const { token } = theme.useToken();
|
||
const setToken = useUserStore(s => s.setToken);
|
||
const setUserInfo = useUserStore(s => s.setUserInfo);
|
||
const navigate = useNavigate();
|
||
const [searchParams] = useSearchParams();
|
||
|
||
const onFinish = async (values: LoginAccountParams) => {
|
||
console.log(values);
|
||
if (loginType !== 'account') {
|
||
message.warning('暂不支持短信登录');
|
||
return;
|
||
}
|
||
|
||
const res = await login(values);
|
||
if (res.code !== 200) {
|
||
message.error(res.message);
|
||
return;
|
||
}
|
||
|
||
const userInfo = await getUserInfo();
|
||
if (userInfo.code !== 200) {
|
||
message.error(userInfo.message);
|
||
return;
|
||
}
|
||
|
||
setToken(res.data.token);
|
||
setUserInfo(userInfo.data);
|
||
|
||
message.success('登录成功');
|
||
const redirect = searchParams.get('redirect') || '/dashboard';
|
||
navigate(redirect, { replace: true });
|
||
};
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
backgroundColor: 'white',
|
||
height: '100vh',
|
||
}}
|
||
>
|
||
<LoginFormPage
|
||
backgroundImageUrl="https://mdn.alipayobjects.com/huamei_gcee1x/afts/img/A*y0ZTS6WLwvgAAAAAAAAAAAAADml6AQ/fmt.webp"
|
||
logo={logo}
|
||
backgroundVideoUrl="https://gw.alipayobjects.com/v/huamei_gcee1x/afts/video/jXRBRK_VAwoAAAAAAAAAAAAAK4eUAQBr"
|
||
title="ChatOne"
|
||
containerStyle={{
|
||
backgroundColor: 'rgba(0, 0, 0,0.65)',
|
||
backdropFilter: 'blur(4px)',
|
||
}}
|
||
subTitle="整合AI聊天管理平台"
|
||
onFinish={onFinish}
|
||
>
|
||
<Tabs
|
||
items={[
|
||
{ key: 'account', label: '账号密码登录' },
|
||
{ key: 'phone', label: '手机号登录' },
|
||
]}
|
||
onChange={key => setLoginType(key as LoginType)}
|
||
activeKey={loginType}
|
||
/>
|
||
{loginType === 'account' && (
|
||
<>
|
||
<ProFormText
|
||
name="username"
|
||
fieldProps={{
|
||
size: 'large',
|
||
prefix: (
|
||
<UserOutlined
|
||
style={{
|
||
color: token.colorText,
|
||
}}
|
||
className={'prefixIcon'}
|
||
/>
|
||
),
|
||
}}
|
||
placeholder={'用户名: admin or user'}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入用户名!',
|
||
},
|
||
]}
|
||
/>
|
||
<ProFormText.Password
|
||
name="password"
|
||
fieldProps={{
|
||
size: 'large',
|
||
prefix: (
|
||
<LockOutlined
|
||
style={{
|
||
color: token.colorText,
|
||
}}
|
||
className={'prefixIcon'}
|
||
/>
|
||
),
|
||
}}
|
||
placeholder={'密码: 123456'}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入密码!',
|
||
},
|
||
]}
|
||
/>
|
||
</>
|
||
)}
|
||
{loginType === 'phone' && (
|
||
<>
|
||
<ProFormText
|
||
fieldProps={{
|
||
size: 'large',
|
||
prefix: (
|
||
<MobileOutlined
|
||
style={{
|
||
color: token.colorText,
|
||
}}
|
||
className={'prefixIcon'}
|
||
/>
|
||
),
|
||
}}
|
||
name="mobile"
|
||
placeholder={'手机号'}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入手机号!',
|
||
},
|
||
{
|
||
pattern: /^1\d{10}$/,
|
||
message: '手机号格式错误!',
|
||
},
|
||
]}
|
||
/>
|
||
<ProFormCaptcha
|
||
fieldProps={{
|
||
size: 'large',
|
||
prefix: (
|
||
<LockOutlined
|
||
style={{
|
||
color: token.colorText,
|
||
}}
|
||
className={'prefixIcon'}
|
||
/>
|
||
),
|
||
}}
|
||
captchaProps={{
|
||
size: 'large',
|
||
}}
|
||
placeholder={'请输入验证码'}
|
||
captchaTextRender={(timing, count) => {
|
||
if (timing) {
|
||
return `${count} ${'获取验证码'}`;
|
||
}
|
||
return '获取验证码';
|
||
}}
|
||
name="captcha"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入验证码!',
|
||
},
|
||
]}
|
||
onGetCaptcha={async () => {
|
||
message.success('获取验证码成功!验证码为:1234');
|
||
}}
|
||
/>
|
||
</>
|
||
)}
|
||
<div
|
||
style={{
|
||
marginBlockEnd: 24,
|
||
}}
|
||
>
|
||
<ProFormCheckbox noStyle name="autoLogin">
|
||
自动登录
|
||
</ProFormCheckbox>
|
||
<a
|
||
style={{
|
||
float: 'right',
|
||
}}
|
||
>
|
||
忘记密码
|
||
</a>
|
||
</div>
|
||
</LoginFormPage>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default function Login() {
|
||
return (
|
||
<ProConfigProvider dark>
|
||
<LoginFrom />
|
||
</ProConfigProvider>
|
||
);
|
||
}
|