Merge pull request '登陆注册功能' (#250) from durian/forgeplus-react:featrue_login_and_register into featrue_login_and_register

This commit is contained in:
jasder 2021-11-09 09:19:36 +08:00
commit 116c3e531f
21 changed files with 1007 additions and 13 deletions

View File

@ -97,13 +97,17 @@ const ProjectIndex = Loadable({
loading: Loading,
});
const LoginRegisterPage = Loadable({
loader: () => import("./modules/loginRegister/LoginRegisterPage"),
loading: Loading,
});
// const CreateMerge = Loadable({
// loader: () => import('./forge/Merge/NewMerge'),
// loading: Loading,
// })
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize"];
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize","login","register","resetPassword"];
class App extends Component {
constructor(props) {
@ -303,14 +307,14 @@ class App extends Component {
}>
</Route>
<Route
{/* <Route
path="/register"
render={
(props) => {
return (<EducoderLogin {...this.props} {...props} {...this.state} />)
}
}
/>
/> */}
{/*403*/}
<Route path="/403" component={Shixunauthority} />
@ -330,6 +334,23 @@ class App extends Component {
}
/>
{/* 登录 */}
<Route
path="/login"
render={(props) =><LoginRegisterPage {...this.props} {...props}/>}
></Route>
{/* 注册 */}
<Route
path="/register"
render={(props) =><LoginRegisterPage {...this.props} {...props}/>}
></Route>
{/* 忘记密码 */}
<Route
path="/resetPassword"
render={(props) =><LoginRegisterPage {...this.props} {...props}/>}
></Route>
{/* 组织 */}
<Route path={"/organize"}

View File

@ -68,6 +68,8 @@ export function initAxiosInterceptors(props) {
if (response.data.status === -1) {
if (window.location.pathname.startsWith('/tasks/')) {
props.showSnackbar(response.data.message || '服务器异常,请联系管理员。')
} else if(window.location.pathname.startsWith('/login') || window.location.pathname.startsWith('/register') || window.location.pathname.startsWith('/resetPassword')) {
return response;
} else {
notification.open({
message: "提示",

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import AccountProfile from "../../modules/user/AccountProfile";
import { getImageUrl } from 'educoder'
import axios from 'axios';
import cookie from 'react-cookies';
import { Input , notification , Dropdown ,Popover, Menu,Badge, Button } from 'antd';
import { Link } from 'react-router-dom';
@ -98,11 +99,17 @@ class NewHeader extends Component {
educoderlogin = () => {
//登录账号
this.setState({
isRender: true
})
if(window.location.pathname === "/"){
window.location.href="/login";
}else{
this.setState({
isRender: true
})
}
}
educoderloginysl = () => {
//退出账号时清除登录页面的下次自动登录(用户再次打开登录页面时下次自动登录框不勾选)
cookie.remove("autologin");
//退出账号
var url = `/accounts/logout.json`;
axios.get((url)).then((result) => {
@ -460,7 +467,7 @@ class NewHeader extends Component {
<a onClick={() => this.educoderlogin()} className="mr5 color-grey-6">登录</a>
{
settings && settings.common && settings.common.register &&
<span><em className="vertical-line"></em><a className="ml5 color-grey-6" href={`${settings.common.register}`} target="_blank"></a></span>
<span><em className="vertical-line"></em><Link className="ml5 color-grey-6" to={`/register`}></Link></span>
}
</span>
:

View File

@ -376,7 +376,7 @@ class LoginDialog extends Component {
const messge = (
<div>
<p>登录密码出错已达上限账号已被锁定</p>
<p className="mt10">请10分钟后重新登录或<a href={'https://www.trustie.net/account/lost_password'} style={{textDecoration: "underline",color: "#4CACFF"}}>找回密码</a></p>
<p className="mt10">请10分钟后重新登录或<a href="/resetPassword" style={{textDecoration: "underline",color: "#4CACFF"}}>找回密码</a></p>
</div>
)
this.openNotifications(messge);
@ -414,7 +414,6 @@ class LoginDialog extends Component {
let { disabled } = this.state;
if (disabled === false && e.keyCode === 13) {
this.loginEDU()
console.log(1)
}
};
getloginurl = (url) => {
@ -516,7 +515,7 @@ class LoginDialog extends Component {
}
value={this.state.loginValue}
name="username"
placeholder="请输入有效的手机号/邮箱号" ></input>
placeholder="请输入邮箱地址/用户名" ></input>
<div style={{ height: '25px' }}><p className="color-orange edu-txt-left none" id="username_error_notice"
style={{ display: Phonenumberisnotco === undefined ? 'none' : 'block' }}>{Phonenumberisnotco}</p></div>
@ -533,7 +532,7 @@ class LoginDialog extends Component {
this.loginEDU : () => {
}
}
placeholder="密码"
placeholder="请输入登录密码"
/>
<div style={{ height: '25px' }}>
<p className="color-orange edu-txt-left none" id="password_error_notice">
@ -560,9 +559,9 @@ class LoginDialog extends Component {
<label htmlFor="p_autolog" style={{ top: '0px' }}>下次自动登录</label>
</span>
<span className="fr">
<a onClick={(url) => this.getloginurl(`${settings && settings.common && settings.common.lost_password}`)} className="mr3 color-grey-9">找回密码</a>
<a href="/resetPassword" className="mr3 color-grey-9">找回密码</a>
<em className="vertical-line"></em>
<a onClick={(url) => this.getloginurl(`${settings && settings.common && settings.common.register}`)} className="color-grey-9">注册</a>
<a href="/register" className="color-grey-9">注册</a>
</span>
</p>
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -0,0 +1,131 @@
import React, { useEffect, useState } from "react";
import { Form, Input, Button, Checkbox } from "antd";
import { Link } from "react-router-dom";
import axios from 'axios';
import educoderLogo from '../login/educoder.png';
import cookie from 'react-cookies';
import './LoginRegisterPage.scss';
function Login(props){
const [message,setMessage] = useState();
const [setting, setSetting] = useState(undefined);
const {form} = props;
const {getFieldDecorator } = form;
useEffect(()=>{
//DOMvalue
clear;
//settings.json
axios.get(`/setting.json`).then((response) => {
if (response && response.data) {
setSetting(response.data.setting);
}
})
},[])
//
function handleSubmit(){
form.validateFields((err, values) => {
if (!err) {
axios.post(`/accounts/login.json`, {
login: values.username,
password: values.password,
autologin: values.remember?1:0,
}).then((response) => {
if (!response.data.login) {
response.data.status === -2 ? setMessage(response.data.message) : setMessage("错误的账号或密码");
} else {
//
cookie.save('autologin',values.remember);
window.location.href = "/"+response.data.login;
}
}).catch((error) => {
console.log('error',error);
})
}
});
}
//
function comfirmWrite(rule, value, callback, index){
setMessage(undefined);
value ? callback():index === 1? callback("请输入邮箱地址或用户名登录"):callback("请输入登录密码");
}
//value->DOM
function clear(){
const password = document.getElementById("login_password");
if(password && password.type==="password"){
setTimeout(()=>{
password.removeAttribute('value');
},0)
}
}
return(
<div>
<div className="right_cont login_content">
<div className="login_register_head">
<span>欢迎登录 GitLink</span>
<span className="link_span">没有账号<Link to={`/register`}>去注册</Link></span>
</div>
<p className = {message?"message active":"message"}>{message}</p>
<Form className="login-form">
<Form.Item>
{getFieldDecorator('username',{
rules:[
{
validator: (rule, value, callback) => { comfirmWrite(rule, value, callback, 1) }
}
],
validateTrigger:"onBlur",
})(<Input className="account" placeholder="请输入邮箱地址/用户名"/>)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [
{
validator: (rule, value, callback) => { comfirmWrite(rule, value, callback, 2) }
}
],
validateTrigger:"onBlur",
})(
<Input.Password className="psd" placeholder="请输入登录密码" onBlur={clear} onChange={clear}/>,
)}
</Form.Item>
<div className="login_register_head">
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: cookie.load('autologin'),
})(<Checkbox>下次自动登录</Checkbox>)}
</Form.Item>
<Link to="/resetPassword" className="goResetPsdBut">忘记密码</Link>
</div>
<Button type="primary" htmlType="submit" onClick={handleSubmit} className="login_register_cofBut">登录</Button>
</Form>
{
setting && setting.third_party && setting.third_party.length > 0 ?
<p className="quick_logon">
<p className="quick_logon_p"></p>
<span className={"startlogin"}>&nbsp;快速登录&nbsp;</span>
{setting.third_party.map((item,key)=>{
return(
<a href={`${item.url}`}>
<img src={item.name === "educoder" ? educoderLogo : ""} width="46px" alt={`${item.name}登录`} />
</a>
)
})
}
</p>
:""
}
</div>
</div>
)
}
export default Form.create({ name: 'login' })(Login);

View File

@ -0,0 +1,28 @@
import React from "react";
import Login from "./Login";
import Register from "./Register";
import ResetPassword from "./ResetPassword";
import logo from './img/logo.png';
import banner from './img/banner.png';
import ball from './img/ball.png';
import img1 from './img/img1.png';
import img2 from './img/img2.png';
import '../loginRegister/LoginRegisterPage.scss';
function LoginRegisterPage(props){
return(
<div className="login_register">
<div className="login_register_left">
<img src={logo} className="logo"></img>
<img src={ball} className="ball"></img>
<img src={banner} className="banner"></img>
</div>
<div className="login_register_right">
{props.location.pathname === "/login" ? <Login {...props}/> : props.location.pathname === "/register" ? <Register/> : <ResetPassword/>}
<img src={img1} className="img1"></img>
<img src={img2} className="img2"></img>
</div>
</div>
)
}
export default LoginRegisterPage;

View File

@ -0,0 +1,237 @@
.login_register{
height: 100%;
}
.login_register_left,.login_register_right,.right_cont{
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}
.login_register_left{
display: flex;
position: absolute;
justify-content: center;
width: 30%;
height: 100%;
background-image: url(./img/bg.png);
.logo{
height: 65px;
margin-top: 160px;
}
.ball{
height: 220px;
z-index: 3;
position: absolute;
bottom: 270px;
animation: moving2 10s linear infinite;
}
@keyframes moving2 {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
100% {
transform: rotate(360deg);
}
}
.banner{
height: 486px;
position: absolute;
bottom: -90px;
left: 10px;
}
}
.login_register_right{
background-image: url(./img/rightBg.png);
position: absolute;
top: 0;
left: 30%;
right: 0;
height: 100%;
.img1{
position: relative;
left: 130px;
top: 50%;
}
.img2{
position: relative;
top: 65%;
left: 550px;
}
.right_cont{
width: 37.5rem;
position: absolute;
top: 15%;
left: 16%;
z-index: 3;
border-radius: 7px;
background-color: white;
padding: 55px 90px;
& .register_tips{
margin-top: -15px;
padding-bottom: 8px;
color: #808080;
font-size: 13px;
}
& .login_register_head{
display: flex;
justify-content: space-between;
align-items: center;
&>span:first-child{
font-size: 24px;
font-weight: 600;
color: #000000;
line-height: 33px;
}
& .ant-input{
width: 19rem;
}
}
& .account{
margin-top: 36px;
}
& .login_register_cofBut{
width: 100%;
height: 44px;
background: #466AFF;
border-color: #466AFF;
border-radius: 7px;
margin-top: 20px;
font-size: 15px;
&:hover{
background: #3456E5;
border-color: #3456E5;
}
}
& a{
color: #466AFF;
&:hover{
opacity:0.8;
}
}
& .link_span{
font-weight: 500;
font-size: 15px;
color: #808080;
}
& .ant-input{
height: 44px;
background-color: #F7F7F7 !important;
font-size: 15px;
color: #333333;
&:hover{
border-color:#466AFF;
}
}
& .has-success .ant-input{
background-color: #F7F7F7;
}
& .ant-form-explain{
margin-top: 5px;
}
& .message, .ant-form-explain{
color: #D40000;
font-size: 13px;
}
& .message.active{
margin-bottom: -30px !important;
margin-top: 10px;
}
//取消antd表单默认样式
.has-error .ant-input{
background: #F7F7F7;
border-color: #D40000;
}
//修改复选框默认antd样式
.ant-checkbox-checked .ant-checkbox-inner {
background-color: #466AFF;
border: #466AFF;
}
.ant-checkbox-checked::after{
border: 1px solid #466AFF;
}
.ant-checkbox-wrapper:hover .ant-checkbox-inner, .ant-checkbox:hover .ant-checkbox-inner, .ant-checkbox-input:focus + .ant-checkbox-inner,.ant-radio-checked .ant-radio-inner,.ant-radio-wrapper:hover .ant-radio, .ant-radio:hover .ant-radio-inner, .ant-radio-input:focus + .ant-radio-inner {
border-color: #466AFF;
}
}
.login_content{
background-image: url(./img/loginBg.png);
height: 480px;
& .login_register_cofBut{
margin-top: 0;
}
& .login_register_head{
& checkbox{
font-size: 15px;
color: #3C476E;
line-height: 21px;
}
& .goResetPsdBut{
margin-top: -25px;
}
}
& .quick_logon{
text-align: center;
& .quick_logon_p{
border-top: 1px solid #979797;
margin-top: 24px;
}
& .startlogin{
position: relative;
background: #dfe0f7;
display: block;
width: 90px;
top: -15px;
left: 39%;
font-size: 13px;
color: #8D8D8D;
}
}
}
.Register_content{
background-image: url(./img/registerBg.png);
& .register_last_form .ant-form-item-control{
line-height: 0.5;
}
}
.ResetPassword_content{
background-image: url(./img/resetPasswordBg.png);
height: 550px;
& .resetFailCaptcha{
position: absolute;
top: 25px;
color: #D40000;
font-size: 13px;
}
}
.codeBut{
height: 45px;
background: #F7F7F7;
border-radius: 7px;
border: 1px solid rgba(167, 177, 200, 0.33);
margin-left: 14px;
width: 102px;
color: #466AFF;
font-size: 15px;
}
.codeBut.disable{
color: rgba(0, 0, 0, 0.25);
background-color: #f5f5f5;
border-color: #d9d9d9;
}
}
.ant-message-notice-content{
box-shadow: 0px 1px 8px 1px rgba(0, 0, 0, 0.11);
border-radius: 10px;
margin-top: 2rem;
margin-left: 10%;
font-size: 15px;
& a{
color: #466AFF;
&:hover{
opacity:0.8;
}
}
}

View File

@ -0,0 +1,312 @@
import React, { useEffect, useRef, useState } from "react";
import { Form, Input, Button, Checkbox, message } from "antd";
import { Link } from "react-router-dom";
import axios from 'axios';
import { setmiyah } from 'educoder';
import './LoginRegisterPage.scss';
function Register(props){
const {form} = props;
const {getFieldDecorator } = form;
const [emailStr, setEmailStr] = useState(undefined);
const [secondsStr, setSecondsStr] = useState(60);
const [countDown, setCountDown] = useState(false);
const [getCaptchaBut, setGetCaptchaBut] = useState(false);
const [mess, setMess] = useState(undefined);
const [tipVisable, setTipVisable] = useState(false);
//
const [userNameGo, setUserNameGo] = useState(true);
const [emailGo, setEmailGo] = useState(true);
const seconds = useRef();
let interval = undefined;
//
const inputEl = useRef(null);
//
function handleSubmit(){
form.validateFields((err, values) => {
if (!err) {
values.agreement && axios.post(`/accounts/register.json`, {
login: values.email,
namespace: values.register_username,
password: values.register_psd,
password_confirmation: values.psdComfirm,
code: values.captcha
}).then((response)=>{
if(response.data && response.data.status === -6){
//
form.setFields({captcha: {value:values.captcha,errors:[new Error('验证码错误,请重新输入')]}})
}else if(response.data && response.data.status === 0){
//forge
window.location.href = "/"+values.register_username;
}else{
setMess(response.data.message);
}
})
}
});
}
//username
function usernameConfirm(rule, value, callback){
setUserNameGo(true);
value && userNameGo ? axios.post(`/accounts/check.json`, {
value: value,
type: 1
}).then(response => {
if (response.data.status === -1) {
callback('该名称已经被使用');
} else {
setUserNameGo(false);
callback();
}
}):callback()
}
//
function emailConfirm(rule, value, callback) {
setEmailGo(true);
value && emailGo ? axios.post(`/accounts/check.json`, {
value: value,
type: 2
}).then(response => {
if (response.data.status === -1) {
callback('该邮箱已被注册');
} else {
setEmailStr(value);
setGetCaptchaBut(true);
setEmailGo(false);
callback();
}
}):callback();setEmailStr(undefined);
}
//
function comfirmPassWord(rule, value, callback, index) {
if ((index === 2 && value && form.getFieldValue('register_psd') && value !== form.getFieldValue('register_psd')) || (index === 1 && value && form.getFieldValue('psdComfirm') && value !== form.getFieldValue('psdComfirm'))) {
if(index===1){
form.setFields({psdComfirm: {value:form.getFieldValue('psdComfirm'),errors:[new Error('密码不一致,请重新输入')]}});
callback();
}else{
callback('密码不一致,请重新输入');
}
} else {
callback();
}
}
//
function checkPassWord(rule, value, callback){
if(!value){
setTipVisable(true);
callback('请输入登录密码');
}else if(/(?!.*\s)(?!^[\u4e00-\u9fa5]+$)^.{8,16}$/.test(value)){
callback()
}else{
setTipVisable(true);
if(value.length<8 || value.length>16){
callback('密码长度为8-16个字符');
}else{
callback('密码不能使用空格');
}
}
}
//
function comfirmRead(rule, value, callback){
if(value){
callback();
}else{
callback("请阅读并接受我们的服务条款");
}
}
//
function getCaptcha() {
setMess(undefined);
if (emailStr) {
//
setCountDown(true);
setGetCaptchaBut(false);
seconds.current = 60;
!interval && clearInterval(interval);
interval = setInterval(() => {
if (seconds.current > 1) {
let oldSeconds = seconds.current;
seconds.current = oldSeconds - 1;
setSecondsStr(oldSeconds - 1);
} else {
clearInterval(interval);
//->
setGetCaptchaBut(true);
setCountDown(false);
}
}, 1000)
//
axios.get(`/accounts/get_verification_code.json`, {
params: {
login: emailStr,
type: 1,
smscode: setmiyah(emailStr),
}
}).then(response => {
if (response.data && response.data.status === 0) {
//
let email = emailStr.substring(emailStr.indexOf("@")+1);
message.success({content:<span>验证码已发送请注意查收<a href={`https://mail.${email}`} target="_blank">前往邮箱</a></span>});
} else {
//,
setGetCaptchaBut(false);
setCountDown(false);
clearInterval(interval);
setMess(response.data.message);
}
})
}
}
//value->DOM
function clear(){
const password = document.getElementById("register_register_psd");
const passwordComfirm = document.getElementById("register_psdComfirm");
if(password && password.type==="password"){
setTimeout(()=>{
password.removeAttribute('value');
},0)
}
if(passwordComfirm && passwordComfirm.type==="password"){
setTimeout(()=>{
passwordComfirm.removeAttribute('value');
},0)
}
}
useEffect(() => {
inputEl.current.focus();
clear;
}, [])
return(
<div>
<div className="right_cont Register_content">
<div className="login_register_head">
<span>欢迎注册 GitLink</span>
<span className="link_span">已有账号<Link to={`/login`}>立即登录</Link></span>
</div>
<p className={mess ? "message active" : "message"}>{mess}</p>
<Form className="login-form">
<Form.Item>
{getFieldDecorator('register_username',{
rules:[
{
required:true,
message:"请输入用户名"
},
{
pattern: /^[a-zA-Z]/,
message: "用户名必须以字母开头"
},
{
pattern: /[a-zA-Z0-9]$/,
message: "用户名只能使用英文字母和数字"
},
{
min: 4,
max: 15,
message: "用户名长度为4到15个字符"
},
{
validator: (rule, value, callback) => { usernameConfirm(rule, value, callback) }
}
],
validateTrigger:"onBlur",
validateFirst: true,
})(<Input ref={inputEl} className="account" placeholder="请输入4-15位用户名以字母开头只能使用字母和数字" readOnly onFocus={()=>{document.getElementById("register_register_username").removeAttribute("readOnly")}}/>)}
</Form.Item>
<Form.Item>
{getFieldDecorator('email',{
rules:[
{
type: 'email',
message: '请输入正确的邮箱格式',
},
{
required:true,
message:"请输入邮箱地址"
},
{
validator: (rule, value, callback) => { emailConfirm(rule, value, callback) }
}
],
validateTrigger:"onBlur",
validateFirst: true,
})(<Input className="email" placeholder="请输入邮箱地址" readOnly onFocus={()=>{document.getElementById("register_email").removeAttribute("readOnly")}} />)}
</Form.Item>
<div className="login_register_head">
<Form.Item>
{getFieldDecorator('captcha', {
rules: [{
required: true,
message: "请输入验证码"
}],
validateTrigger: "onBlur",
})(
<Input className="captcha" placeholder="请输入验证码" readOnly onFocus={()=>{document.getElementById("register_captcha").removeAttribute("readOnly")}} />
)}
<Button className={getCaptchaBut ? 'codeBut':'codeBut disable'} disabled={!getCaptchaBut} onClick={getCaptcha}>{getCaptchaBut || (!getCaptchaBut && !countDown)?"获取验证码":`重发(${secondsStr}s)`}</Button>
</Form.Item>
</div>
<Form.Item>
{getFieldDecorator('register_psd',{
rules:[
{
validator: (rule, value, callback) => { comfirmPassWord(rule, value, callback, 1) }
},
{
validator: (rule, value, callback) => { checkPassWord(rule, value, callback) }
}
],
validateTrigger:"onBlur",
validateFirst: true,
})(<Input.Password className="register_psd" placeholder="请输入登录密码" onBlur={clear} onChange={clear} readOnly onFocus={()=>{document.getElementById("register_register_psd").removeAttribute("readOnly")}}/>)}
</Form.Item>
<span className="register_tips" style={{display:tipVisable?"none":"block"}}>请输入8-16位密码区分大小写不能使用空格</span>
<Form.Item>
{getFieldDecorator('psdComfirm', {
rules: [
{
required: true,
message: "请确认登录密码"
},
{
validator: (rule, value, callback) => { comfirmPassWord(rule, value, callback, 2) }
}
],
validateTrigger: "onBlur",
validateFirst: true,
})(<Input.Password className="psdComfirm" placeholder="请确认登录密码" onBlur={clear} onChange={clear} readOnly onFocus={()=>{document.getElementById("register_psdComfirm").removeAttribute("readOnly")}}/>)}
</Form.Item>
<Form.Item className="register_last_form">
{getFieldDecorator('agreement', {
valuePropName: 'checked',
initialValue: false,
rules: [
{
validator: (rule, value, callback) => { comfirmRead(rule, value, callback) }
}
],
})(<Checkbox>我已阅读并接受<a className="login-form-forgot" href="https://forum.trustie.net/forums/5029/detail" target="_blank">GitLink服务协议条款</a></Checkbox>)}
</Form.Item>
<Button type="primary" htmlType="submit" className="login_register_cofBut" onClick={handleSubmit}>注册</Button>
</Form>
</div>
</div>
)
}
export default Form.create({ name: 'register' })(Register);

View File

@ -0,0 +1,257 @@
import React, { useEffect, useRef, useState } from "react";
import { Form, Input, Button, message } from "antd";
import { Link } from "react-router-dom";
import axios from 'axios';
import { setmiyah } from 'educoder';
import './LoginRegisterPage.scss';
function ResetPassword(props) {
const {form } = props;
const {getFieldDecorator } = form;
const [emailStr, setEmailStr] = useState(undefined);
const [secondsStr, setSecondsStr] = useState(60);
const [countDown, setCountDown] = useState(false);
const [getCaptchaBut, setGetCaptchaBut] = useState(false);
const [mess, setMess] = useState(undefined);
const [tipVisable, setTipVisable] = useState(false);
//check.json
const [emailGo, setEmailGo] = useState(true);
const seconds = useRef();
let interval = undefined;
//
const inputEl = useRef(null);
//
function handleSubmit() {
form.validateFieldsAndScroll((err, values) => {
if (!err) {
axios.post(`/accounts/reset_password.json`, {
login: values.email,
password: values.psd,
password_confirmation: values.psdComfirm,
code: values.captcha,
}).then((response) => {
if (response.data.status === 0) {
//
axios.post(`/accounts/login.json`, {
login: values.email,
password: values.psd
}).then((login_response) => {
if (!login_response.data.login) {
setMess(login_response.data.message);
} else {
window.location.href = "/" + login_response.data.login;
}
}).catch((error) => {
console.log('error',error);
})
} else {
const message = response.data.message;
message === "验证码不正确" ? form.setFields({captcha: {value:values.captcha,errors:[new Error('验证码错误,请重新输入')]}}) : setMess(message);
}
})
}
});
}
//
function emailConfirm(rule, value, callback) {
setEmailGo(true);
value && emailGo ? axios.post(`/accounts/check.json`, {
value: value,
type: 2
}).then(response => {
if (response.data && response.data.status === -1) {
setEmailStr(value)
setGetCaptchaBut(true);
setEmailGo(false);
callback();
} else {
callback('此邮箱未注册');
}
}):callback();setEmailStr(undefined);
}
//
function comfirmPassWord(rule, value, callback, index) {
if ((index === 2 && value && form.getFieldValue('psd') && value !== form.getFieldValue('psd')) || (index === 1 && value && form.getFieldValue('psdComfirm') && value !== form.getFieldValue('psdComfirm'))) {
if(index===1){
form.setFields({psdComfirm: {value:form.getFieldValue('psdComfirm'),errors:[new Error('密码不一致,请重新输入')]}});
callback();
}else{
callback('密码不一致,请重新输入');
}
} else {
callback();
}
}
//
function checkPassWord(rule, value, callback){
if(!value){
setTipVisable(true);
callback('请输入新密码');
}else if(/(?!.*\s)(?!^[\u4e00-\u9fa5]+$)^.{8,16}$/.test(value)){
callback()
}else{
setTipVisable(true);
if(value.length<8 || value.length>16){
callback('密码长度为8-16个字符');
}else{
callback('密码不能使用空格');
}
}
}
//
function getCaptcha() {
setMess(undefined);
if (emailStr) {
//
setCountDown(true);
setGetCaptchaBut(false);
seconds.current = 60;
!interval && clearInterval(interval);
interval = setInterval(() => {
if (seconds.current > 1) {
let oldSeconds = seconds.current;
seconds.current = oldSeconds - 1;
setSecondsStr(oldSeconds - 1);
} else {
//->
setGetCaptchaBut(true);
setCountDown(false);
clearInterval(interval);
}
}, 1000)
//
axios.get(`/accounts/get_verification_code.json`, {
params: {
login: emailStr,
type: 2,
smscode: setmiyah(emailStr),
}
}).then(response => {
if (response.data && response.data.status === 0) {
//
let email = emailStr.substring(emailStr.indexOf("@")+1);
message.success({content:<span>验证码已发送请注意查收<a href={`https://mail.${email}`} target="_blank">前往邮箱</a></span>});
} else {
//,
setGetCaptchaBut(false);
setCountDown(false);
clearInterval(interval);
setMess(response.data.message);
}
})
}
}
//value->DOM
function clear(){
const password = document.getElementById("resetPassword_psd");
const passwordComfirm = document.getElementById("resetPassword_psdComfirm");
if(password && password.type==="password"){
setTimeout(()=>{
password.removeAttribute('value');
},0)
}
if(passwordComfirm && passwordComfirm.type==="password"){
setTimeout(()=>{
passwordComfirm.removeAttribute('value');
},0)
}
}
useEffect(() => {
inputEl.current.focus();
clear;
}, [])
return (
<div>
<div className="right_cont ResetPassword_content">
<div className="login_register_head">
<span>找回密码</span>
<span className="link_span">已有账号<Link to={`/login`}>立即登录</Link></span>
</div>
<p className={mess ? "message active" : "message"}>{mess}</p>
<Form className="login-form">
<Form.Item>
{getFieldDecorator('email', {
rules: [
{
type: 'email',
message: '请输入正确的邮箱格式',
},
{
required: true,
message: "请输入已注册的邮箱"
},
{
validator: (rule, value, callback) => { emailConfirm(rule, value, callback) }
}
],
validateTrigger: "onBlur",
validateFirst: true,
})(<Input ref={inputEl} className="account" placeholder="请输入已注册的邮箱" readOnly onFocus={()=>{document.getElementById("resetPassword_email").removeAttribute("readOnly")}} />)}
</Form.Item>
<div className="login_register_head">
<Form.Item>
{getFieldDecorator('captcha', {
rules: [{
required: true,
message: "请输入验证码"
}],
validateTrigger: "onBlur",
})(
<Input className="captcha" placeholder="请输入验证码" readOnly onFocus={()=>{document.getElementById("resetPassword_captcha").removeAttribute("readOnly")}} />
)}
<Button className={getCaptchaBut ? 'codeBut':'codeBut disable'} disabled={!getCaptchaBut} onClick={getCaptcha}>{getCaptchaBut || (!getCaptchaBut && !countDown)?"获取验证码":`重发(${secondsStr}s)`}</Button>
</Form.Item>
</div>
<Form.Item>
{getFieldDecorator('psd', {
rules: [
{
validator: (rule, value, callback) => { comfirmPassWord(rule, value, callback,1) }
},
{
validator: (rule, value, callback) => { checkPassWord(rule, value, callback) }
}
],
validateTrigger: "onBlur",
validateFirst: true,
})(<Input.Password className="psd" placeholder="请输入新密码" onBlur={clear} onChange={clear} readOnly onFocus={()=>{document.getElementById("resetPassword_psd").removeAttribute("readOnly")}} />)}
</Form.Item>
<span className="register_tips" style={{display:tipVisable?"none":"block"}}>请输入8-16位密码区分大小写不能使用空格</span>
<Form.Item>
{getFieldDecorator('psdComfirm', {
rules: [
{
required: true,
message: "请确认新密码"
},
{
validator: (rule, value, callback) => { comfirmPassWord(rule, value, callback,2) }
}
],
validateTrigger: "onBlur",
validateFirst: true,
})(<Input.Password className="psdComfirm" placeholder="请确认新密码" onBlur={clear} onChange={clear} readOnly onFocus={()=>{document.getElementById("resetPassword_psdComfirm").removeAttribute("readOnly")}} />)}
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login_register_cofBut" onClick={handleSubmit}>重置密码并登录</Button>
</Form.Item>
</Form>
</div>
</div>
)
}
export default Form.create({ name: 'resetPassword' })(ResetPassword);

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB