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

释放双眼,带上耳机,听听看~!

实战

上接,笔记:https://blog.csdn.net/u010132177/article/details/104150177
https://gitee.com/pasaulis/react-guli

1)创建目录


1
2
3
4
5
6
7
8
9
10
11
1src 目录下
2   api ajax相关
3   assets 公用资源
4   components 非路由组件
5   config 配置
6   pages 路由组件
7   utils 工具模块
8   Appj.s 应用根组件
9   index.js 入口js
10
11

cmd指创建:


1
2
3
1mkdir api assets components config pages utils
2
3

2)配置路由、引入antd

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

3)重置css样式

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


1
2
3
1 <link rel="stylesheet" href="/css/reset.css">
2
3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
1/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */
2html,
3body,
4p,
5ol,
6ul,
7li,
8dl,
9dt,
10dd,
11blockquote,
12figure,
13fieldset,
14legend,
15textarea,
16pre,
17iframe,
18hr,
19h1,
20h2,
21h3,
22h4,
23h5,
24h6 {
25  margin: 0;
26  padding: 0;
27}
28
29h1,
30h2,
31h3,
32h4,
33h5,
34h6 {
35  font-size: 100%;
36  font-weight: normal;
37}
38
39ul {
40  list-style: none;
41}
42
43button,
44input,
45select,
46textarea {
47  margin: 0;
48}
49
50html {
51  box-sizing: border-box;
52}
53
54*, *::before, *::after {
55  box-sizing: inherit;
56}
57
58img,
59video {
60  height: auto;
61  max-width: 100%;
62}
63
64iframe {
65  border: 0;
66}
67
68table {
69  border-collapse: collapse;
70  border-spacing: 0;
71}
72
73td,
74th {
75  padding: 0;
76}
77
78td:not([align]),
79th:not([align]) {
80  text-align: left;
81}
82
83

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
1import axios from 'axios'
2
3export default function ajax(url,data={},type='GET'){
4    if(type==='GET'){
5        return axios.get(url,{
6            params:data
7        })
8    } else {
9        return axios.post(url,data)
10    }
11}
12
13

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1import ajax from './ajax'
2
3// const BASE = 'http://localhost:5000'
4const BASE = ''
5
6//【1】导出一个函数,第1种写法
7//登录接口函数
8// export function reqLogin(username,password){
9//     return ajax('login',{username,password},'POST')
10// }
11
12//【2】导出一个函数,第2种写法
13// 登录接口函数
14export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')
15
16//添加用户接口
17export const AddUser=(user)=>ajax(BASE+'/manage/user/add',user,'POST')
18
19

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

  • 如果不添加将无法跨域,显示为404 not found

  • 运行环境将用另一种方法配置代理接口


1
2
3
4
5
6
7
8
9
10
11
1  "development": [
2      "last 1 chrome version",
3      "last 1 firefox version",
4      "last 1 safari version"
5    ]
6  },
7  //最下面添加此句即可
8  "proxy":"http://localhost:5000"
9}
10
11

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

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

1.编写页面基本样式

2.使用antd的form登录组件

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

