feat: 初始化 ChatOne 管理后台前端

基于 React + Vite + Ant Design Pro 搭建登录、动态菜单、布局主题与数据总览,并补充按菜单整理的 UI 设计稿。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-25 23:57:53 +08:00
parent ed9b23ba90
commit a09dcef3f1
59 changed files with 7296 additions and 129 deletions

216
src/pages/login/index.tsx Normal file
View File

@@ -0,0 +1,216 @@
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={'密码: ant.design'}
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>
);
}