From 473ac605c160d31e5c072d39c910f15788d3a7ac Mon Sep 17 00:00:00 2001 From: Bosn Date: Thu, 1 Nov 2018 10:06:45 +0800 Subject: [PATCH] add password recovery --- src/actions/account.js | 5 ++ src/components/account/LoginForm.jsx | 5 +- src/components/account/RegisterForm.jsx | 16 +++- src/components/account/ResetForm.jsx | 107 ++++++++++++++++++++++++ src/components/account/ResetForm.sass | 22 +++++ src/relatives/AccountRelative.js | 10 +++ src/relatives/services/Account.js | 103 ++++++++++++++++------- src/routes.jsx | 2 + 8 files changed, 234 insertions(+), 36 deletions(-) create mode 100644 src/components/account/ResetForm.jsx create mode 100644 src/components/account/ResetForm.sass diff --git a/src/actions/account.js b/src/actions/account.js index 2cfa3ad..8316e20 100644 --- a/src/actions/account.js +++ b/src/actions/account.js @@ -3,6 +3,11 @@ export const login = (user, onResolved) => ({ type: 'USER_LOGIN', user, onResolv export const loginSucceeded = (user) => ({ type: 'USER_LOGIN_SUCCEEDED', user }) export const loginFailed = (message) => ({ type: 'USER_LOGIN_FAILED', message }) +// 重置 +export const reset = (email, password, onResolved) => ({ type: 'USER_RESET', email, password, onResolved }) +export const resetSucceeded = () => ({ type: 'USER_RESET_SUCCEEDED' }) +export const resetFailed = (message) => ({ type: 'USER_RESET_FAILED', message }) + // 登出 export const logout = () => ({ type: 'USER_LOGOUT' }) export const logoutSucceeded = () => ({ type: 'USER_LOGOUT_SUCCEEDED' }) diff --git a/src/components/account/LoginForm.jsx b/src/components/account/LoginForm.jsx index a129dce..8377eff 100644 --- a/src/components/account/LoginForm.jsx +++ b/src/components/account/LoginForm.jsx @@ -54,8 +54,9 @@ class LoginForm extends Component {
- - 注册 + + 注册 + 密码找回
{this.props.auth.message &&
diff --git a/src/components/account/RegisterForm.jsx b/src/components/account/RegisterForm.jsx index 1457d71..e2d8266 100644 --- a/src/components/account/RegisterForm.jsx +++ b/src/components/account/RegisterForm.jsx @@ -10,12 +10,14 @@ const mockUser = process.env.NODE_ENV === 'development' ? () => Mock.mock({ fullname: '@CNAME', email: '@email', - password: '@string(6)' + password: '@string(6)', + errMsg: '', }) : () => ({ fullname: '', email: '', - password: '' + password: '', + errMsg: '', }) // 展示组件 @@ -25,6 +27,7 @@ class RegisterForm extends Component { this.state = mockUser() } render () { + const { errMsg } = this.state return (
@@ -43,6 +46,7 @@ class RegisterForm extends Component { this.setState({ password: e.target.value })} type='password' className='form-control' placeholder='Password' required />
+ {errMsg &&
{errMsg}
} 取消 @@ -52,8 +56,14 @@ class RegisterForm extends Component { handleSubmit = (e) => { let { history, onAddUser } = this.props e.preventDefault() - onAddUser(this.state, () => { + onAddUser(this.state, (errorOccurred, errMsg) => { + if (errorOccurred) { + this.setState({ + errMsg, + }) + } else { history.push('/') // 另一种方式是 + } }) } } diff --git a/src/components/account/ResetForm.jsx b/src/components/account/ResetForm.jsx new file mode 100644 index 0000000..f4e79c4 --- /dev/null +++ b/src/components/account/ResetForm.jsx @@ -0,0 +1,107 @@ +import React, { Component } from 'react' +import { PropTypes, connect } from '../../family' +import { reset } from '../../actions/account' +import { Link } from 'react-router-dom' +import './ResetForm.css' + +// 展示组件 +class LoginForm extends Component { + static contextTypes = { + store: PropTypes.object.isRequired + } + static propTypes = { + auth: PropTypes.object.isRequired + } + constructor(props) { + super(props) + this.state = { + email: '', + password: '', + showPassword: false, + newPassword: '', + } + } + render() { + const { showPassword, email, password, newPassword } = this.state + if (newPassword) { + return ( +
+
+ 找回密码 +
+
+
+ 您的密码已重设为: {newPassword}, 请重新登陆。 +
+
+
+ ) + } + return ( +
+
+ 找回密码 +
+
+
+
+ + this.setState({ email: e.target.value })} className='form-control' placeholder='Email' autoFocus='true' required disabled={showPassword} /> +
+ {showPassword &&
+
+ + this.setState({ password: e.target.value })} className='form-control' placeholder='验证码' /> +
+
+ 已发送验证码至您输入的邮箱,请输入验证码以重置您的密码。 +
+
} +
+
+ + 返回 +
+ {this.props.auth.message && +
+ {this.props.auth.message} +
+ } +
+
+ ) + } + handleSubmit = (e) => { + const { onReset } = this.props + const { email, password } = this.state + e.preventDefault() + if (email) { + this.setState({ + showPassword: true, + }) + onReset(email, password, (result) => { + if (!result.isOk) { + alert(result.errMsg) + return + } + if (result.data) { + this.setState({ + newPassword: result.data, + }) + } + }) + } + } +} + +// 容器组件 +const mapStateToProps = (state) => ({ + auth: state.auth +}) +const mapDispatchToProps = ({ + onReset: reset, +}) +export default connect( + mapStateToProps, + mapDispatchToProps +)(LoginForm) diff --git a/src/components/account/ResetForm.sass b/src/components/account/ResetForm.sass new file mode 100644 index 0000000..991eaf3 --- /dev/null +++ b/src/components/account/ResetForm.sass @@ -0,0 +1,22 @@ +@import "../../assets/variables.sass"; +// 水平居中 + 垂直居中 +.ResetForm + position: fixed; + left: 50%; + top: 50%; + width: 25rem; + margin-left: -12.5rem; + margin-top: -135px; + border: 1px solid #E6E6E6; + border-radius: .5rem; + .header + padding: 1.5rem 3rem; + border-bottom: 1px solid $border; + .title + font-size: 2rem; + .body + padding: 1.5rem 3rem; + .footer + padding: 1.5rem 3rem; + border-top: 1px solid $border; + diff --git a/src/relatives/AccountRelative.js b/src/relatives/AccountRelative.js index 0ae1c99..0102486 100644 --- a/src/relatives/AccountRelative.js +++ b/src/relatives/AccountRelative.js @@ -63,6 +63,16 @@ export default { } } catch (e) { yield put(AccountAction.addUserFailed(e.message)) + if (action.onResolved) action.onResolved(true, e.message) + } + }, + * [AccountAction.reset().type] (action) { + try { + const result = yield call(AccountService.resetUser, action.email, action.password) + action.onResolved && action.onResolved(result) + } catch (e) { + yield put(AccountAction.resetFailed(e.message)) + if (action.onResolved) action.onResolved(true, e.message) } }, * [AccountAction.updateUser().type] (action) { diff --git a/src/relatives/services/Account.js b/src/relatives/services/Account.js index 88eb40c..43c6eb9 100644 --- a/src/relatives/services/Account.js +++ b/src/relatives/services/Account.js @@ -1,74 +1,115 @@ -import { CREDENTIALS, serve, HEADERS } from './constant' +import { + CREDENTIALS, + serve, + HEADERS +} from './constant' export default { // 获取登陆信息 - fetchLoginInfo () { - return fetch(`${serve}/account/info`, { ...CREDENTIALS }) + fetchLoginInfo() { + return fetch(`${serve}/account/info`, { ...CREDENTIALS + }) .then(res => res.json()) .then(json => json.data) }, // 注册用户 - addUser (user) { + addUser(user) { return fetch(`${serve}/account/register`, { - ...CREDENTIALS, - method: 'POST', - body: JSON.stringify(user), - headers: { 'Content-Type': 'application/json' } - }) + ...CREDENTIALS, + method: 'POST', + body: JSON.stringify(user), + headers: { + 'Content-Type': 'application/json' + } + }) + .then(res => res.json()) + .then(json => json.data) + }, + // 重置用户 + resetUser(email, password) { + return fetch(`${serve}/account/reset`, { + ...CREDENTIALS, + method: 'POST', + body: JSON.stringify({ + email, password, + }), + headers: { + 'Content-Type': 'application/json' + } + }) .then(res => res.json()) .then(json => json.data) }, // 修改密码 - updateUser (user) { + updateUser(user) { return fetch(`${serve}/account/update`, { - ...CREDENTIALS, - method: 'POST', - body: JSON.stringify(user), - headers: HEADERS.JSON - }) + ...CREDENTIALS, + method: 'POST', + body: JSON.stringify(user), + headers: HEADERS.JSON + }) .then(res => res.json()) .then(json => json.data) }, // 用户登陆 - login ({ email, password, captcha }) { + login({ + email, + password, + captcha + }) { return fetch(`${serve}/account/login`, { - ...CREDENTIALS, - method: 'POST', - body: JSON.stringify({ email, password, captcha }), - headers: { 'Content-Type': 'application/json' } - }) + ...CREDENTIALS, + method: 'POST', + body: JSON.stringify({ + email, + password, + captcha + }), + headers: { + 'Content-Type': 'application/json' + } + }) .then(res => res.json()) .then(json => json.data) }, // 用户退出 - logout () { - return fetch(`${serve}/account/logout`, { ...CREDENTIALS }) + logout() { + return fetch(`${serve}/account/logout`, { ...CREDENTIALS + }) .then(res => res.json()) .then(json => json.data) }, // 获取用户总数 - fetchUserCount () { + fetchUserCount() { return fetch(`${serve}/account/count`) .then(res => res.json()) .then(json => json.data) }, // 获取用户列表 - fetchUserList ({ name = '', cursor = 1, limit = 100 } = {}) { + fetchUserList({ + name = '', + cursor = 1, + limit = 100 + } = {}) { return fetch(`${serve}/account/list?name=${name}&cursor=${cursor}&limit=${limit}`) .then(res => res.json()) - // .then(json => json.data) + // .then(json => json.data) }, // 根据 id 删除指定用户 - deleteUser (id) { + deleteUser(id) { return fetch(`${serve}/account/remove?id=${id}`) .then(res => res.json()) .then(json => json.data) }, // 获取用户列表 - fetchLogList ({ cursor = 1, limit = 100 } = {}) { - return fetch(`${serve}/account/logger?cursor=${cursor}&limit=${limit}`, { ...CREDENTIALS }) + fetchLogList({ + cursor = 1, + limit = 100 + } = {}) { + return fetch(`${serve}/account/logger?cursor=${cursor}&limit=${limit}`, { ...CREDENTIALS + }) .then(res => res.json()) - // .then(json => json.data) + // .then(json => json.data) } -} +} \ No newline at end of file diff --git a/src/routes.jsx b/src/routes.jsx index 3c6511b..429eb23 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -10,6 +10,7 @@ import Home from './components/home/Home' import LoginForm from './components/account/LoginForm' import RegisterForm from './components/account/RegisterForm' import UpdateForm from './components/account/UpdateForm' +import ResetForm from './components/account/ResetForm' const UserList = (props) => ( require.ensure([], require => cb(require('./components/account/UserList')))}> @@ -92,6 +93,7 @@ const Routes = ({ match, location }, { store }) => { {/* */} + {/*