4.用axios编写ajax请求


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
1import React,{Component} from 'react'
2import login from '../../assets/images/logo.png'
3import './login.less'
4import { Form, Icon, Input, Button, Checkbox } from 'antd';
5import {reqLogin} from '../../api/' //因为api文件夹下有index.js所以只要指定到文件夹即可
6
7class Login extends Component{
8    constructor(props){
9        super(props);
10    }
11
12  //点提交按钮后的操作
13    handleSubmit = e => {
14        e.preventDefault();
15        this.props.form.validateFields((err, values) => {
16          if (!err) {//如果本地验证不存在错误,即正确返回
17            //console.log('在此处发起axios请求验证,发送用户名,密码给服务器,即:', values);
18            const {username,password}=values //解构本地values给username,password,用于发送给服务器
19            //调用src/api/index.js的ajax登录请求,发送数据
20            reqLogin(username,password).then(response=>{//处理正常响应
21                console.log(response.data)
22            }).catch(err=>{//处理出错信息
23              console.log(err)
24            })
25          }else{
26              console.log('验证失败')
27          }
28        });
29      };
30
31    // 密码校验
32    validatePwd=(rule,value,callback)=>{
33        console.log('validatePwd()', rule, value)
34        if(!value){
35            callback('密码必须输入!')
36        }else if(value.length<4){
37            callback('密码必须大于4位')
38        }else if(value.length>12){
39            callback('密码不能超过12位')
40        }else if(!/^[a-zA-Z0-9_]+$/.test(value)){
41            callback('密码必须由字母、数字、下划线组成')
42        }else{
43            callback() //本地验证成功
44        }
45    }
46
47    render(){
48        //const form = this.props.form
49        //const { getFieldDecorator }=form
50        const {getFieldDecorator}=this.props.form  //以上两句合二为一
51
52        
53        return(
54           <div className='login'>
55
56               <header className='login-header'>
57                   <img src={login} />
58                   <h1>深蓝后台管理系统</h1>
59               </header>
60
61               <section className='login-content'>
62                    <h2>用户登录</h2>
63                    <Form onSubmit={this.handleSubmit} className="login-form">
64                        <Form.Item>
65                            {
66                                getFieldDecorator('username',{
67                                    rules:[
68                                        {required:true,whitespace:true,message:'用户名必须输入!'},
69                                        {min:4,message:'用户名必须大于4位'},
70                                        {max:12,message:'用户名最多只能12位'},
71                                        {pattern:/^[a-zA-Z0-9_]+$/,message:'用户名只能是字母、数字、下划线'}
72                                    ],
73                                    //initialValue:'admin' //默认显示值
74                                })(
75                                <Input
76                                    prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
77                                    placeholder="用户名"
78                                    />)
79                            }
80                            
81                        </Form.Item>
82                        <Form.Item>
83                            {
84                                getFieldDecorator('password',{
85                                    rules:[
86                                        { validator: this.validatePwd}
87                                    ]
88                                })(
89                                <Input
90                                    prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
91                                    type="password"
92                                    placeholder="密码"
93                                    />)
94                            }
95                            
96                        </Form.Item>
97                        <Form.Item>
98                            <Button type="primary" htmlType="submit" className="login-form-button">
99                                登录
100                            </Button>
101                        </Form.Item>
102                    </Form>
103               </section>
104
105           </div>
106        )
107    }
108}
109const WrapLogin = Form.create()(Login)
110export default WrapLogin
111
112
113

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1.login{
2    background: #fff url('./images/bg.jpg') ;
3    background-size: 100% 100%;
4    width:100%;
5    height: 100%;
6
7    .login-header{
8        display: flex;
9        align-items: center;
10        height: 80px;
11        background-color: rgba(21, 20, 13, 0.5);
12        img{
13            width: 40px;
14            height: 40px;
15            margin: 0 15px 0 50px;
16        }
17        h1{
18            font-size: 30px;
19            color: #fff;
20            margin: 0px;
21        }
22    }
23
24    .login-content{
25        width: 400px;
26        height: 300px;
27        background-color: rgba(255, 255, 255, 0.7);
28        margin: 50px auto;
29        padding: 20px 40px;
30        
31        h2{
32            text-align: center;
33            font-size: 24px;
34            margin-bottom: 20px;
35        }
36
37        .login-form-button{
38            width: 100%;
39        }
40    }
41}
42
43

6)简单登录

1.src/api/ajax.js


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1import axios from 'axios'
2import {message} from 'antd'
3
4export default function ajax(url, data={}, type='GET') {
5
6  return new Promise((resolve, reject) => {
7    let promise
8    // 1. 执行异步ajax请求
9    if(type==='GET') { // 发GET请求
10      promise = axios.get(url, { // 配置对象
11        params: data // 指定请求参数
12      })
13    } else { // 发POST请求
14      promise = axios.post(url, data)
15    }
16    // 2. 如果成功了, 调用resolve(value)
17    promise.then(response => {
18      resolve(response.data)
19    // 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
20    }).catch(error => {
21      // reject(error)
22      message.error('请求出错了: ' + error.message)
23    })
24  })
25
26}
27
28

