实战

上接,笔记:https://blog.csdn.net/u010132177/article/details/104150177

https://gitee.com/pasaulis/react-guli

1)创建目录

src 目录下
api ajax相关
assets 公用资源
components 非路由组件
config 配置
pages 路由组件
utils 工具模块
Appj.s 应用根组件
index.js 入口js

cmd指创建:

mkdir api assets components config pages utils

2)配置路由、引入antd

https://blog.csdn.net/u010132177/article/details/103344017

3)重置css样式

在public下新建css目录,放入如下文件reset.css,并在index.html里引入

 <link rel="stylesheet" href="/css/reset.css">
/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */
html,
body,
p,
ol,
ul,
li,
dl,
dt,
dd,
blockquote,
figure,
fieldset,
legend,
textarea,
pre,
iframe,
hr,
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
padding: 0;
} h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 100%;
font-weight: normal;
} ul {
list-style: none;
} button,
input,
select,
textarea {
margin: 0;
} html {
box-sizing: border-box;
} *, *::before, *::after {
box-sizing: inherit;
} img,
video {
height: auto;
max-width: 100%;
} iframe {
border: 0;
} table {
border-collapse: collapse;
border-spacing: 0;
} td,
th {
padding: 0;
} td:not([align]),
th:not([align]) {
text-align: left;
}

4)用用axios编写ajax请求组件目录[src/api/]

1.ajax.js主要为用axios写异步的get,post请求最基础部分

import axios from 'axios'

export default function ajax(url,data={},type='GET'){
if(type==='GET'){
return axios.get(url,{
params:data
})
} else {
return axios.post(url,data)
}
}

2.index.js主要为调用ajax组件编写各个对应接口的请求函数的两种写法

import ajax from './ajax'

// const BASE = 'http://localhost:5000'
const BASE = '' //【1】导出一个函数,第1种写法
//登录接口函数
// export function reqLogin(username,password){
// return ajax('login',{username,password},'POST')
// } //【2】导出一个函数,第2种写法
// 登录接口函数
export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST') //添加用户接口
export const AddUser=(user)=>ajax(BASE+'/manage/user/add',user,'POST')

3.开发环境配置代理接口,用于处理跨域请求package.json

  • 如果不添加将无法跨域,显示为404 not found
  • 运行环境将用另一种方法配置代理接口
  "development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
//最下面添加此句即可
"proxy":"http://localhost:5000"
}

配置修改后记录重启项目才有用,ctrl+c、npm start

5)写页面:page/login/login.jsx

1.编写页面基本样式

2.使用antd的form登录组件

3.编写登录组件的本地验证

4.用axios编写ajax请求

import React,{Component} from 'react'
import login from '../../assets/images/logo.png'
import './login.less'
import { Form, Icon, Input, Button, Checkbox } from 'antd';
import {reqLogin} from '../../api/' //因为api文件夹下有index.js所以只要指定到文件夹即可 class Login extends Component{
constructor(props){
super(props);
} //点提交按钮后的操作
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {//如果本地验证不存在错误,即正确返回
//console.log('在此处发起axios请求验证,发送用户名,密码给服务器,即:', values);
const {username,password}=values //解构本地values给username,password,用于发送给服务器
//调用src/api/index.js的ajax登录请求,发送数据
reqLogin(username,password).then(response=>{//处理正常响应
console.log(response.data)
}).catch(err=>{//处理出错信息
console.log(err)
})
}else{
console.log('验证失败')
}
});
}; // 密码校验
validatePwd=(rule,value,callback)=>{
console.log('validatePwd()', rule, value)
if(!value){
callback('密码必须输入!')
}else if(value.length<4){
callback('密码必须大于4位')
}else if(value.length>12){
callback('密码不能超过12位')
}else if(!/^[a-zA-Z0-9_]+$/.test(value)){
callback('密码必须由字母、数字、下划线组成')
}else{
callback() //本地验证成功
}
} render(){
//const form = this.props.form
//const { getFieldDecorator }=form
const {getFieldDecorator}=this.props.form //以上两句合二为一 return(
<div className='login'> <header className='login-header'>
<img src={login} />
<h1>深蓝后台管理系统</h1>
</header> <section className='login-content'>
<h2>用户登录</h2>
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
{
getFieldDecorator('username',{
rules:[
{required:true,whitespace:true,message:'用户名必须输入!'},
{min:4,message:'用户名必须大于4位'},
{max:12,message:'用户名最多只能12位'},
{pattern:/^[a-zA-Z0-9_]+$/,message:'用户名只能是字母、数字、下划线'}
],
//initialValue:'admin' //默认显示值
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="用户名"
/>)
} </Form.Item>
<Form.Item>
{
getFieldDecorator('password',{
rules:[
{ validator: this.validatePwd}
]
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="密码"
/>)
} </Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
登录
</Button>
</Form.Item>
</Form>
</section> </div>
)
}
}
const WrapLogin = Form.create()(Login)
export default WrapLogin

5.写样式src/login/login.less

.login{
background: #fff url('./images/bg.jpg') ;
background-size: 100% 100%;
width:100%;
height: 100%; .login-header{
display: flex;
align-items: center;
height: 80px;
background-color: rgba(21, 20, 13, 0.5);
img{
width: 40px;
height: 40px;
margin: 0 15px 0 50px;
}
h1{
font-size: 30px;
color: #fff;
margin: 0px;
}
} .login-content{
width: 400px;
height: 300px;
background-color: rgba(255, 255, 255, 0.7);
margin: 50px auto;
padding: 20px 40px; h2{
text-align: center;
font-size: 24px;
margin-bottom: 20px;
} .login-form-button{
width: 100%;
}
}
}

6)简单登录

1.src/api/ajax.js

import axios from 'axios'
import {message} from 'antd' export default function ajax(url, data={}, type='GET') { return new Promise((resolve, reject) => {
let promise
// 1. 执行异步ajax请求
if(type==='GET') { // 发GET请求
promise = axios.get(url, { // 配置对象
params: data // 指定请求参数
})
} else { // 发POST请求
promise = axios.post(url, data)
}
// 2. 如果成功了, 调用resolve(value)
promise.then(response => {
resolve(response.data)
// 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
}).catch(error => {
// reject(error)
message.error('请求出错了: ' + error.message)
})
}) }

2.src/api/index.js

import ajax from './ajax'

// const BASE = 'http://localhost:5000'
const BASE = '' //【1】导出一个函数,第1种写法
//登录接口函数
// export function reqLogin(username,password){
// return ajax('login',{username,password},'POST')
// } //【2】导出一个函数,第2种写法
// 登录接口函数
export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST') //添加用户接口
export const AddUser=(user)=>ajax(BASE+'/manage/user/add',user,'POST')

3.src/pages/login/login.jsx

/*
能发送异步ajax请求的函数模块
封装axios库
函数的返回值是promise对象
1. 优化1: 统一处理请求异常?
在外层包一个自己创建的promise对象
在请求出错时, 不reject(error), 而是显示错误提示
2. 优化2: 异步得到不是reponse, 而是response.data
在请求成功resolve时: resolve(response.data)
*/ import axios from 'axios'
import {message} from 'antd' export default function ajax(url, data={}, type='GET') {
return new Promise((resolve, reject) => {
let promise
// 1. 执行异步ajax请求
if(type==='GET') { // 发GET请求
promise = axios.get(url, { // 配置对象
params: data // 指定请求参数
})
} else { // 发POST请求
promise = axios.post(url, data)
}
// 2. 如果成功了, 调用resolve(value)
promise.then(response => {
resolve(response.data)
// 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
}).catch(error => {
// reject(error)
message.error('请求出错了: ' + error.message)
})
}) } // 请求登陆接口
// ajax('/login', {username: 'Tom', passsword: '12345'}, 'POST').then()
// 添加用户
// ajax('/manage/user/add', {username: 'Tom', passsword: '12345', phone: '13712341234'}, 'POST').then()

4.其它src/app.js路由部分

import React,{Component} from 'react'
import {BrowserRouter,Route,Switch} from 'react-router-dom'
import Admin from './pages/admin/admin'
import Login from './pages/login/login' class App extends Component{
constructor(props){
super(props);
} render(){
return(
<BrowserRouter>
<Switch>
<Route path='/login' component={Login} />
<Route path='/' component={Admin} />
</Switch>
</BrowserRouter>
)
}
}
export default App

5.src/index.js

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App' ReactDOM.render(<App/>,document.getElementById('root'))

7)登录功能完善 保存登录状态

localStrage的第三方库store:https://github.com/marcuswestin/store.js

库好处:

  1. 兼容所有浏览器
  2. 自动把数据解析为字典格式

1.src/utils/storageUtils.js

  1. 编写函数用于保存用户名到localstorage里去
  2. 从localSorage读取user
  3. 从localStorage删除user
/*
保存用户名到localStorage
*/
import store from 'store'
const USER_KEY='user_key' //定义localStorage内的键名为user_key export default{
//1.保存user到localStorage
saveUser(user){
//localStorage.setItem(USER_KEY,JSON.stringify(user)) //原生localStorage写法,下同
store.set(USER_KEY,user) //store库写法,自动把user解析为字典
}, //2.从localSorage读取user
getUser () {
// return JSON.parse(localStorage.getItem(USER_KEY)||'{}') //如果没有得到数据,就返回空字典
return store.get(USER_KEY) || {}
}, //3.从localStorage删除user
removeUser (){
//localStorage.removeItem(USER_KEY)
store.remove(USER_KEY)
}
}

2. src/utils/memoryUtils.js

/*
用于在内存中保存数据的工具模块
*/
export default{
user:{},
}

3. src/app.js

【1】引入模块

【2】读取local中保存user, 保存到内存中

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App' import memoryUtils from './utils/memoryUtils' //引入【1】
import storageUtils from './utils/storageUtils' // 【2】读取localstorage中保存的user, 保存到内存中,用于login.jsx页面读取是否登录
const user = storageUtils.getUser()
memoryUtils.user = user ReactDOM.render(<App/>,document.getElementById('root'))

4.src/pages/login/login.jsx

【1】如果用户已经登陆, 自动跳转到管理界面

