Skip to content

Commit

Permalink
add password recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
Bosn committed Nov 1, 2018
1 parent 6f5bd3f commit 473ac60
Show file tree
Hide file tree
Showing 8 changed files with 234 additions and 36 deletions.
5 changes: 5 additions & 0 deletions src/actions/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -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' })
Expand Down
5 changes: 3 additions & 2 deletions src/components/account/LoginForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ class LoginForm extends Component {
</div>
</div>
<div className='footer'>
<button type='submit' className='btn btn-primary w140 mr20'>提交</button>
<Link to='/account/register'>注册</Link>
<button type='submit' className='btn btn-primary w80 mr10'>提交</button>
<Link to='/account/register' className='mr10'>注册</Link>
<Link to='/account/reset'>密码找回</Link>
</div>
{this.props.auth.message &&
<div className='alert alert-danger fade show' role='alert'>
Expand Down
16 changes: 13 additions & 3 deletions src/components/account/RegisterForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
})

// 展示组件
Expand All @@ -25,6 +27,7 @@ class RegisterForm extends Component {
this.state = mockUser()
}
render () {
const { errMsg } = this.state
return (
<section className='RegisterForm'>
<div className='header'>
Expand All @@ -43,6 +46,7 @@ class RegisterForm extends Component {
<label>密码:</label>
<input value={this.state.password} onChange={e => this.setState({ password: e.target.value })} type='password' className='form-control' placeholder='Password' required />
</div>
{errMsg && <div className='alert alert-danger'>{errMsg}</div>}
<button type='submit' className='btn btn-primary w140 mr20'>提交</button>
<Link to='/account' className=''>取消</Link>
</form>
Expand All @@ -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('/') // 另一种方式是 <Redirect to="/somewhere/else"/>
}
})
}
}
Expand Down
107 changes: 107 additions & 0 deletions src/components/account/ResetForm.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<section className='ResetForm'>
<div className='header'>
<span className='title'>找回密码</span>
</div>
<div className='body'>
<div className='form-group'>
您的密码已重设为: {newPassword}, 请重新登陆。
</div>
</div>
</section>
)
}
return (
<section className='ResetForm'>
<div className='header'>
<span className='title'>找回密码</span>
</div>
<form onSubmit={this.handleSubmit}>
<div className='body'>
<div className='form-group'>
<label>邮箱:</label>
<input value={email} onChange={e => this.setState({ email: e.target.value })} className='form-control' placeholder='Email' autoFocus='true' required disabled={showPassword} />
</div>
{showPassword && <div>
<div className='form-group'>
<label>验证码:</label>
<input value={password} onChange={e => this.setState({ password: e.target.value })} className='form-control' placeholder='验证码' />
</div>
<div className='form-group'>
已发送验证码至您输入的邮箱,请输入验证码以重置您的密码。
</div>
</div>}
</div>
<div className='footer'>
<button type='submit' className='btn btn-primary w80 mr10'>提交</button>
<Link to='/account/login' className='mr10'>返回</Link>
</div>
{this.props.auth.message &&
<div className='alert alert-danger fade show' role='alert'>
{this.props.auth.message}
</div>
}
</form>
</section>
)
}
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)
22 changes: 22 additions & 0 deletions src/components/account/ResetForm.sass
Original file line number Diff line number Diff line change
@@ -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;

10 changes: 10 additions & 0 deletions src/relatives/AccountRelative.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
103 changes: 72 additions & 31 deletions src/relatives/services/Account.js
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
2 changes: 2 additions & 0 deletions src/routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
<Bundle load={cb => require.ensure([], require => cb(require('./components/account/UserList')))}>
Expand Down Expand Up @@ -92,6 +93,7 @@ const Routes = ({ match, location }, { store }) => {
{/* <Route component={Header}/> */}
<Switch>
<Route path='/account/register' component={RegisterForm} />
<Route path='/account/reset' component={ResetForm} />
<Route component={LoginForm} />
</Switch>
{/* <Footer/> */}
Expand Down

0 comments on commit 473ac60

Please sign in to comment.