232 lines
6.4 KiB
TypeScript
232 lines
6.4 KiB
TypeScript
import Settings from '@/../config/defaultSettings';
|
|
import oidcLogo from '@/assets/oidc-logo.svg';
|
|
import { PageFooter } from '@/components';
|
|
import { login } from '@/services/api';
|
|
import { API } from '@/services/typings';
|
|
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
|
import { LoginForm, ProFormCheckbox, ProFormText } from '@ant-design/pro-components';
|
|
import { Helmet, history, useModel } from '@umijs/max';
|
|
import { Alert, Button, Tabs, message } from 'antd';
|
|
import { createStyles } from 'antd-style';
|
|
import React, { CSSProperties, useState } from 'react';
|
|
import { flushSync } from 'react-dom';
|
|
|
|
const iconStyles: CSSProperties = {
|
|
transform: 'translateY(-12%)',
|
|
};
|
|
|
|
const useStyles = createStyles(({ token }) => {
|
|
return {
|
|
action: {
|
|
marginLeft: '8px',
|
|
color: 'rgba(0, 0, 0, 0.2)',
|
|
fontSize: '24px',
|
|
verticalAlign: 'middle',
|
|
cursor: 'pointer',
|
|
transition: 'color 0.3s',
|
|
'&:hover': {
|
|
color: token.colorPrimaryActive,
|
|
},
|
|
},
|
|
lang: {
|
|
width: 42,
|
|
height: 42,
|
|
lineHeight: '42px',
|
|
position: 'fixed',
|
|
right: 16,
|
|
borderRadius: token.borderRadius,
|
|
':hover': {
|
|
backgroundColor: token.colorBgTextHover,
|
|
},
|
|
},
|
|
container: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
height: '100vh',
|
|
overflow: 'auto',
|
|
backgroundImage:
|
|
"url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
|
|
backgroundSize: '100% 100%',
|
|
},
|
|
};
|
|
});
|
|
const LoginMessage: React.FC<{
|
|
content: string;
|
|
}> = ({ content }) => {
|
|
return (
|
|
<Alert
|
|
style={{
|
|
marginBottom: 24,
|
|
}}
|
|
message={content}
|
|
type="error"
|
|
showIcon
|
|
/>
|
|
);
|
|
};
|
|
|
|
const randomString = (length: number) => {
|
|
const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
const maxPos = chars.length;
|
|
let randomStr = '';
|
|
for (let i = 0; i < length; i++) {
|
|
randomStr += chars.charAt(Math.floor(Math.random() * maxPos));
|
|
}
|
|
return randomStr;
|
|
};
|
|
|
|
const generateLink = () => {
|
|
const preLink =
|
|
'https://auth.zjueva.net/connect/authorize?response_type=code&client_id=ETA&redirect_uri=https://xms.zjueva.net/api/external_sign&scope=openid&state=';
|
|
return preLink + randomString(30);
|
|
};
|
|
|
|
const Login: React.FC = () => {
|
|
const [userLoginState, setUserLoginState] = useState<API.LoginResult>({});
|
|
const [type, setType] = useState<string>('account');
|
|
const { initialState, setInitialState } = useModel('@@initialState');
|
|
const { styles } = useStyles();
|
|
const fetchUserInfo = async () => {
|
|
const userInfo = await initialState?.fetchUserInfo?.();
|
|
if (userInfo) {
|
|
flushSync(() => {
|
|
setInitialState((s: any) => ({
|
|
...s,
|
|
currentUser: userInfo,
|
|
}));
|
|
});
|
|
}
|
|
};
|
|
const handleSubmit = async (values: API.LoginParams) => {
|
|
try {
|
|
// 登录
|
|
const msg = await login({
|
|
...values,
|
|
type,
|
|
});
|
|
if (msg.status === 'ok') {
|
|
const defaultLoginSuccessMessage = '登录成功!';
|
|
message.success(defaultLoginSuccessMessage);
|
|
await fetchUserInfo();
|
|
const urlParams = new URL(window.location.href).searchParams;
|
|
history.push(urlParams.get('redirect') || '/');
|
|
return;
|
|
}
|
|
console.log(msg);
|
|
// 如果失败去设置用户错误信息
|
|
setUserLoginState(msg);
|
|
} catch (error) {
|
|
const defaultLoginFailureMessage = '登录失败,请重试!';
|
|
console.log(error);
|
|
message.error(defaultLoginFailureMessage);
|
|
}
|
|
};
|
|
const { status, type: loginType } = userLoginState;
|
|
return (
|
|
<div className={styles.container}>
|
|
<Helmet>
|
|
<title>
|
|
{'登录'}- {Settings.title}
|
|
</title>
|
|
</Helmet>
|
|
<div
|
|
style={{
|
|
flex: '1',
|
|
padding: '32px 0',
|
|
}}
|
|
>
|
|
<LoginForm
|
|
contentStyle={{
|
|
minWidth: 280,
|
|
maxWidth: '75vw',
|
|
}}
|
|
logo={<img alt="logo" src="/logo.svg" />}
|
|
title="EVA Tea"
|
|
subTitle={'E志者协会是东区教学楼最专业的志愿维修组织'}
|
|
initialValues={{
|
|
autoLogin: true,
|
|
}}
|
|
onFinish={async (values: API.LoginParams) => {
|
|
await handleSubmit(values as API.LoginParams);
|
|
}}
|
|
>
|
|
<Tabs
|
|
activeKey={type}
|
|
onChange={setType}
|
|
centered
|
|
items={[
|
|
{
|
|
key: 'account',
|
|
label: '账户密码登录',
|
|
},
|
|
]}
|
|
/>
|
|
|
|
{status === 'error' && loginType === 'account' && (
|
|
<LoginMessage content={'错误的用户名和密码(admin/ant.design)'} />
|
|
)}
|
|
{type === 'account' && (
|
|
<>
|
|
<ProFormText
|
|
name="username"
|
|
fieldProps={{
|
|
size: 'large',
|
|
prefix: <UserOutlined />,
|
|
}}
|
|
placeholder={'用户名: admin or user'}
|
|
rules={[
|
|
{
|
|
required: true,
|
|
message: '用户名是必填项!',
|
|
},
|
|
]}
|
|
/>
|
|
<ProFormText.Password
|
|
name="password"
|
|
fieldProps={{
|
|
size: 'large',
|
|
prefix: <LockOutlined />,
|
|
}}
|
|
placeholder={'密码: ant.design'}
|
|
rules={[
|
|
{
|
|
required: true,
|
|
message: '密码是必填项!',
|
|
},
|
|
]}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
<div
|
|
style={{
|
|
marginBottom: 24,
|
|
}}
|
|
>
|
|
<ProFormCheckbox noStyle name="autoLogin">
|
|
自动登录
|
|
</ProFormCheckbox>
|
|
</div>
|
|
<div style={{ marginBottom: '2vh' }}>
|
|
{' '}
|
|
<Button
|
|
type="default"
|
|
block
|
|
size="large"
|
|
href={generateLink()}
|
|
style={{ backgroundColor: '#e0af68', color: '#ffffff' }}
|
|
>
|
|
<div>
|
|
<img src={oidcLogo} width="23px" style={iconStyles} />
|
|
使用 EVA 统一身份认证
|
|
</div>
|
|
</Button>
|
|
</div>
|
|
</LoginForm>
|
|
</div>
|
|
<PageFooter />
|
|
</div>
|
|
);
|
|
};
|
|
export default Login;
|