import React, {Component} from 'react'
import {Redirect} from 'react-router-dom'
import {
Form,
Icon,
Input,
Button,
message
} from 'antd'
import './login.less'
import logo from '../../assets/images/logo.png'
import {reqLogin} from '../../api'
import memoryUtils from '../../utils/memoryUtils'
import storageUtils from '../../utils/storageUtils' const Item = Form.Item // 不能写在import之前 /*
登陆的路由组件
*/
class Login extends Component { handleSubmit = (event) => { // 阻止事件的默认行为
event.preventDefault() // 对所有表单字段进行检验
this.props.form.validateFields(async (err, values) => {
// 检验成功
if (!err) {
// console.log('提交登陆的ajax请求', values)
// 请求登陆
const {username, password} = values
const result = await reqLogin(username, password) // {status: 0, data: user} {status: 1, msg: 'xxx'}
// console.log('请求成功', result)
if (result.status===0) { // 登陆成功
// 提示登陆成功
message.success('登陆成功') // 保存user
const user = result.data
memoryUtils.user = user // 保存在内存中
storageUtils.saveUser(user) // 保存到local中 // 跳转到管理界面 (不需要再回退回到登陆)
this.props.history.replace('/') } else { // 登陆失败
// 提示错误信息
message.error(result.msg)
} } else {
console.log('检验失败!')
}
}); // 得到form对象
// const form = this.props.form
// // 获取表单项的输入数据
// const values = form.getFieldsValue()
// console.log('handleSubmit()', values)
} /*
对密码进行自定义验证
*/
/*
用户名/密码的的合法性要求
1). 必须输入
2). 必须大于等于4位
3). 必须小于等于12位
4). 必须是英文、数字或下划线组成
*/
validatePwd = (rule, value, callback) => {
console.log('validatePwd()', rule, value)
if(!value) {
callback('密码必须输入')
} else if (value.length<4) {
callback('密码长度不能小于4位')
} else if (value.length>12) {
callback('密码长度不能大于12位')
} else if (!/^[a-zA-Z0-9_]+$/.test(value)) {
callback('密码必须是英文、数字或下划线组成')
} else {
callback() // 验证通过
}
// callback('xxxx') // 验证失败, 并指定提示的文本
} render () { // 【1】如果用户已经登陆, 自动跳转到管理界面
const user = memoryUtils.user
if(user && user._id) {
return <Redirect to='/'/>
} // 得到具强大功能的form对象
const form = this.props.form
const { getFieldDecorator } = form; return (
<div className="login">
<header className="login-header">
<img src={logo} alt="logo"/>
<h1>React项目: 后台管理系统</h1>
</header>
<section className="login-content">
<h2>用户登陆</h2>
<Form onSubmit={this.handleSubmit} className="login-form">
<Item>
{
/*
用户名/密码的的合法性要求
1). 必须输入
2). 必须大于等于4位
3). 必须小于等于12位
4). 必须是英文、数字或下划线组成
*/
}
{
getFieldDecorator('username', { // 配置对象: 属性名是特定的一些名称
// 声明式验证: 直接使用别人定义好的验证规则进行验证
rules: [
{ required: true, whitespace: true, message: '用户名必须输入' },
{ min: 4, message: '用户名至少4位' },
{ max: 12, message: '用户名最多12位' },
{ pattern: /^[a-zA-Z0-9_]+$/, message: '用户名必须是英文、数字或下划线组成' },
],
initialValue: 'admin', // 初始值
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="用户名"
/>
)
}
</Item>
<Form.Item>
{
getFieldDecorator('password', {
rules: [
{
validator: this.validatePwd
}
]
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="密码"
/>
)
} </Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="login-form-button">
登陆
</Button>
</Form.Item>
</Form>
</section>
</div>
)
}
} /*
1. 高阶函数
1). 一类特别的函数
a. 接受函数类型的参数
b. 返回值是函数
2). 常见
a. 定时器: setTimeout()/setInterval()
b. Promise: Promise(() => {}) then(value => {}, reason => {})
c. 数组遍历相关的方法: forEach()/filter()/map()/reduce()/find()/findIndex()
d. 函数对象的bind()
e. Form.create()() / getFieldDecorator()()
3). 高阶函数更新动态, 更加具有扩展性 2. 高阶组件
1). 本质就是一个函数
2). 接收一个组件(被包装组件), 返回一个新的组件(包装组件), 包装组件会向被包装组件传入特定属性
3). 作用: 扩展组件的功能
4). 高阶组件也是高阶函数: 接收一个组件函数, 返回是一个新的组件函数
*/
/*
包装Form组件生成一个新的组件: Form(Login)
新组件会向Form组件传递一个强大的对象属性: form
*/
const WrapLogin = Form.create()(Login)
export default WrapLogin
/*
1. 前台表单验证
2. 收集表单输入数据
*/ /*
async和await
1. 作用?
简化promise对象的使用: 不用再使用then()来指定成功/失败的回调函数
以同步编码(没有回调函数了)方式实现异步流程
2. 哪里写await?
在返回promise的表达式左侧写await: 不想要promise, 想要promise异步执行的成功的value数据
3. 哪里写async?
await所在函数(最近的)定义的左侧写async
*/

5.src/pages/admin/admin.jsx

import React,{Component} from 'react'
import {Redirect} from 'react-router-dom'
import memoryUtils from '../../utils/memoryUtils' class Admin extends Component{
constructor(props){
super(props);
} render(){
//【1】如果memoryUtils中的user对象不存在(未登录),则跳转到登录页面
const user=memoryUtils.user
if(!user || !user._id){
return <Redirect to='/login'/>
}
return(
<div>
Admin
</div>
)
}
}
export default Admin

6.效果http://localhost:3000

admin admin

输入错误则提示,成功则跳转到/admin页面





f12打开application,把localstorage里的user_key再刷新即会跳转到登录页面

《React后台管理系统实战 :一》:目录结构、引入antd、引入路由、写login页面、使用antd的form登录组件、form前台验证、高阶函数/组件的更多相关文章

  1. 《React后台管理系统实战 零》:基础笔记

    day01 1. 项目开发准备 1). 描述项目 2). 技术选型 3). API接口/接口文档/测试接口 2. 启动项目开发 1). 使用react脚手架创建项目 2). 开发环境运行: npm s ...

  2. 《React后台管理系统实战 :二》antd左导航:cmd批量创建子/目录、用antd进行页面布局、分离左导航为单独组件、子路由、动态写左导航、css样式相对陷阱

    一.admin页面布局及路由创建 0)cmd批量创建目录及子目录 //创建各个目录,及charts和子目录bar md home category product role user charts\b ...

  3. 1.函数的结构,调用,传参,形参,实参,args,kwargs,名称空间,高阶函数

    1.函数的初识 初始函数 获取任意一个字符串的元素的个数 s1='dsjdkjkfefenga' count=0 for i in s1: count+=1 print(count) 获取列表的元素的 ...

  4. 《React后台管理系统实战 :四》产品分类管理页:添加产品分类、修改(更新)产品分类

    一.静态页面 目录结构 F:\Test\react-demo\admin-client\src\pages\admin\category add-cate-form.jsx index.jsx ind ...

  5. 《React后台管理系统实战 :三》header组件:页面排版、天气请求接口及页面调用、时间格式化及使用定时器、退出函数

    一.布局及排版 1.布局src/pages/admin/header/index.jsx import React,{Component} from 'react' import './header. ...

  6. react高阶函数组件

    Layout as a Higher Order Component // components/MyLayout.js import Header from './Header'; const la ...

  7. react_结合 redux - 高阶函数 - 高阶组件 - 前端、后台项目打包运行

    Redux 独立的集中式状态管理 js 库 - 参见 My Git 不是 react 库,可以与 angular.vue 配合使用,通常和 react 用 yarn add redux import ...

  8. React.js高阶函数的定义与使用

    /* 高阶函数的简单定义与使用 一: 先定义一个普通组件 二: 用function higherOrder(WrappendComponent) { return } 将组件包裹起来,并用export ...

  9. Kotlin高阶函数实战

    前言 1. 高阶函数有多重要? 高阶函数,在 Kotlin 里有着举足轻重的地位.它是 Kotlin 函数式编程的基石,它是各种框架的关键元素,比如:协程,Jetpack Compose,Gradle ...

随机推荐

  1. Yii2掉index.php?r=

    普通 首先确认apache2配置 1. 开启 apache 的 mod_rewrite 模块 去掉LoadModule rewrite_module modules/mod_rewrite.so前的“ ...

  2. python manage.py shell

    启动python有两种方式:python manage.py shell和python. 这两个命令 都会启动交互解释器,但是manage.py shell命令有一个重要的不同: 在启动解释器之前,它 ...

  3. DBC物品中打包物品参数设置

    DBC库中添加某物品包或捆,主要修改以下这两地方: 物品DBC: Stdmode字段 填写31表示捆或包   Shape字段 表示解开后的物品,填写时需要先在你的服务端文件里面找到UnbindList ...

  4. MYSQL命令练习及跳过数据库密码进行密码重新设置

        2.看当前所有数据库:show databases; 3.进入mysql数据库:use mysql; 4.查看mysql数据库中所有的表:show tables; 5.查看user表中的数据: ...

  5. 150元搭建微型家庭服务器(支持DLAN,samda,aria2)

    概览 看到有人用树莓派搭家庭服务器,感觉太不值了,300块都可以搭一台性能还可以的低功耗x86主机了,我搭一个100块顶多的服务器玩玩. 0.Linux服务器学习(比虚拟机双系统舒服多了) 1.流媒体 ...

  6. Tensorflow机器学习入门——常量、变量、placeholder和基本运算

    一.这里列出了tensorflow的一些基本函数,比较全面:https://blog.csdn.net/M_Z_G_Y/article/details/80523834 二.这里是tensortflo ...

  7. 【Go语言系列】第三方框架和库——GIN:GIN介绍

    1.Gin 是什么? Gin 是一个用 Go (Golang) 编写的 HTTP web 框架. 它是一个类似于 martini 但拥有更好性能的 API 框架, 由于 httprouter,速度提高 ...

  8. STM32内部时钟树

    1.外部晶振是干什么用的? 2.内部晶振是干什么用的? 3.外部晶振频率的大小能影响什么?

  9. 「CF383C Propagating tree」

    这应该属于一个比较麻烦的数据结构处理树上问题. 题目大意 给出一颗根节点编号为 \(1\) 的树,对于一个节点修改时在它的子树中对于深度奇偶性相同的节点加上这个权值,不同则减去这个值,单点查询. 分析 ...

  10. Wcf托管在IIS中,HttpContext.Current为空

    config中需要配置 <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> 另需要在服务类上加 ...