2.src/api/index.js


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1import ajax from './ajax'
2
3// const BASE = 'http://localhost:5000'
4const BASE = ''
5
6//【1】导出一个函数,第1种写法
7//登录接口函数
8// export function reqLogin(username,password){
9//     return ajax('login',{username,password},'POST')
10// }
11
12//【2】导出一个函数,第2种写法
13// 登录接口函数
14export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')
15
16//添加用户接口
17export const AddUser=(user)=>ajax(BASE+'/manage/user/add',user,'POST')
18
19

3.src/pages/login/login.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
1/*
2能发送异步ajax请求的函数模块
3封装axios库
4函数的返回值是promise对象
51. 优化1: 统一处理请求异常?
6    在外层包一个自己创建的promise对象
7    在请求出错时, 不reject(error), 而是显示错误提示
82. 优化2: 异步得到不是reponse, 而是response.data
9   在请求成功resolve时: resolve(response.data)
10 */
11
12import axios from 'axios'
13import {message} from 'antd'
14
15export default function ajax(url, data={}, type='GET') {
16  return new Promise((resolve, reject) => {
17    let promise
18    // 1. 执行异步ajax请求
19    if(type==='GET') { // 发GET请求
20      promise = axios.get(url, { // 配置对象
21        params: data // 指定请求参数
22      })
23    } else { // 发POST请求
24      promise = axios.post(url, data)
25    }
26    // 2. 如果成功了, 调用resolve(value)
27    promise.then(response => {
28      resolve(response.data)
29    // 3. 如果失败了, 不调用reject(reason), 而是提示异常信息
30    }).catch(error => {
31      // reject(error)
32      message.error('请求出错了: ' + error.message)
33    })
34  })
35
36
37}
38
39// 请求登陆接口
40// ajax('/login', {username: 'Tom', passsword: '12345'}, 'POST').then()
41// 添加用户
42// ajax('/manage/user/add', {username: 'Tom', passsword: '12345', phone: '13712341234'}, 'POST').then()
43
44
45

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1import React,{Component} from 'react'
2import {BrowserRouter,Route,Switch} from 'react-router-dom'
3import Admin from './pages/admin/admin'
4import Login from './pages/login/login'
5
6class App extends Component{
7    constructor(props){
8        super(props);
9    }
10
11    render(){
12        return(
13           <BrowserRouter>
14           <Switch>
15               <Route path='/login' component={Login} />
16               <Route path='/' component={Admin} />
17           </Switch>
18           </BrowserRouter>
19        )
20    }
21}
22export default App
23
24

5.src/index.js


1
2
3
4
5
6
7
1import React from 'react'
2import ReactDOM from 'react-dom'
3import App from './App'
4
5ReactDOM.render(<App/>,document.getElementById('root'))
6
7

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

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

库好处:

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

1.src/utils/storageUtils.js

  1. 编写函数用于保存用户名到localstorage里去

  2. 从localSorage读取user

  3. 从localStorage删除user


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1/*
2保存用户名到localStorage
3*/
4import store from 'store'
5const USER_KEY='user_key' //定义localStorage内的键名为user_key
6
7export default{
8    //1.保存user到localStorage
9    saveUser(user){
10        //localStorage.setItem(USER_KEY,JSON.stringify(user)) //原生localStorage写法,下同
11        store.set(USER_KEY,user)  //store库写法,自动把user解析为字典
12    },
13
14    //2.从localSorage读取user
15    getUser () {
16        // return JSON.parse(localStorage.getItem(USER_KEY)||'{}') //如果没有得到数据,就返回空字典
17        return store.get(USER_KEY) || {}
18    },
19
20    //3.从localStorage删除user
21    removeUser (){
22        //localStorage.removeItem(USER_KEY)
23        store.remove(USER_KEY)
24    }
25}
26
27

2. src/utils/memoryUtils.js


