|
|
|
@@ -1,187 +1,48 @@ |
|
|
|
import { clearSessionToken, setSessionToken } from '@/access'; |
|
|
|
import { getFakeCaptcha } from '@/services/ant-design-pro/login'; |
|
|
|
import { getCaptchaImg, login } from '@/services/system/auth'; |
|
|
|
import { |
|
|
|
AlipayCircleOutlined, |
|
|
|
LockOutlined, |
|
|
|
MobileOutlined, |
|
|
|
TaobaoCircleOutlined, |
|
|
|
UserOutlined, |
|
|
|
WeiboCircleOutlined, |
|
|
|
} from '@ant-design/icons'; |
|
|
|
import { |
|
|
|
LoginForm, |
|
|
|
ProFormCaptcha, |
|
|
|
ProFormCheckbox, |
|
|
|
ProFormText, |
|
|
|
} from '@ant-design/pro-components'; |
|
|
|
import { useEmotionCss } from '@ant-design/use-emotion-css'; |
|
|
|
import { FormattedMessage, SelectLang, history, useIntl, useModel } from '@umijs/max'; |
|
|
|
import { Alert, Col, Image, Row, message } from 'antd'; |
|
|
|
import { loginPasswordKey, loginUserKey, rememberPasswordKey } from '@/utils/localStorage'; |
|
|
|
import { to } from '@/utils/promise'; |
|
|
|
import { history, useModel } from '@umijs/max'; |
|
|
|
import { Button, Checkbox, Flex, Form, Image, Input, message } from 'antd'; |
|
|
|
import React, { useEffect, useState } from 'react'; |
|
|
|
import { flushSync } from 'react-dom'; |
|
|
|
import styles from './login.less'; |
|
|
|
|
|
|
|
const ActionIcons = () => { |
|
|
|
const langClassName = useEmotionCss(({ token }) => { |
|
|
|
return { |
|
|
|
marginLeft: '8px', |
|
|
|
color: 'rgba(0, 0, 0, 0.2)', |
|
|
|
fontSize: '24px', |
|
|
|
verticalAlign: 'middle', |
|
|
|
cursor: 'pointer', |
|
|
|
transition: 'color 0.3s', |
|
|
|
'&:hover': { |
|
|
|
color: token.colorPrimaryActive, |
|
|
|
}, |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<AlipayCircleOutlined key="AlipayCircleOutlined" className={langClassName} /> |
|
|
|
<TaobaoCircleOutlined key="TaobaoCircleOutlined" className={langClassName} /> |
|
|
|
<WeiboCircleOutlined key="WeiboCircleOutlined" className={langClassName} /> |
|
|
|
</> |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
const Lang = () => { |
|
|
|
const langClassName = useEmotionCss(({ token }) => { |
|
|
|
return { |
|
|
|
width: 42, |
|
|
|
height: 42, |
|
|
|
lineHeight: '42px', |
|
|
|
position: 'fixed', |
|
|
|
right: 16, |
|
|
|
borderRadius: token.borderRadius, |
|
|
|
':hover': { |
|
|
|
backgroundColor: token.colorBgTextHover, |
|
|
|
}, |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
const LoginInputPrefix = ({ icon }: { icon: string }) => { |
|
|
|
return ( |
|
|
|
<div className={langClassName} data-lang> |
|
|
|
{SelectLang && <SelectLang />} |
|
|
|
<div className={styles['login-input-prefix']}> |
|
|
|
<img className={styles['login-input-prefix__icon']} src={icon} alt="" /> |
|
|
|
<div className={styles['login-input-prefix__line']}></div> |
|
|
|
</div> |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
const LoginMessage: React.FC<{ |
|
|
|
content: string; |
|
|
|
}> = ({ content }) => { |
|
|
|
return ( |
|
|
|
<Alert |
|
|
|
style={{ |
|
|
|
marginBottom: 24, |
|
|
|
}} |
|
|
|
message={content} |
|
|
|
type="error" |
|
|
|
showIcon |
|
|
|
/> |
|
|
|
); |
|
|
|
}; |
|
|
|
|
|
|
|
const Login: React.FC = () => { |
|
|
|
const [userLoginState, setUserLoginState] = useState<API.LoginResult>({ code: 200 }); |
|
|
|
const [type, setType] = useState<string>('account'); |
|
|
|
const { initialState, setInitialState } = useModel('@@initialState'); |
|
|
|
const [captchaCode, setCaptchaCode] = useState<string>(''); |
|
|
|
const [uuid, setUuid] = useState<string>(''); |
|
|
|
const [form] = Form.useForm(); |
|
|
|
const [usernameReadOnly, setUsernameReadOnly] = useState<boolean>(true); |
|
|
|
|
|
|
|
const containerClassName = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
display: 'flex', |
|
|
|
height: '100vh', |
|
|
|
backgroundColor: '#fff', |
|
|
|
backgroundSize: '100% 100%', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const containerLeftBox = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
background: 'linear-gradient(180deg,#e2ecff 0%,#f6fafe 100%)', |
|
|
|
width: '43%', |
|
|
|
height: '100%', |
|
|
|
position: 'relative', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const leftTopBoX = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
display: 'flex', |
|
|
|
position: 'absolute', |
|
|
|
top: '55px', |
|
|
|
left: '60px', |
|
|
|
fontWeight: '500', |
|
|
|
color: '#1d1d20', |
|
|
|
fontSize: '36px', |
|
|
|
fontFamily: 'Alibaba', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const centerTitleBoX = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
display: 'flex', |
|
|
|
position: 'absolute', |
|
|
|
top: '163px', |
|
|
|
left: '50%', |
|
|
|
transform: 'translateX(-50%)', |
|
|
|
fontWeight: '500', |
|
|
|
color: '#111111', |
|
|
|
fontSize: '45px', |
|
|
|
fontFamily: 'Alibaba', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const centerMessage = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
display: 'flex', |
|
|
|
position: 'absolute', |
|
|
|
top: '242px', |
|
|
|
left: '50%', |
|
|
|
transform: 'translateX(-50%)', |
|
|
|
fontWeight: '500', |
|
|
|
color: '#606b7a', |
|
|
|
fontSize: '26px', |
|
|
|
fontFamily: 'Alibaba', |
|
|
|
letterSpacing: '8px', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const containerRightBox = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
background: '#fff', |
|
|
|
width: '57%', |
|
|
|
height: '100%', |
|
|
|
position: 'relative', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const rightTopTitle = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
display: 'flex', |
|
|
|
position: 'absolute', |
|
|
|
alignItems: 'center', |
|
|
|
top: '147px', |
|
|
|
left: '230px', |
|
|
|
fontFamily: 'Alibaba', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const containerLoginForm = useEmotionCss(() => { |
|
|
|
return { |
|
|
|
width: '640px', |
|
|
|
position: 'absolute', |
|
|
|
background: '#ffffff', |
|
|
|
borderRadius: '10px', |
|
|
|
boxShadow: '0px 3px 20px rgba(153, 153, 153, 0.16)', |
|
|
|
padding: '62px 36px 45px 36px', |
|
|
|
left: '230px', |
|
|
|
top: '220px', |
|
|
|
}; |
|
|
|
}); |
|
|
|
const intl = useIntl(); |
|
|
|
useEffect(() => { |
|
|
|
getCaptchaCode(); |
|
|
|
const autoLogin = localStorage.getItem(rememberPasswordKey) ?? 'false'; |
|
|
|
if (autoLogin === 'true') { |
|
|
|
const username = localStorage.getItem(loginUserKey); |
|
|
|
const password = localStorage.getItem(loginPasswordKey); |
|
|
|
form.setFieldsValue({ username: username, password: password, autoLogin: true }); |
|
|
|
} else { |
|
|
|
form.setFieldsValue({ username: '', password: '', autoLogin: false }); |
|
|
|
} |
|
|
|
}, []); |
|
|
|
|
|
|
|
const getCaptchaCode = async () => { |
|
|
|
const response = await getCaptchaImg(); |
|
|
|
const imgdata = `data:image/png;base64,${response.img}`; |
|
|
|
setCaptchaCode(imgdata); |
|
|
|
setUuid(response.uuid); |
|
|
|
const [res] = await to(getCaptchaImg()); |
|
|
|
if (res) { |
|
|
|
const imgdata = `data:image/png;base64,${res.img}`; |
|
|
|
setCaptchaCode(imgdata); |
|
|
|
setUuid(res.uuid); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
const fetchUserInfo = async () => { |
|
|
|
@@ -196,302 +57,134 @@ const Login: React.FC = () => { |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
// 登录 |
|
|
|
const handleSubmit = async (values: API.LoginParams) => { |
|
|
|
try { |
|
|
|
// 登录 |
|
|
|
const response = await login({ ...values, uuid }); |
|
|
|
if (response.code === 200) { |
|
|
|
const defaultLoginSuccessMessage = intl.formatMessage({ |
|
|
|
id: 'pages.login.success', |
|
|
|
defaultMessage: '登录成功!', |
|
|
|
}); |
|
|
|
const current = new Date(); |
|
|
|
const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60); |
|
|
|
setSessionToken(response.data?.access_token, response.data?.access_token, expireTime); |
|
|
|
message.success(defaultLoginSuccessMessage); |
|
|
|
await fetchUserInfo(); |
|
|
|
const urlParams = new URL(window.location.href).searchParams; |
|
|
|
history.push(urlParams.get('redirect') || '/'); |
|
|
|
return; |
|
|
|
const [response] = await to(login({ ...values, uuid })); |
|
|
|
if (response && response.data) { |
|
|
|
const current = new Date(); |
|
|
|
const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60); |
|
|
|
const { access_token } = response.data; |
|
|
|
setSessionToken(access_token, access_token, expireTime); |
|
|
|
message.success('登录成功!'); |
|
|
|
|
|
|
|
localStorage.setItem(rememberPasswordKey, values.autoLogin ? 'true' : 'false'); |
|
|
|
if (values.autoLogin) { |
|
|
|
localStorage.setItem(loginUserKey, values.username ?? ''); |
|
|
|
localStorage.setItem(loginPasswordKey, values.password ?? ''); |
|
|
|
} else { |
|
|
|
clearSessionToken(); |
|
|
|
// 如果失败去设置用户错误信息 |
|
|
|
setUserLoginState({ ...response, type }); |
|
|
|
getCaptchaCode(); |
|
|
|
localStorage.removeItem(loginUserKey); |
|
|
|
localStorage.removeItem(loginPasswordKey); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
|
|
|
|
await fetchUserInfo(); |
|
|
|
const urlParams = new URL(window.location.href).searchParams; |
|
|
|
history.push(urlParams.get('redirect') || '/'); |
|
|
|
} else { |
|
|
|
clearSessionToken(); |
|
|
|
getCaptchaCode(); |
|
|
|
} |
|
|
|
}; |
|
|
|
const { code } = userLoginState; |
|
|
|
const loginType = type; |
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
getCaptchaCode(); |
|
|
|
}, []); |
|
|
|
|
|
|
|
return ( |
|
|
|
<div className={containerClassName}> |
|
|
|
<div className={containerLeftBox}> |
|
|
|
<div className={leftTopBoX}> |
|
|
|
<div className={styles['user-login']}> |
|
|
|
<div className={styles['user-login__left']}> |
|
|
|
<div className={styles['user-login__left__top']}> |
|
|
|
<img |
|
|
|
src="/assets/images/left-top-logo.png" |
|
|
|
style={{ height: '42px', marginRight: '10px' }} |
|
|
|
src={require('@/assets/img/logo.png')} |
|
|
|
style={{ width: '32px', marginRight: '12px' }} |
|
|
|
alt="" |
|
|
|
/> |
|
|
|
智能材料科研平台 |
|
|
|
</div> |
|
|
|
<div className={centerTitleBoX}> |
|
|
|
<span style={{ whiteSpace: 'nowrap' }}>智能材料科研平台</span> |
|
|
|
|
|
|
|
<div className={styles['user-login__left__title']}> |
|
|
|
<span>智能材料科研平台</span> |
|
|
|
<img |
|
|
|
src="/assets/images/ai-logo.png" |
|
|
|
style={{ height: '47px', marginTop: '-10px' }} |
|
|
|
src={require('@/assets/img/login-ai-logo.png')} |
|
|
|
className={styles['user-login__left__title__img']} |
|
|
|
alt="" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
<div className={centerMessage}> |
|
|
|
<span style={{ whiteSpace: 'nowrap' }}>大语言模型运维 统一管理平台</span> |
|
|
|
<div className={styles['user-login__left__message']}> |
|
|
|
<span>大语言模型运维 统一管理平台</span> |
|
|
|
</div> |
|
|
|
<img |
|
|
|
src="/assets/images/left-back-logo.png" |
|
|
|
style={{ |
|
|
|
width: '90%', |
|
|
|
position: 'absolute', |
|
|
|
top: '326px', |
|
|
|
left: '50%', |
|
|
|
transform: 'translateX(-50%)', |
|
|
|
}} |
|
|
|
className={styles['user-login__left__bottom-img']} |
|
|
|
src={require('@/assets/img/login-left-image.png')} |
|
|
|
alt="" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
<div className={containerRightBox}> |
|
|
|
<div className={rightTopTitle}> |
|
|
|
<span style={{ color: '#111111', fontSize: '36px' }}>hello~</span> |
|
|
|
<span style={{ color: '#606b7a', fontSize: '32px', marginLeft: '10px' }}>欢迎登陆</span> |
|
|
|
<span style={{ color: '#1664ff', fontSize: '32px' }}>智能材料科研平台</span> |
|
|
|
</div> |
|
|
|
<div className={containerLoginForm}> |
|
|
|
<div |
|
|
|
style={{ |
|
|
|
color: '#1d1d20', |
|
|
|
fontSize: '22px', |
|
|
|
marginLeft: '30px', |
|
|
|
fontFamily: 'Alibaba', |
|
|
|
}} |
|
|
|
> |
|
|
|
账号登录 |
|
|
|
<div className={styles['user-login__right']}> |
|
|
|
<div> |
|
|
|
<div className={styles['user-login__right__title']}> |
|
|
|
<span style={{ color: '#111111' }}>欢迎登录</span> |
|
|
|
<span>智能材料科研平台</span> |
|
|
|
</div> |
|
|
|
<div className={styles.loginForm}> |
|
|
|
<LoginForm |
|
|
|
title="" |
|
|
|
initialValues={{ |
|
|
|
autoLogin: true, |
|
|
|
}} |
|
|
|
// actions={[ |
|
|
|
// <FormattedMessage |
|
|
|
// key="loginWith" |
|
|
|
// id="pages.login.loginWith" |
|
|
|
// defaultMessage="其他登录方式" |
|
|
|
// />, |
|
|
|
// <ActionIcons key="icons" />, |
|
|
|
// ]} |
|
|
|
onFinish={async (values) => { |
|
|
|
await handleSubmit(values as API.LoginParams); |
|
|
|
}} |
|
|
|
> |
|
|
|
{code !== 200 && loginType === 'account' && ( |
|
|
|
<LoginMessage |
|
|
|
content={intl.formatMessage({ |
|
|
|
id: 'pages.login.accountLogin.errorMessage', |
|
|
|
defaultMessage: '账户或密码错误(admin/admin123)', |
|
|
|
})} |
|
|
|
/> |
|
|
|
)} |
|
|
|
{type === 'account' && ( |
|
|
|
<> |
|
|
|
<ProFormText |
|
|
|
name="username" |
|
|
|
initialValue="admin" |
|
|
|
fieldProps={{ |
|
|
|
size: 'large', |
|
|
|
prefix: <UserOutlined />, |
|
|
|
}} |
|
|
|
placeholder={intl.formatMessage({ |
|
|
|
id: 'pages.login.username.placeholder', |
|
|
|
defaultMessage: '用户名: admin', |
|
|
|
})} |
|
|
|
rules={[ |
|
|
|
{ |
|
|
|
required: true, |
|
|
|
message: ( |
|
|
|
<FormattedMessage |
|
|
|
id="pages.login.username.required" |
|
|
|
defaultMessage="请输入用户名!" |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
]} |
|
|
|
/> |
|
|
|
<ProFormText.Password |
|
|
|
name="password" |
|
|
|
initialValue="admin123" |
|
|
|
fieldProps={{ |
|
|
|
size: 'large', |
|
|
|
prefix: <LockOutlined />, |
|
|
|
}} |
|
|
|
placeholder={intl.formatMessage({ |
|
|
|
id: 'pages.login.password.placeholder', |
|
|
|
defaultMessage: '请输入密码', |
|
|
|
})} |
|
|
|
rules={[ |
|
|
|
{ |
|
|
|
required: true, |
|
|
|
message: ( |
|
|
|
<FormattedMessage |
|
|
|
id="pages.login.password.required" |
|
|
|
defaultMessage="请输入密码!" |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
]} |
|
|
|
<div className={styles['user-login__right__content']}> |
|
|
|
<div className={styles['user-login__right__content__title']}>账号登录</div> |
|
|
|
<div className={styles['user-login__right__content__form']}> |
|
|
|
<Form |
|
|
|
labelCol={{ span: 0 }} |
|
|
|
wrapperCol={{ span: 24 }} |
|
|
|
initialValues={{ autoLogin: true }} |
|
|
|
onFinish={handleSubmit} |
|
|
|
autoComplete="off" |
|
|
|
form={form} |
|
|
|
> |
|
|
|
<Form.Item name="username" rules={[{ required: true, message: '请输入用户名' }]}> |
|
|
|
<Input |
|
|
|
placeholder="请输入用户名" |
|
|
|
prefix={<LoginInputPrefix icon={require('@/assets/img/login-user.png')} />} |
|
|
|
allowClear |
|
|
|
readOnly={usernameReadOnly} |
|
|
|
onFocus={() => setUsernameReadOnly(false)} |
|
|
|
/> |
|
|
|
<Row> |
|
|
|
<Col flex={4}> |
|
|
|
<ProFormText |
|
|
|
style={{ |
|
|
|
float: 'right', |
|
|
|
}} |
|
|
|
name="code" |
|
|
|
placeholder={intl.formatMessage({ |
|
|
|
id: 'pages.login.captcha.placeholder', |
|
|
|
defaultMessage: '请输入验证', |
|
|
|
})} |
|
|
|
rules={[ |
|
|
|
{ |
|
|
|
required: true, |
|
|
|
message: ( |
|
|
|
<FormattedMessage |
|
|
|
id="pages.login.captcha.placeholder" |
|
|
|
defaultMessage="请输入验证啊" |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
]} |
|
|
|
/> |
|
|
|
</Col> |
|
|
|
<Col> |
|
|
|
<Image |
|
|
|
src={captchaCode} |
|
|
|
alt="验证码" |
|
|
|
style={{ |
|
|
|
display: 'inline-block', |
|
|
|
verticalAlign: 'top', |
|
|
|
cursor: 'pointer', |
|
|
|
paddingLeft: '22px', |
|
|
|
width: '170px', |
|
|
|
height: '66px', |
|
|
|
}} |
|
|
|
preview={false} |
|
|
|
onClick={() => getCaptchaCode()} |
|
|
|
/> |
|
|
|
</Col> |
|
|
|
</Row> |
|
|
|
</> |
|
|
|
)} |
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
{code !== 200 && loginType === 'mobile' && <LoginMessage content="验证码错误" />} |
|
|
|
{type === 'mobile' && ( |
|
|
|
<> |
|
|
|
<ProFormText |
|
|
|
fieldProps={{ |
|
|
|
size: 'large', |
|
|
|
prefix: <MobileOutlined />, |
|
|
|
}} |
|
|
|
name="mobile" |
|
|
|
placeholder={intl.formatMessage({ |
|
|
|
id: 'pages.login.phoneNumber.placeholder', |
|
|
|
defaultMessage: '手机号', |
|
|
|
})} |
|
|
|
rules={[ |
|
|
|
{ |
|
|
|
required: true, |
|
|
|
message: ( |
|
|
|
<FormattedMessage |
|
|
|
id="pages.login.phoneNumber.required" |
|
|
|
defaultMessage="请输入手机号!" |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
{ |
|
|
|
pattern: /^1\d{10}$/, |
|
|
|
message: ( |
|
|
|
<FormattedMessage |
|
|
|
id="pages.login.phoneNumber.invalid" |
|
|
|
defaultMessage="手机号格式错误!" |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
]} |
|
|
|
<Form.Item name="password" rules={[{ required: true, message: '请输入密码' }]}> |
|
|
|
<Input.Password |
|
|
|
placeholder="请输入密码" |
|
|
|
prefix={<LoginInputPrefix icon={require('@/assets/img/login-password.png')} />} |
|
|
|
allowClear |
|
|
|
/> |
|
|
|
<ProFormCaptcha |
|
|
|
fieldProps={{ |
|
|
|
size: 'large', |
|
|
|
prefix: <LockOutlined />, |
|
|
|
}} |
|
|
|
captchaProps={{ |
|
|
|
size: 'large', |
|
|
|
}} |
|
|
|
placeholder={intl.formatMessage({ |
|
|
|
id: 'pages.login.captcha.placeholder', |
|
|
|
defaultMessage: '请输入验证码', |
|
|
|
})} |
|
|
|
captchaTextRender={(timing, count) => { |
|
|
|
if (timing) { |
|
|
|
return `${count} ${intl.formatMessage({ |
|
|
|
id: 'pages.getCaptchaSecondText', |
|
|
|
defaultMessage: '获取验证码', |
|
|
|
})}`; |
|
|
|
} |
|
|
|
return intl.formatMessage({ |
|
|
|
id: 'pages.login.phoneLogin.getVerificationCode', |
|
|
|
defaultMessage: '获取验证码', |
|
|
|
}); |
|
|
|
}} |
|
|
|
name="captcha" |
|
|
|
rules={[ |
|
|
|
{ |
|
|
|
required: true, |
|
|
|
message: ( |
|
|
|
<FormattedMessage |
|
|
|
id="pages.login.captcha.required" |
|
|
|
defaultMessage="请输入验证码!" |
|
|
|
/> |
|
|
|
), |
|
|
|
}, |
|
|
|
]} |
|
|
|
onGetCaptcha={async (phone) => { |
|
|
|
const result = await getFakeCaptcha({ |
|
|
|
phone, |
|
|
|
}); |
|
|
|
if (!result) { |
|
|
|
return; |
|
|
|
} |
|
|
|
message.success('获取验证码成功!验证码为:1234'); |
|
|
|
}} |
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
<Flex align="start" style={{ height: '98px' }}> |
|
|
|
<div style={{ flex: 1 }}> |
|
|
|
<Form.Item name="code" rules={[{ required: true, message: '请输入验证码' }]}> |
|
|
|
<Input |
|
|
|
placeholder="请输入验证码" |
|
|
|
prefix={ |
|
|
|
<LoginInputPrefix icon={require('@/assets/img/login-captcha.png')} /> |
|
|
|
} |
|
|
|
allowClear |
|
|
|
/> |
|
|
|
</Form.Item> |
|
|
|
</div> |
|
|
|
<Image |
|
|
|
className={styles['user-login__right__content__form__captcha']} |
|
|
|
src={captchaCode} |
|
|
|
alt="验证码" |
|
|
|
preview={false} |
|
|
|
onClick={() => getCaptchaCode()} |
|
|
|
/> |
|
|
|
</> |
|
|
|
)} |
|
|
|
<div |
|
|
|
style={{ |
|
|
|
marginBottom: 24, |
|
|
|
}} |
|
|
|
> |
|
|
|
<ProFormCheckbox noStyle name="autoLogin"> |
|
|
|
<FormattedMessage id="pages.login.rememberMe" defaultMessage="记住密码" /> |
|
|
|
</ProFormCheckbox> |
|
|
|
</div> |
|
|
|
</LoginForm> |
|
|
|
</Flex> |
|
|
|
|
|
|
|
<Form.Item |
|
|
|
name="autoLogin" |
|
|
|
valuePropName="checked" |
|
|
|
labelCol={{ span: 0 }} |
|
|
|
wrapperCol={{ span: 16 }} |
|
|
|
> |
|
|
|
<Checkbox>记住密码</Checkbox> |
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
<Form.Item labelCol={{ span: 0 }} wrapperCol={{ span: 24 }}> |
|
|
|
<Button type="primary" htmlType="submit"> |
|
|
|
登录 |
|
|
|
</Button> |
|
|
|
</Form.Item> |
|
|
|
</Form> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|