next-terminal/web/src/components/Login.js
dushixiang 11daa8bd4e
修复若干问题 (#229)
* 优化图标和LOGO

* 修改登录页面动画的速度为3

* 增加对websocket的异常处理

* 修复了用户组和用户名唯一判断错误的问题

* 提示版本号
2022-03-05 16:19:32 +08:00

247 lines
8.9 KiB
JavaScript

import React, {Component} from 'react';
import {Button, Card, Checkbox, Form, Input, Modal, Typography} from "antd";
import './Login.css'
import request from "../common/request";
import {message} from "antd/es";
import {withRouter} from "react-router-dom";
import {LockOutlined, OneToOneOutlined, UserOutlined} from '@ant-design/icons';
import Particles from "react-tsparticles";
import Background from '../images/bg.png'
import {setToken} from "../utils/utils";
const {Title} = Typography;
class LoginForm extends Component {
formRef = React.createRef();
totpInputRef = React.createRef();
state = {
inLogin: false,
height: window.innerHeight,
width: window.innerWidth,
loginAccount: undefined,
totpModalVisible: false,
confirmLoading: false
};
componentDidMount() {
window.addEventListener('resize', () => {
this.setState({
height: window.innerHeight,
width: window.innerWidth
})
});
}
handleSubmit = async params => {
this.setState({
inLogin: true
});
try {
let result = await request.post('/login', params);
if (result.code === 0) {
// 进行双因子认证
this.setState({
loginAccount: params,
totpModalVisible: true
})
this.totpInputRef.current.focus();
return;
}
if (result.code !== 1) {
throw new Error(result.message);
}
// 跳转登录
sessionStorage.removeItem('current');
sessionStorage.removeItem('openKeys');
setToken(result['data']);
// this.props.history.push();
window.location.href = "/"
} catch (e) {
message.error(e.message);
} finally {
this.setState({
inLogin: false
});
}
};
handleOk = async (values) => {
this.setState({
confirmLoading: true
})
let loginAccount = this.state.loginAccount;
loginAccount['totp'] = values['totp'];
try {
let result = await request.post('/loginWithTotp', loginAccount);
if (result['code'] !== 1) {
message.error(result['message']);
return;
}
// 跳转登录
sessionStorage.removeItem('current');
sessionStorage.removeItem('openKeys');
setToken(result['data']);
// this.props.history.push();
window.location.href = "/"
} catch (e) {
message.error(e.message);
} finally {
this.setState({
confirmLoading: false
});
}
}
handleCancel = () => {
this.setState({
totpModalVisible: false
})
}
render() {
return (
<div className='login-bg'
style={{width: this.state.width, height: this.state.height}}>
<Particles
id="tsparticles"
options={{
background: {
color: {
// value: "#0d47a1",
},
image: `url(${Background})`,
repeat: 'no-repeat',
size: '100% 100%'
},
fpsLimit: 60,
interactivity: {
events: {
onClick: {
enable: true,
mode: "push",
},
onHover: {
enable: true,
mode: "repulse",
},
resize: true,
},
modes: {
bubble: {
distance: 400,
duration: 2,
opacity: 0.8,
size: 40,
},
push: {
quantity: 4,
},
repulse: {
distance: 200,
duration: 0.4,
},
},
},
particles: {
color: {
value: "#ffffff",
},
links: {
color: "#ffffff",
distance: 150,
enable: true,
opacity: 0.5,
width: 1,
},
collisions: {
enable: true,
},
move: {
direction: "none",
enable: true,
outMode: "bounce",
random: false,
speed: 3,
straight: false,
},
number: {
density: {
enable: true,
value_area: 800,
},
value: 80,
},
opacity: {
value: 0.5,
},
shape: {
type: "circle",
},
size: {
random: true,
value: 5,
},
},
detectRetina: true,
}}
/>
<Card className='login-card' title={null}>
<div style={{textAlign: "center", margin: '15px auto 30px auto', color: '#1890ff'}}>
<Title level={1}>Next Terminal</Title>
</div>
<Form onFinish={this.handleSubmit} className="login-form">
<Form.Item name='username' rules={[{required: true, message: '请输入登录账号!'}]}>
<Input prefix={<UserOutlined/>} placeholder="登录账号"/>
</Form.Item>
<Form.Item name='password' rules={[{required: true, message: '请输入登录密码!'}]}>
<Input.Password prefix={<LockOutlined/>} placeholder="登录密码"/>
</Form.Item>
<Form.Item name='remember' valuePropName='checked' initialValue={false}>
<Checkbox>记住登录</Checkbox>
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button"
loading={this.state.inLogin}>
登录
</Button>
</Form.Item>
</Form>
</Card>
<Modal title="双因素认证" visible={this.state.totpModalVisible} confirmLoading={this.state.confirmLoading}
maskClosable={false}
okButtonProps={{form:'totp-form', key: 'submit', htmlType: 'submit'}}
onOk={() => {
this.formRef.current
.validateFields()
.then(values => {
this.handleOk(values);
// this.formRef.current.resetFields();
});
}}
onCancel={this.handleCancel}>
<Form id='totp-form' ref={this.formRef}>
<Form.Item name='totp' rules={[{required: true, message: '请输入双因素认证APP中显示的授权码'}]}>
<Input ref={this.totpInputRef} prefix={<OneToOneOutlined/>} placeholder="请输入双因素认证APP中显示的授权码"/>
</Form.Item>
</Form>
</Modal>
</div>
);
}
}
export default withRouter(LoginForm);