1
2
3
4
5
6
7
8
1/*
2用于在内存中保存数据的工具模块
3*/
4export default{
5    user:{},
6}
7
8

3. src/app.js

【1】引入模块
【2】读取local中保存user, 保存到内存中


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

4.src/pages/login/login.jsx

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
1import React, {Component} from 'react'
2import {Redirect} from 'react-router-dom'
3import {
4  Form,
5  Icon,
6  Input,
7  Button,
8  message
9} from 'antd'
10import './login.less'
11import logo from '../../assets/images/logo.png'
12import {reqLogin} from '../../api'
13import memoryUtils from '../../utils/memoryUtils'
14import storageUtils from '../../utils/storageUtils'
15
16
17const Item = Form.Item // 不能写在import之前
18
19
20/*
21登陆的路由组件
22 */
23class Login extends Component {
24
25  handleSubmit = (event) => {
26
27    // 阻止事件的默认行为
28    event.preventDefault()
29
30    // 对所有表单字段进行检验
31    this.props.form.validateFields(async (err, values) => {
32      // 检验成功
33      if (!err) {
34        // console.log('提交登陆的ajax请求', values)
35        // 请求登陆
36        const {username, password} = values
37        const result = await reqLogin(username, password) // {status: 0, data: user}  {status: 1, msg: 'xxx'}
38        // console.log('请求成功', result)
39        if (result.status===0) { // 登陆成功
40          // 提示登陆成功
41          message.success('登陆成功')
42
43          // 保存user
44          const user = result.data
45          memoryUtils.user = user // 保存在内存中
46          storageUtils.saveUser(user) // 保存到local中
47
48          // 跳转到管理界面 (不需要再回退回到登陆)
49          this.props.history.replace('/')
50
51        } else { // 登陆失败
52          // 提示错误信息
53          message.error(result.msg)
54        }
55
56      } else {
57        console.log('检验失败!')
58      }
59    });
60
61    // 得到form对象
62    // const form = this.props.form
63    // // 获取表单项的输入数据
64    // const values = form.getFieldsValue()
65    // console.log('handleSubmit()', values)
66  }
67
68  /*
69  对密码进行自定义验证
70  */
71  /*
72   用户名/密码的的合法性要求
73     1). 必须输入
74     2). 必须大于等于4位
75     3). 必须小于等于12位
76     4). 必须是英文、数字或下划线组成
77    */
78  validatePwd = (rule, value, callback) => {
79    console.log('validatePwd()', rule, value)
80    if(!value) {
81      callback('密码必须输入')
82    } else if (value.length<4) {
83      callback('密码长度不能小于4位')
84    } else if (value.length>12) {
85      callback('密码长度不能大于12位')
86    } else if (!/^[a-zA-Z0-9_]+$/.test(value)) {
87      callback('密码必须是英文、数字或下划线组成')
88    } else {
89      callback() // 验证通过
90    }
91    // callback('xxxx') // 验证失败, 并指定提示的文本
92  }
93
94  render () {
95
96    // 【1】如果用户已经登陆, 自动跳转到管理界面
97    const user = memoryUtils.user
98    if(user && user._id) {
99      return <Redirect to='/'/>
100    }
101
102    // 得到具强大功能的form对象
103    const form = this.props.form
104    const { getFieldDecorator } = form;
105
106    return (
107      <div className="login">
108        <header className="login-header">
109          <img src={logo} alt="logo"/>
110          <h1>React项目: 后台管理系统</h1>
111        </header>
112        <section className="login-content">
113          <h2>用户登陆</h2>
114          <Form onSubmit={this.handleSubmit} className="login-form">
115            <Item>
116              {
117                /*
118              用户名/密码的的合法性要求
119                1). 必须输入
120                2). 必须大于等于4位
121                3). 必须小于等于12位
122                4). 必须是英文、数字或下划线组成
123               */
124              }
125              {
126                getFieldDecorator('username', { // 配置对象: 属性名是特定的一些名称
127                  // 声明式验证: 直接使用别人定义好的验证规则进行验证
128                  rules: [
129                    { required: true, whitespace: true, message: '用户名必须输入' },
130                    { min: 4, message: '用户名至少4位' },
131                    { max: 12, message: '用户名最多12位' },
132                    { pattern: /^[a-zA-Z0-9_]+$/, message: '用户名必须是英文、数字或下划线组成' },
133                  ],
134                  initialValue: 'admin', // 初始值
135                })(
136                  <Input
137                    prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
138                    placeholder="用户名"
139                  />
140                )
141              }
142            </Item>
143            <Form.Item>
144              {
145                getFieldDecorator('password', {
146                  rules: [
147                    {
148                      validator: this.validatePwd
149                    }
150                  ]
151                })(
152                  <Input
153                    prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
154                    type="password"
155                    placeholder="密码"
156                  />
157                )
158              }
159
160            </Form.Item>
161            <Form.Item>
162              <Button type="primary" htmlType="submit" className="login-form-button">
163                登陆
164              </Button>
165            </Form.Item>
166          </Form>
167        </section>
168      </div>
169    )
170  }
171}
172
173/*
1741. 高阶函数
175    1). 一类特别的函数
176        a. 接受函数类型的参数
177        b. 返回值是函数
178    2). 常见
179        a. 定时器: setTimeout()/setInterval()
180        b. Promise: Promise(() => {}) then(value => {}, reason => {})
181        c. 数组遍历相关的方法: forEach()/filter()/map()/reduce()/find()/findIndex()
182        d. 函数对象的bind()
183        e. Form.create()() / getFieldDecorator()()
184    3). 高阶函数更新动态, 更加具有扩展性
185
1862. 高阶组件
187    1). 本质就是一个函数
188    2). 接收一个组件(被包装组件), 返回一个新的组件(包装组件), 包装组件会向被包装组件传入特定属性
189    3). 作用: 扩展组件的功能
190    4). 高阶组件也是高阶函数: 接收一个组件函数, 返回是一个新的组件函数
191 */
192/*
193包装Form组件生成一个新的组件: Form(Login)
194新组件会向Form组件传递一个强大的对象属性: form
195 */
196const WrapLogin = Form.create()(Login)
197export default WrapLogin
198/*
1991. 前台表单验证
2002. 收集表单输入数据
201 */
202
203/*
204async和await
2051. 作用?
206   简化promise对象的使用: 不用再使用then()来指定成功/失败的回调函数
207   以同步编码(没有回调函数了)方式实现异步流程
2082. 哪里写await?
209    在返回promise的表达式左侧写await: 不想要promise, 想要promise异步执行的成功的value数据
2103. 哪里写async?
211    await所在函数(最近的)定义的左侧写async
212 */
213
214

