React + Koa2打造『官方管理后台』4

十.建立数据请求模型以及完成前端登录逻辑

1.后端的Service中及控制器中

 return ctx.body = returnInfo(LOGIN.SUCCESS,result)
 
 const uid = usernameExist.get('id');
        return {
            uid,
            username
        };

2.安装qs序列化data

npm i qs -S

3.在utils中新建http.js封装axios

import axios from 'axios';
import qs from 'qs';
export default class HTTP{
    axiosPost(options){
        axios({
            url:options.url,
            method:'post',
            header:{
                'Content-Type':'application/x-www-form-urlencoded'
            },
            data: qs.stringify(options.data)
        }).then((res)=>{
            options.success(res.data)
        }).catch((err)=>{
            options.error(err)
        })
    }
    axiosGet(options){
        axios({
            url:options.url,
            method:'get',
        }).then((res)=>{
            options.success(res.data)
        }).catch((err)=>{
            options.error(err)
        })
    }
}

4.在src中建立config文件夹,且创建config.js

const BASE_URL = "http://localhost:3000/admin/";
const API ={
    LOGIN_ACTION: BASE_URL+'login_action'
}
export{
    API
}

5.在service中新建login.js

import HTTP from 'utils/http';
import {API} from '../config/config';

export default class LoginService extends HTTP{
    loginAction(userInfo){
        return new Promise((resolve,reject)=>{
            this.axiosPost({
                url:API.LOGIN_ACTION,
                data:userInfo,
                success(data){
                    resolve(data)
                },
                error(error){
                    alert('网络请求失败!');
                    window.location.reload();
                }
            })
        })
    }
}

6.删除Login下Index.js中的测试代码

7.在utils文件夹中建立tools.js

function trimSpace(str){
    return str.replace(/\s+/g,'')
}
export{
    trimSpace
}

8.loginfrom中引入

import React, { Component } from 'react'
import LoginService from 'services/login'
import {trimSpace} from 'utils/tools'

import './index.scss'

const loginService = new LoginService();
export default class LoginForm extends Component {
    constructor(props){
        super(props);
        this.state={
            username:'',
            password:''
        }
    }
    onInputTyping(e){
        const id = e.target.id,
              val = e.target.value;
        this.setState({
            [id]:val
        })      
    }
    async onLoginSubmit(e){
        const {username,password} = this.state;
        if(trimSpace(username).length<=0){
            alert('用户名长度不正确')
            return
        }
        if(trimSpace(password).length<=0){
            alert('密码长度不正确')
            return
        }
        const result = await loginService.loginAction({
            username:trimSpace(username),
            password:trimSpace(password)
        })
        const errorCode = result.error_code,
              errorMsg = result.error_msg;
        if(errorCode!==0){
            alert(errorMsg+'(errorCode:'+errorCode+')')
            return
        }     
        alert('登录成功') 
    }
    render() {
        return (
            <div className="login-form-wrapper">
                <div className="input-box">
                    <label htmlFor="username" className="iconfont icon-user">&#xe623;</label>
                    <input 
                    type="text" 
                    id="username"
                    className="login-input" 
                    placeholder="用户名"
                    onChange={(e)=>this.onInputTyping(e)}
                    ></input>
                </div>
                <div className="input-box">
                    <label htmlFor="password" className="iconfont icon-lock">&#xe7b8;</label>
                    <input 
                    type="password"
                    id='password'
                    className="login-input" 
                    placeholder="密码"
                    onChange={(e)=> this.onInputTyping(e)}
                    ></input>
                </div>
                <div className="input-box">
                    <button className="btn btn-primary"
                    onClick={(e)=>this.onLoginSubmit(e)}
                    >登录后台</button>
                </div>
            </div>
        )
    }
}

输入后点击提交测试

9.登录成功后跳转

在pages中的login.js中

import React, { Component } from 'react'
import Login from "../components/Login"
export default class LoginPage extends Component {
    constructor(props){
        super(props)
        this.state={}
        
    }
    render() {
        const {history} = this.props
        return (
            <div className="container">
                <Login history={history}/>
            </div>
        )
    }
}

在components中的login/index.js中继续传递

import React, { Component } from 'react'

import Logo from './Logo'
import Form from './Form'
import './index.scss'
export default class Login extends Component {
   
    render() {
        const {history} = this.props;
        return (
            <div className="login-container">
                <Logo/>
                <Form history={history}/>
            </div>
        )
    }
}

在from中的index.js中继续传递

import React, { Component } from 'react'
import './index.scss'
import Title from './Title'
import LoginForm from './LoginForm'
export default class Form extends Component {
    render() {
        const {history} = this.props;
        return (
            
            <div className="form-wrapper">
                <Title/>
                <LoginForm history={history}/>
            </div>
        )
    }
}

在loginfrom中跳转

 const {history} = this.props;
        alert('登录成功') 
        history.push('/')

十一.登录验证、跨域设置cookie以及路由跳转

1.后台的控制器中Admin.js存取session

if(!ctx.session.userInfo){
            ctx.session.userInfo = result
        }
        return ctx.body = returnInfo(LOGIN.SUCCESS,ctx.session.userInfo)

检测是否在redis中存储成功

2.增加路由,检查登录态

const router = require('koa-router')(),
adminController = require('../controller/admin')
router.prefix('/admin')
router.get('/create_admin',adminController.createAdmin)
router.post('/login_action',adminController.loginAction)
router.get('/login_check',adminController.loginCheck)
module.exports = router;

3.err_config.js中新增状态

 NOT_LOGIN_STATUS:{
            error_code:10006,
            error_msg:'It is not loged status'
        },
        LOGIN_STATUS:{
            error_code:10007,
            error_msg:'It is loged status'
        }

4.控制器中admin.js

 async loginCheck(ctx,next){
        if(ctx.session && ctx.session.userInfo){
            ctx.body = returnInfo(LOGIN.LOGIN_STATUS)
            return
        }
        ctx.body = returnInfo(LOGIN.NOT_LOGIN_STATUS)
    }

5.前端中的config.js

const BASE_URL = "http://localhost:3000/admin/";
const API ={
    LOGIN_ACTION: BASE_URL+'login_action',
    LOGIN_CHECK:BASE_URL+'login_check'
}
export{
    API
}

6.service中的login.js

loginCheck(){
        return new Promise((resolve,reject)=>{
            this.axiosGet({
                url:API.LOGIN_CHECK,
                success(data){
                    resolve(data)
                },
                error(error){
                    alert('网路请求失败')
                    window.location.reload
                }
            })
        })
    }

去除loginAction的reload

7.在loginform的index.js中

async loginCheck(){
        
        const result = await loginService.loginCheck();
        const errorCode = result.error_code;
        if(errorCode === 10007){
            const {history} = this.props;
            history.push('/')
        }
    }
    componentDidMount(){
        this.loginCheck();
    }

8.携带资质解决cookie跨域存储问题

前端的util中的http.js

import axios from 'axios';
import qs from 'qs';
export default class HTTP{
    axiosPost(options){
        axios({
            url:options.url,
            method:'post',
            withCredentials:true,
            header:{
                'Content-Type':'application/x-www-form-urlencoded'
            },
            data: qs.stringify(options.data)
        }).then((res)=>{
            options.success(res.data)
        }).catch((err)=>{
            options.error(err)
        })
    }
    axiosGet(options){
        axios({
            url:options.url,
            withCredentials:true,
            method:'get',
        }).then((res)=>{
            options.success(res.data)
        }).catch((err)=>{
            options.error(err)
        })
    }
}

后端的app.js

app.use(cors({
  origin:function(ctx){
    return corsOrigin
  },
  credentials:true
}))

再进行测试,即可实现检测登录态如果有则不显示登录页

十二.管理首页登录验证、Header组件、退出登录

1.pages下的index.js首页登录验证

 async loginCheck(){
        const result = await loginservice.loginCheck();
        const errorCode = result.error_code;
        if(errorCode === 10006){
            const {history} = this.props;
            history.push('/login')
        }
    }
    componentDidMount(){
        this.loginCheck();
    }

app.js传递history

import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom'
import { Route, Switch, NavLink, Link } from 'react-router-dom'
import IndexPage from './pages/index'
import LoginPage from './pages/login'

import DetailPage from './pages/sub/detail'
import ListPage from './pages/sub/list'
function App() {
  return (
    <Router>
      <Switch>
        <Route component={LoginPage} path="/login"></Route>
        <Route  path="/" render={ props =>(
          <IndexPage history={props.history}>
            <Switch>
            <Route component={ ListPage } path="/sub/list"></Route>
            <Route component={DetailPage} path="/sub/detail"></Route>
           
            </Switch>
          </IndexPage>
        )}/>
      </Switch>
    </Router>
  );
}

export default App;

删除cookie测试是否能跳转至首页

2.在components中建立index 再建立Header文件夹,建立index.js index.scss

index.js

import React, { Component } from 'react'
import './index.scss'
export default class Header extends Component {
    render() {
        return (
            <header className="header"></header>
        )
    }
}

index.scss

.header{
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    min-width: 1200px;
    height: 60px;
    padding:0 15px;
    background-color: #fff;
    border-bottom: 1px solid #ddd;
    box-sizing: border-box;
}

3.assets中的scss中的common.js中隐去滚动条

html,body,#root,.container{
    height: 100%;
}

4.在pages中的index.js文件

import Header from '../components/Index/Header'
<div className="container">
              <Header/>
            </div>

5.在header中写三个子组件

logo

import React, { Component } from 'react'
import './index.scss'
export default class HeaderLogo extends Component {
    render() {
        return (
            <div className="header-logo-wrapper">
                <a href="/" className="logo-link"></a>
            </div>
        )
    }
}
.header-logo-wrapper{
    float: left;
    height: 100%;
    .logo-link{
        display: inline-block;
        width: 44px;
        height: 44px;
        margin:8px 10px 0 0;
        background:url('../../../../assets/img/logo.png') no-repeat;
        background-size: 44px 44px;
    }
}

title

import React, { Component } from 'react'
import './index.scss'
export default class HeaderTitle extends Component {
    render() {
        return (
           <h1 className="header-title">海儿科技官方网站管理后台</h1>
        )
    }
}

.header-title{
    float: left;
    line-height: 60px;
    font-size: 20px;
}

logoout

import React, { Component } from 'react'
import './index.scss'
export default class Logout extends Component {
    onLogoutClick(){}
    render() {
        return (
           <span className="header-logout" onClick={()=>this.onLogoutClick()}>安全退出</span>
        )
    }
}
.header-logout{
    float: right;
    line-height: 60px;
    font-size: 14px;
    cursor:pointer
}

引入

import React, { Component } from 'react'

import HeaderLogo from './Logo';
import HeaderTitle from './Title';
import HeaderLogout from './Logout'
import './index.scss'
export default class Header extends Component {
    render() {
        return (
            <header className="header">
                <HeaderLogo></HeaderLogo>
                <HeaderTitle></HeaderTitle>
                <HeaderLogout></HeaderLogout>
            </header>
        )
    }
}

6.在后台中书写路由

const router = require('koa-router')(),
adminController = require('../controller/admin')
router.prefix('/admin')
router.get('/create_admin',adminController.createAdmin)
router.post('/login_action',adminController.loginAction)
router.get('/login_check',adminController.loginCheck)
router.get('/loginout_action',adminController.logoutAction)
module.exports = router;

7.在config的http.js中配置访问路径

const BASE_URL = "http://localhost:3000/admin/";
const API ={
    LOGIN_ACTION: BASE_URL+'login_action',
    LOGIN_CHECK:BASE_URL+'login_check',
    LOGOUT_ACTION:BASE_URL+'logout_action'
}
export{
    API
}

8..在service的login.js中

logoutAction(){
        return new Promise((resolve,reject)=>{
            this.axiosGet({
                url:API.LOGOUT_ACTION,
                success(data){
                    resolve(data)
                },
                error(error){
                    alert('网络请求失败')
                    window.location.reload();
                }
            })
        })
    }

9.传递history,index.js中

render() {
        const {history} = this.props
        return (
            <div className="container">
              <Header history={history}/>
            </div>
        )
    }
    
    import React, { Component } from 'react'

import HeaderLogo from './Logo';
import HeaderTitle from './Title';
import HeaderLogout from './Logout'
import './index.scss'
export default class Header extends Component {
    render() {
        const {history} = this.props;
        return (
            <header className="header">
                <HeaderLogo></HeaderLogo>
                <HeaderTitle></HeaderTitle>
                <HeaderLogout history={history}></HeaderLogout>
            </header>
        )
    }
}

10.修改Loginout

import React, { Component } from 'react'

import loginService from '../../../../services/login'
import './index.scss'

const loginService = new LoginService();
export default class Logout extends Component {
    async onLogoutClick(){
        const cfm = window.confirm('确定退出登录吗?')
        if(cfm){
            const result= await loginService.logoutAction();
            const errorCode = result.error_code;
            if(errorCode === 0){
                const {history} = this.props;
                history.push('/login')
            }
        }
    }
    render() {
        return (
           <span className="header-logout" onClick={()=>this.onLogoutClick()}>安全退出</span>
        )
    }
}

11.在err_confing.js中

 LOGOUT_SUCCESS:{
            error_code:0,
            error_msg:'Logout is ok'
        },

12.在admin的控制器中

 async logoutAction(ctx,next){
        delete ctx.session.userInfo;
        ctx.body = returnInfo(LOGIN.LOGOUT_SUCCESS)
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值