首先上效果图:
选择皮肤下拉。
功能分析:
- 点击记住用户名区域,checkbox选中,再点击则checkbox取消选中。
- 点击皮肤选择框,会显示皮肤下拉。
- 如果记住用户勾选了,则从本地缓存中读取上次登陆的用户名称。
- 如果记住用户勾选了,则点击登陆提交时,需要将当前用户的名称保存到本地缓存中。
- 点击皮肤选择框,弹出皮肤选择下拉,但点击界面其他地方时将下拉框隐藏。
- 点击登陆按钮时请求后台数据,如果登陆成功跳转界面。
- 如果登陆失败则弹出错误提醒框。
代码存放位置:
直接上代码了:
Login.js的内容:
import React, { Component } from 'react' // 引入React
import { connect,dispatch } from 'react-redux' // 引入connect
import topTitleLog from './images/topTitleLog.png';
import sysLogo from './images/sysLogo.png';
import paopao from './images/paopao.png';
import SkinDropCom from '../../components/SkinDropCom';
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
import '../../assets/css/goingStyle.css';
import './Login.less';
import 'font-awesome/css/font-awesome.min.css';
import {parseBoolean,AssertUtils} from '../../utils/GoingUtils';
import { Link } from 'react-router'
import { notification } from 'antd';
class Login extends Component {
//构造函数 绑定事件
constructor(...props) {
super(...props);
[
'_setCurUserName', '_clickLoginBtn', '_selCheckBoxEl', '_setPsw', '_changeCurSkin','_showDropDown'
].forEach(func=> {
this[func] = this[func].bind(this);
});
}
componentWillMount() {
}
//如果挑战界面 存在错误信息 将错误内容展示出来
componentDidUpdate() {
const { dispatch,loginMd}=this.props;
if (loginMd.error != null) {
notification['error']({
message: '错误信息',
description: `系统登陆失败,失败原因:${loginMd.error}`,
});
}
}
//校验用户名 跟 密码是否为空
validateFieldsAndScroll({userName,password}) {
if (AssertUtils.isNotEmpty(userName) && AssertUtils.isNotEmpty(password)) {
return true;
} else {
notification['error']({
message: '错误信息',
description: `用户名或者密码信息不能为空,请确认!`,
});
return false;
}
}
//点击登陆按钮 跳转到首页
_clickLoginBtn() {
const { dispatch,loginMd}=this.props;
if (this.validateFieldsAndScroll(loginMd)) {
//如果是需要记录当前用户名 则将当前用户名称记录到本地缓存中
if (parseBoolean(loginMd.checkVal)) {
let newUserName = loginMd.userName;
localStorage.removeItem("userName");
localStorage.setItem("userName", newUserName);
}
dispatch({
type: 'loginMd/loginSys'
});
}
}
//点击记住用户名的checkbox
_changeCurSkin(selSkin) {
const { dispatch,loginMd}=this.props;
dispatch({
type: 'loginMd/changeCurSkin',
curSkin: selSkin
});
}
//点击记住用户名的checkbox
_selCheckBoxEl() {
const { dispatch,loginMd}=this.props;
dispatch({
type: 'loginMd/selCheckBox',
checkVal: loginMd.checkVal
});
}
//显示皮肤选择下拉
_showDropDown(visibilityFlag){
const { dispatch,loginMd}=this.props;
dispatch({
type: 'loginMd/showDropDown',
visibilityFlag: visibilityFlag
});
}
//变更密码时 将值放入到状态里管理起来
_setPsw(ev) {
const { dispatch,loginMd}=this.props;
var newPwd = ev.target.value;
dispatch({
type: 'loginMd/changePwd',
password: newPwd
});
}
//变更用户时 将值放入到状态里管理起来
_setCurUserName(ev) {
const { dispatch,loginMd}=this.props;
var newUserName = ev.target.value;
dispatch({
type: 'loginMd/changeUserName',
userName: newUserName
});
}
render() {
const {loginMd} =this.props;
let userName = loginMd.userName;
console.log(this.props.location+"-----------------")
if (parseBoolean(loginMd.loginHome)) {
window.location.href = "http://localhost:8000/";
}
return (
<div>
<header>
<div className="loginSysImg">
<img src={topTitleLog}/>
</div>
<div className="loginSysTitle">
<span>欢迎访问ERP系统</span>
</div>
</header>
<div className="pageBody">
<div className="sysImgDiv">
<img src={sysLogo}/>
</div>
<div className="loginWin">
<div className="goingFlexRow">
<div><input type="text" ref="userName" value={userName} onChange={this._setCurUserName}/></div>
<div className="inputImg"><i className="fa fa-user" aria-hidden="true"></i></div>
</div>
<div className="goingFlexRow">
<div><input type="password" onChange={this._setPsw}/></div>
<div className="inputImg"><i className="fa fa-key" aria-hidden="true"></i></div>
</div>
<div className="goingFlexRow" onClick={(ev)=>{
this._showDropDown(true);
ev.stopPropagation();
}}>
<div>
<input type="text" value={loginMd.currentSkin} onChange={()=>{}}/>
</div>
<div className="inputImg"><i className="fa fa-caret-down" aria-hidden="true"></i></div>
</div>
<SkinDropCom skinsList={loginMd.skinsList} visibility={loginMd.visibility}
changeCurSkin={this._changeCurSkin} showDropDown={this._showDropDown} width="180px"/>
<div className="goingButtonRow">
<div className="labelInfo" onClick={()=>this._selCheckBoxEl()}>
<span><input type="checkbox" checked={parseBoolean(loginMd.checkVal)} onChange={()=>{}}/></span>
<span><label>记住用户名</label></span>
</div>
<div className="loginBtnDiv">
<button onClick={()=>this._clickLoginBtn()}>登陆</button>
</div>
</div>
</div>
</div>
<footer>
<div className="sysRight">@copy Right 科技公司版权所有</div>
</footer>
</div>
)
}
}
// 利用connect将组件与Redux绑定起来
export default connect(({loginMd}) => ({loginMd}))(Login)
login.less文件内容:
@startCol: #FAFAFA;
@endCol: #F0F0F0;
@navHeight:30px;@baseBorderCol:#CCCCCC;
header,footer{
height: @navHeight;
width: 100%;
background: linear-gradient(@startCol, @endCol); /* 标准的语法 */
display: flex;
}
header {
div.loginSysImg {
margin-left: 10px;
}
div.loginSysTitle {
margin-left: 10px;
line-height: @navHeight;
font-size: 13px;
color: #494949;
}
}
div.pageBody {
position: absolute;
width: 100%;
top: 33%;
height: 150px;
background: linear-gradient(#76CBD4, #2D93A1); /* 标准的语法 */
display: flex;
justify-content: center;
div.sysImgDiv {
position: relative;
top: -48px;
left: -10px;
}
div.loginWin {
padding: 10px 10px;
width: 200px;
div.goingFlexRow {
width: 100%;
margin-top: 6px;
display: flex;
}
div.goingButtonRow {
display: flex;
justify-content: space-between;
margin-top: 10px;
.labelInfo {
position: relative;
top: 5px;
label {
position: relative;
top: -3px;
left: 5px;
color: #fff;
display: inline-block;
}
}
.loginBtnDiv {
cursor: pointer;
position: relative;
button {
width: 70px;
height: 25px;
line-height: 25px;
cursor: pointer;
&:hover {
background: linear-gradient(#DDDDDD, #C9C9C9); /* 标准的语法 */
}
}
}
}
}
}
footer {
position: fixed;
bottom: 0px;
border-top: 1px solid @baseBorderCol;
.sysRight {
line-height:@navHeight;
margin-left: 10px;
font-size: 13px;
}
}
.inputImg {
position: relative;
left: -16px;
top: 3px;
}
.inputCom{
height: 23px;
width: 180px;
border: 1px solid @baseBorderCol;
border-radius: 3px;
}
input[type="text"] {
.inputCom;
}
input[type="password"] {
.inputCom;
}
input[type="checkbox"] {
height: 15px;
width: 15px;
}
dva对redux封装了,所以使用起来不需要单独去写所谓的action,以及reducer相关文件。
Login.js直接关联到了src/models/LoginMd.js.
如何关联的:第一步就是新建一个LoginMd.js,并将该models注册到index.js里面
app.model(require('./models/LoginMd'));
第二步,在Login.js文件开发时,将该文件对应的对象映射进来:
// 利用connect将组件与Redux绑定起来
export default connect(({loginMd}) => ({loginMd}))(Login)
这样就完成了redux相关的各种配置了,确实要简单多了。
src/models/LoginMd.js的内容如下
import {loginSv, findCurrentUser, logout} from '../services/LoginSv'
import {parse} from 'qs'
import {parseBoolean,AssertUtils} from '../utils/GoingUtils';
export default {
namespace: 'loginMd',
state: {
//从本地缓存中读取是否选中的状态值
checkVal:((curCheckVal=false)=>{
return curCheckVal;
})(localStorage.getItem("checkVal")),
userName:(()=>{
//如果记住用户名是true 则本地缓存中获取用户信息
if(parseBoolean(localStorage.getItem("checkVal"))){
return localStorage.getItem("userName");
}else{
return '';
}
})(),
visibility:"hidden",
currentSkin:((currentSkin='蓝色金典')=>{
return currentSkin;
})(localStorage.getItem("currentSkin")||'蓝色金典'),
//皮肤下拉
skinsList: [
{text: '蓝色金典',itemId:'blue'},
{text: '浅色简约',itemId:'apply'},
{text: '简约时尚',itemId:'shisha'}
]
},
subscriptions: {
setup({ dispatch, history }) { // eslint-disable-line
},
},
effects: {
*loginSys({ payload }, { call, put }) {
const record = yield call(loginSv, parse(payload));
if (record.code) {
yield put({
type: 'loginSuccess',
payload: {
user: record.data
}})
} else {
yield put({
type: 'loginFail'
})
}
},
},
reducers: {
selCheckBox(state, action) {
//讲checkVal的值保存到本地缓存中
localStorage.removeItem("checkVal");
localStorage.setItem("checkVal",!parseBoolean(action.checkVal));
return { ...state, checkVal:!parseBoolean(action.checkVal)};
},
changeUserName(state, action) {
//用户名输入的值变更
return { ...state, userName:action.userName};
},
changePwd(state, action) {
//密码值的变更
return { ...state, password:action.password};
},
showDropDown(state, action) {
//皮肤下拉的显示与隐藏
return { ...state, visibility:parseBoolean(action.visibilityFlag)?"visible":"hidden"};
},
changeCurSkin(state, action) {
//选择相应的皮肤选项
localStorage.removeItem("currentSkin");
localStorage.setItem("currentSkin",action.curSkin);
return { ...state, visibility:"hidden",currentSkin:action.curSkin};
},
loginSuccess (state, action) {
//登陆成功
return {
...state,
...action.payload,
loginHome: true,
loginButtonLoading: false
}
},
loginFail (state) {
return {
...state,
login: false,
error:'用户名或者密码不正确',
loginButtonLoading: false
}
},
},
};
调用原理:
当在Login.js文件中调用:
dispatch({
type: 'loginMd/changePwd',
password: newPwd
});
loginMd/changePwd中的loginMd对应的是Models的命名空间
namespace: 'loginMd',
loginMd/changePwd中的changePwd对应reducers下的相应方法
changePwd(state, action) {
//密码值的变更
return { ...state, password:action.password};
},
到此就完成了登陆界面的开发。该界面里面用到了一个封装组件,皮肤下拉,下节我们来介绍该组件的实现。