5.src/pages/admin/admin.jsx


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1import React,{Component} from 'react'
2import {Redirect} from 'react-router-dom'
3import memoryUtils from '../../utils/memoryUtils'
4
5
6class Admin extends Component{
7    constructor(props){
8        super(props);
9    }
10
11    render(){
12    //【1】如果memoryUtils中的user对象不存在(未登录),则跳转到登录页面
13        const user=memoryUtils.user
14        if(!user || !user._id){
15            return <Redirect to='/login'/>
16        }
17        return(
18           <div>
19               Admin
20           </div>
21        )
22    }
23}
24export default Admin
25
26
27

6.效果http://localhost:3000

admin admin
输入错误则提示,成功则跳转到/admin页面
《React后台管理系统实战 :一》:目录结构、引入antd、引入路由、写login页面、使用antd的form登录组件、form前台验证、高阶函数/组件
《React后台管理系统实战 :一》:目录结构、引入antd、引入路由、写login页面、使用antd的form登录组件、form前台验证、高阶函数/组件
f12打开application,把localstorage里的user_key再刷新即会跳转到登录页面

给TA打赏
共{{data.count}}人
人已打赏
安全技术

base64加密解密

2021-8-18 16:36:11

安全技术

C++ 高性能服务器网络框架设计细节

2022-1-11 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索