天天看點

react項目實戰(權限子產品開發三) 登陸界面開發

首先上效果圖:

react項目實戰(權限子產品開發三) 登陸界面開發

選擇皮膚下拉。

react項目實戰(權限子產品開發三) 登陸界面開發

功能分析:

  • 點選記住使用者名區域,checkbox選中,再點選則checkbox取消選中。
  • 點選皮膚選擇框,會顯示皮膚下拉。
  • 如果記住使用者勾選了,則從本地緩存中讀取上次登陸的使用者名稱。
  • 如果記住使用者勾選了,則點選登陸送出時,需要将目前使用者的名稱儲存到本地緩存中。
  • 點選皮膚選擇框,彈出皮膚選擇下拉,但點選界面其他地方時将下拉框隐藏。
  • 點選登陸按鈕時請求背景資料,如果登陸成功跳轉界面。
  • 如果登陸失敗則彈出錯誤提醒框。

代碼存放位置:

react項目實戰(權限子產品開發三) 登陸界面開發

直接上代碼了:

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:px;@baseBorderCol:#CCCCCC;

header,footer{
  height: @navHeight;
  width: %;
  background: linear-gradient(@startCol, @endCol); /* 标準的文法 */
  display: flex;
}
header {
  div.loginSysImg {
    margin-left: px;
  }
  div.loginSysTitle {
    margin-left: px;
    line-height: @navHeight;
    font-size: px;
    color: #494949;
  }
}

div.pageBody {
  position: absolute;
  width: %;
  top: %;
  height: px;
  background: linear-gradient(#76CBD4, #2D93A1); /* 标準的文法 */
  display: flex;
  justify-content: center;
  div.sysImgDiv {
    position: relative;
    top: -px;
    left: -px;
  }
  div.loginWin {
    padding: px px;
    width: px;
    div.goingFlexRow {
      width: %;
      margin-top: px;
      display: flex;
    }
    div.goingButtonRow {
      display: flex;
      justify-content: space-between;
      margin-top: px;
      .labelInfo {
        position: relative;
        top: px;
        label {
          position: relative;
          top: -px;
          left: px;
          color: #fff;
          display: inline-block;
        }
      }
      .loginBtnDiv {
        cursor: pointer;
        position: relative;
        button {
          width: px;
          height: px;
          line-height: px;
          cursor: pointer;
          &:hover {
            background: linear-gradient(#DDDDDD, #C9C9C9); /* 标準的文法 */
          }
        }

      }
    }
  }
}

footer {
  position: fixed;
  bottom: px;
  border-top: px solid @baseBorderCol;
  .sysRight {
    line-height:@navHeight;
    margin-left: px;
    font-size: px;
  }

}

.inputImg {
  position: relative;
  left: -px;
  top: px;
}

.inputCom{
  height: px;
  width: px;
  border: px solid @baseBorderCol;
  border-radius: px;
}
input[type="text"] {
  .inputCom;
}

input[type="password"] {
  .inputCom;
}

input[type="checkbox"] {
  height: px;
  width: px;
}

           

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的命名空間

loginMd/changePwd中的changePwd對應reducers下的相應方法

changePwd(state, action) {
      //密碼值的變更
      return { ...state, password:action.password};
    },
           

到此就完成了登陸界面的開發。該界面裡面用到了一個封裝元件,皮膚下拉,下節我們來介紹該元件的實作。

具體細節不講了,有興趣的可以q我,一起探讨學習