0%

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

详情请点阅读全文

一、布局及排版

1.布局src/pages/admin/header/index.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
import React,{Component} from 'react'
import './header.less'

export default class Header extends Component{
render(){
return(
<div className='header'>

<div className='header-top'>
<span>欢迎,admin</span>
<a href='javascript:'>退出</a>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
<span>首页</span>
</div>

<div className='header-bottom-right'>
<span>2020-2-6 10:10:10</span>
<img src='http://www.weather.com.cn/m2/i/icon_weather/21x15/d14.gif' alt='天气'/>
<span>雪</span>
</div>
</div>

</div>
)
}
}

2. 样式src/pages/admin/header/header.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
44
45
46
47
48
49
50
.header{
height: 80px;
.header-top{
height: 40px;
line-height: 40px;
// 以上两行控制行内文字居中
text-align:right;
border-bottom: 2px solid lightseagreen;
padding-right: 30px;
span{
margin-right:10px;
}
}

.header-bottom{
height: 40px;
background-color: white;
display:flex;
align-items: center;
padding:0 30px;
.header-bottom-left{
width: 25%;
text-align:center;
font-size: 18px;
position: relative;
//以下用伪元素::after及transform来实现下指三角形
&::after {
content: '';
position: absolute;
right: 50%;
top: 100%;
transform: translateX(50%);
border-top: 20px solid white;
border-right: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 20px solid transparent;
}
}
.header-bottom-right{
width:75%;
text-align:right;
img{
margin: 0 10px;
height:4px;
width:3px;
}
}
}

}

效果:

在这里插入图片描述

二、天气请求接口函数

1. 基础知识

1. 1【jsonp】 请求库https://github.com/webmodules/jsonp

jsonp原理

1
2
3
4
5
6
7
8
9
10
1). jsonp只能解决GET类型的ajax请求跨域问题
2). jsonp请求不是ajax请求, 而是一般的get请求
3). 基本原理
浏览器端:
动态生成<script>来请求后台接口(src就是接口的url)
定义好用于接收响应数据的函数(fn), 并将函数名通过请求参数提交给后台(如: callback=fn)
服务器端:
接收到请求处理产生结果数据后, 返回一个函数调用的js代码, 并将结果数据作为实参传入函数调用
浏览器端:
收到响应自动执行函数调用的js代码, 也就执行了提前定义好的回调函数, 并得到了需要的结果数据
  1. jsonp本质是get 请求
  2. 用来解决跨域问题
  3. 浏览器端通过script标签发请求Request URL: http://api.map.baidu.com/telematics/v3/weather?location=%E5%BE%90%E5%B7%9E&output=json&ak=3p49MVra6urFRGOT9s8UBWr2&callback=__jp0注意最后多了一个callback=__jp0,它指定了返回时用来处理数据的函数名
  4. 服务器返回一个函数执行语句代码类似__jp0&&__jp0({"error":0,"status":"success",...})在network里查看请求,注意看它的名字和请求时的指定一样
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    jsonp(url, opts, fn)
    url (String) url to fetch
    opts (Object), optional
    param (String) name of the query string parameter to specify the callback (defaults to callback)
    timeout (Number) how long after a timeout error is emitted. 0 to disable (defaults to 60000)
    prefix (String) prefix for the global callback functions that handle jsonp responses (defaults to __jp)
    name (String) name of the global callback functions that handle jsonp responses (defaults to prefix + incremented counter)
    fn callback

    The callback is called with err, data parameters.
    If it times out, the err will be an Error object whose message is Timeout.
    Returns a function that, when called, will cancel the in-progress jsonp

    request (fn won't be called).

    1.2. promise对象函数,及jsonp的写法

    【1】天气接口函数
    【2】返回一个promise函数
    【3】发送一个jsonp请求,{}内为空意思是使用默认配置选项
    【4】异步返回成功数据给调用者
    【5】异步返回失败信息给调用者
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 【1】天气接口函数
    export const reqWeather=(city) => {
    //百度天气api接口
    const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
    //【2】返回一个promise函数
    return new Promise((resolve,reject) => {
    //【3】发送一个jsonp请求,{}内为空意思是使用默认配置选项
    jsonp(url,{},(err,data) => {
    //【3.1】如果请求成功
    if(!err && data.status==='success'){
    //请求成功处理语句编写处
    resolve({obj}) //【4】异步返回成功数据给调用者
    }else{//【3.2】如果请求失败
    //请求失败处理语句编写处
    reject() //【5】异步返回失败信息给调用者
    }
    })
    })
    }

1.3. 获取天气信息(支持jsonp)

请求URL:

http://api.map.baidu.com/telematics/v3/weather

请求方式:

GET

参数类型:

|参数             |是否必选 |类型     |说明
|location    |Y       |string   |城市名称
|output      |Y       |string   |返回数据格式: json
|ak          |Y       |string   |唯一的应用key(3p49MVra6urFRGOT9s8UBWr2)

返回示例:

{
  "error": 0,
  "status": "success",
  "date": "2019-06-02",
  "results": [
    {
      "currentCity": "北京",
      "pm25": "119",
      "index": [
        {
          "des": "建议着长袖T恤、衬衫加单裤等服装。年老体弱者宜着针织长袖衬衫、马甲和长裤。",
          "tipt": "穿衣指数",
          "title": "穿衣",
          "zs": "舒适"
        },
        {
          "des": "不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。",
          "tipt": "洗车指数",
          "title": "洗车",
          "zs": "不宜"
        },
        {
          "des": "各项气象条件适宜,无明显降温过程,发生感冒机率较低。",
          "tipt": "感冒指数",
          "title": "感冒",
          "zs": "少发"
        },
        {
          "des": "天气较好,赶快投身大自然参与户外运动,尽情感受运动的快乐吧。",
          "tipt": "运动指数",
          "title": "运动",
          "zs": "适宜"
        },
        {
          "des": "紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。",
          "tipt": "紫外线强度指数",
          "title": "紫外线强度",
          "zs": "弱"
        }
      ],
      "weather_data": [
        {
          "date": "周日 06月02日 (实时:30℃)",
          "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png",
          "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/leizhenyu.png",
          "weather": "多云转雷阵雨",
          "wind": "西南风3-4级",
          "temperature": "31 ~ 20℃"
        },
        {
          "date": "周一",
          "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png",
          "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png",
          "weather": "多云",
          "wind": "南风微风",
          "temperature": "34 ~ 20℃"
        },
        {
          "date": "周二",
          "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/leizhenyu.png",
          "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/leizhenyu.png",
          "weather": "雷阵雨",
          "wind": "东风微风",
          "temperature": "28 ~ 21℃"
        },
        {
          "date": "周三",
          "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png",
          "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png",
          "weather": "多云",
          "wind": "北风3-4级",
          "temperature": "33 ~ 19℃"
        }
      ]
    }
  ]
}

2.编写天气请求接口src/api/index.js

【0】借用antd返回信息组件
【1】天气请求接口函数编写
【2】从数据中解构取出图片、天气
【3】异步返回图片、天气给调用函数者
【4】返回错误信息给调用者

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
import ajax from './ajax'
import jsonp from 'jsonp'
import {message} from 'antd' //【0】借用antd返回信息组件 默认暴露不要大括号,否则需要大括号
// const BASE = 'http://localhost:5000'
const BASE = ''

//导出一个函数,第1种写法
//登录接口函数
// export function reqLogin(username,password){
// return ajax('login',{username,password},'POST')
// }

//导出一个函数,第2种写法
// 登录接口函数
export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')

//添加用户接口
export const AddUser=(user)=>ajax(BASE+'/manage/user/add',user,'POST')

// 【1】天气请求接口函数编写
export const reqWeather=(city) => {
const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
//返回一个promise函数
return new Promise((resolve,reject) => {
//发送一个jsonp请求
jsonp(url,{},(err,data) => {
//输出请求的数据到控制台
console.log('jsonp()', err, data)
//如果请求成功
if(!err && data.status==='success'){
//【2】从数据中解构取出图片、天气
const {dayPictureUrl,weather}=data.results[0].weather_data[0]
//【3】异步返回图片、天气给调用函数者
resolve({dayPictureUrl,weather})
}else{//如果请求失败
//【4】返回错误信息给调用者
message.error('天气信息获取失败')
}
})
})
}
reqWeather('上海')

刷新页面即会在控制台输入【1.3 的 返回示例】类似的信息,之所以会直接运行,原因是login页面请求了api,从而触发reqWeather(‘上海’)直接运行

三、header.jsx组件时间部分

3.1时间格式处理工具编写

时间获取:

1
Date.now() //返回样式:1581070286188

时间格式化工具编写 src/utils/dateUtils.js

功能:把【时间获取Date.now()】的时间处理成如下格式 2020-2-6 10:10:10

1
2
3
4
5
6
7
8
9
10
11
12
/*
包含n个日期时间处理的工具函数模块
*/
/*
格式化日期
*/
export function formateDate(time) {
if (!time) return ''
let date = new Date(time)
return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
+ ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds()
}

3.2 src/pages/admin/header.jsx

【1】时间格式化工具
【2】内存中存取用户信息工具
【3】当前时间格式化后的字符串
【4】每过一秒获取一次系统时间
定时器函数setInterval(()=>{},1000)
【5】在第一次render()之后执行一次
一般在此执行异步操作: 发ajax请求/启动定时器
【6】解构state内的数据
【7】获取memoryUtils中的用户名
【8】把变量填入标签内:用户名、当前时间、天气图标、天气

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
import React,{Component} from 'react'
import './header.less'
import {formateDate} from '../../../utils/dateUtils.js' //【1】时间格式化工具
import memoryUtils from '../../../utils/memoryUtils' //【2】内存中存取用户信息工具

export default class Header extends Component{

state={
curentTime:formateDate(Date.now()), //【3】当前时间格式化后的字符串
dayPictureUrl:'', //天气小图标地址
weather:'', //天气文字
}


// 【4】每过一秒获取一次系统时间
getTime=()=>{
//定时器函数setInterval()
this.intervalId = setInterval(()=>{
let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
this.setState({curentTime}) //更新到state里,实现每秒更新一次时间
},1000)
}

/* 【5】在第一次render()之后执行一次
一般在此执行异步操作: 发ajax请求/启动定时器*/
componentDidMount(){
this.getTime()
}


render(){
//【6】解构state内的数据
const {curentTime,dayPictureUrl,weather} = this.state
//【7】获取memoryUtils中的用户名
const username = memoryUtils.user.username

return(
<div className='header'>
//【8】把变量填入标签内:用户名、当前时间、天气图标、天气
<div className='header-top'>
<span>欢迎,{username}</span>
<a href='javascript:'>退出</a>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
<span>首页</span>
</div>

<div className='header-bottom-right'>
<span>{curentTime}</span>
<img src={dayPictureUrl} alt='天气'/>
<span>{weather}</span>
</div>
</div>

</div>
)
}
}

效果:2020-2-7 20:36:58 时间会动态变化

四、天气部分

天气显示src/pages/admin/header.jsx

【1】引入接口函数,非默认导
【2】天气小图标地址
【3】天气文字
【4】异步获取天气
【5】渲染之后调用一次天气
【6】解构state内的数据
【7】写入页面

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
import React,{Component} from 'react'
import './header.less'
import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
import {reqWeather} from '../../../api/index' //【1】引入接口函数,非默认导出,加花括号

export default class Header extends Component{

state={
curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
dayPictureUrl:'', //【2】天气小图标地址
weather:'', //【3】天气文字
}

//【4】异步获取天气
getWeather = async () => {
//解构天气小图标,天气
const {dayPictureUrl, weather} = await reqWeather('上海')
//更新状态
this.setState({dayPictureUrl, weather})
}

// 每过一秒获取一次系统时间
getTime=()=>{
//定时器函数setInterval()
this.intervalId = setInterval(()=>{
let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
this.setState({curentTime})
},1000)
}

/* 在第一次render()之后执行一次
一般在此执行异步操作: 发ajax请求/启动定时器*/
componentDidMount(){
this.getTime() //调用时间,因为getTime内部写了定时,所以每隔1秒会更新一次时间state,页面就会重新渲染一次
this.getWeather() //【5】渲染之后调用一次天气
}


render(){
//【6】解构state内的数据
const {curentTime,dayPictureUrl,weather} = this.state
//获取用户名
const username = memoryUtils.user.username

return(
<div className='header'>

<div className='header-top'>
<span>欢迎,{username}</span>
<a href='javascript:'>退出</a>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
<span>首页</span>
</div>
{/*【7】写入页面*/}
<div className='header-bottom-right'>
<span>{curentTime}</span>
<img src={dayPictureUrl} alt='天气'/>
<span>{weather}</span>
</div>
</div>

</div>
)
}
}

效果:在这里插入图片描述

五、显示当前路径模块

【1】用于包装当前组件,使其具有路由的3属性history
【1.1】包装起来,使有路由组件的属性
【2】导入导航配置菜单
【3】根据当前网址,在menuListConfig内找到对应的title
【4】得到当前需要显示的title
【5】显示title

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
import React,{Component} from 'react'
import './header.less'
import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号

//import {message} from 'antd'
import {withRouter} from 'react-router-dom' //【1】用于包装当前组件,使其具有路由的3属性history
import menuList from '../../../config/menuConfig.js' //【2】导入导航配置菜单

class Header extends Component{

state={
curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
dayPictureUrl:'', //天气小图标地址
weather:'', //天气文字
}

// 获取路径
// getPath=()=>{

// }

//【3】根据当前网址,在menuListConfig内找到对应的title
getTitle = () => {
// 得到当前请求路径
const path = this.props.location.pathname
let title
menuList.forEach(item => {
if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
title = item.title
} else if (item.children) {
// 在所有子item中查找匹配的
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
// 如果有值才说明有匹配的
if(cItem) {
// 取出它的title
title = cItem.title
}
}
})
return title //返回title
}

//异步获取天气
getWeather = async () => {
//解构天气小图标,天气
const {dayPictureUrl, weather} = await reqWeather('上海')
//更新状态
this.setState({dayPictureUrl, weather})
}

// 每过一秒获取一次系统时间
getTime=()=>{
//定时器函数setInterval()
this.intervalId = setInterval(()=>{
let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
this.setState({curentTime})
},1000)
}

/* 在第一次render()之后执行一次
一般在此执行异步操作: 发ajax请求/启动定时器*/
componentDidMount(){
this.getTime()
this.getWeather()
}


render(){
//解构state内的数据
const {curentTime,dayPictureUrl,weather} = this.state
//获取用户名
const username = memoryUtils.user.username

// 【4】得到当前需要显示的title
const title = this.getTitle()

return(
<div className='header'>

<div className='header-top'>
<span>欢迎,{username}</span>
<a href='javascript:'>退出</a>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
{/*【5】显示title*/}
<span>{title}</span>
</div>

<div className='header-bottom-right'>
<span>{curentTime}</span>
<img src={dayPictureUrl} alt='天气'/>
<span>{weather}</span>
</div>
</div>

</div>
)
}
}

export default withRouter(Header)//【1.1】包装起来,使当前组件具有路由组件的属性

效果:http://localhost:3000/role![在这里插入图片描述](https://img-blog.csdnimg.cn/202002072152458.png)

六、退出登录

【antd对话框】https://ant.design/components/modal-cn/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {Modal} from 'antd'

//退出登录
loginOut=()=>{
Modal.confirm({
title: '确定要退出登录吗?',
content: '是请点确定,否则点取消',
onOk() {
console.log('OK');
},
onCancel() {
console.log('Cancel');
},
})
}

完整代码src/pages/admin/header/index.jsx

【1】内存中存取用户信息工具 默认导出,不用加花括号
【2】删除localstorage中的用户登录数据
【3】引入对话框模块
【4】退出登录函数
【5】改成前头函数,因为下面要用到
【6】删除localstorage中登录信息。及内存中登录信息
【7】删除内存中user信息
【8】跳转到登录页面,用替换因为无需退回
【9】调用退出

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
import React,{Component} from 'react'
import './header.less'
import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
import memoryUtils from '../../../utils/memoryUtils' //【1】内存中存取用户信息工具 默认导出,不用加花括号
import storageUtils from '../../../utils/storageUtils' //【2】删除localstorage中的用户登录数据
import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号

import {withRouter} from 'react-router-dom' //用于包装当前组件,使其具体路由的3属性history
import menuList from '../../../config/menuConfig.js' //导入导航配置菜单

import {Modal} from 'antd' //【3】引入对话框模块

class Header extends Component{

state={
curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
dayPictureUrl:'', //天气小图标地址
weather:'', //天气文字
}

// 获取路径
// getPath=()=>{

// }

getTitle = () => {
// 得到当前请求路径
const path = this.props.location.pathname
let title
menuList.forEach(item => {
if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
title = item.title
} else if (item.children) {
// 在所有子item中查找匹配的
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
// 如果有值才说明有匹配的
if(cItem) {
// 取出它的title
title = cItem.title
}
}
})
return title
}

//异步获取天气
getWeather = async () => {
//解构天气小图标,天气
const {dayPictureUrl, weather} = await reqWeather('上海')
//更新状态
this.setState({dayPictureUrl, weather})
}

// 每过一秒获取一次系统时间
getTime=()=>{
//定时器函数setInterval()
this.intervalId = setInterval(()=>{
let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
this.setState({curentTime})
},1000)
}

//【4】退出登录函数
loginOut=()=>{
Modal.confirm({
title: '确定要退出登录吗?',
content: '是请点确定,否则点取消',
onOk:()=> {//【5】改成前头函数,因为下面要用到this.props.history.replace()
console.log('OK');
//【6】删除localstorage中登录信息。及内存中登录信息
storageUtils.removeUser()
memoryUtils.user={}//【7】删除内存中user信息
//【8】跳转到登录页面,用替换因为无需退回
this.props.history.replace('/login')
}//,取消时什么也不做,所以可省略不写
// onCancel() {
// console.log('Cancel');
// },
})

}

//在第一次render()之后执行一次
//一般在此执行异步操作: 发ajax请求启动定时器
componentDidMount(){
this.getTime();
this.getWeather();
}


render(){
//解构state内的数据
const {curentTime,dayPictureUrl,weather} = this.state
//获取用户名
const username = memoryUtils.user.username

// 得到当前需要显示的title
const title = this.getTitle()

return(
<div className='header'>

<div className='header-top'>
<span>欢迎,{username}</span>
{/*【9】*/}
<a href='javascript:' onClick={this.loginOut}>退出</a>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
<span>{title}</span>
</div>

<div className='header-bottom-right'>
<span>{curentTime}</span>
<img src={dayPictureUrl} alt='天气'/>
<span>{weather}</span>
</div>
</div>

</div>
)
}
}

export default withRouter(Header)

效果:在这里插入图片描述

七、警告完善

退出后控制台会提示如下信息:

此为定时器函数未清除造成的问题

1
2
3
4
5
6
7
8
9
10
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in Header (created by Context.Consumer)
in withRouter(Header) (at admin.jsx:42)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at admin.jsx:41)
in section (created by BasicLayout)
in BasicLayout (created by Context.Consumer)
in Adapter (at admin.jsx:37)
in Admin (created by Context.Consumer)

解决:清除定时器函数

1
2
3
4
5
6
7
/*
当前组件卸载之前调用
*/
componentWillUnmount () {
// 清除定时器
clearInterval(this.intervalId)
}

完整代码

【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
import React,{Component} from 'react'
import './header.less'
import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
import storageUtils from '../../../utils/storageUtils' //删除localstorage中的用户登录数据
import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号

import {withRouter} from 'react-router-dom' //用于包装当前组件,使其具体路由的3属性history
import menuList from '../../../config/menuConfig.js' //导入导航配置菜单

import {Modal} from 'antd'

class Header extends Component{

state={
curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
dayPictureUrl:'', //天气小图标地址
weather:'', //天气文字
}

// 获取路径
// getPath=()=>{

// }

getTitle = () => {
// 得到当前请求路径
const path = this.props.location.pathname
let title
menuList.forEach(item => {
if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
title = item.title
} else if (item.children) {
// 在所有子item中查找匹配的
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
// 如果有值才说明有匹配的
if(cItem) {
// 取出它的title
title = cItem.title
}
}
})
return title
}

//异步获取天气
getWeather = async () => {
//解构天气小图标,天气
const {dayPictureUrl, weather} = await reqWeather('上海')
//更新状态
this.setState({dayPictureUrl, weather})


}

// 每过一秒获取一次系统时间
getTime=()=>{
//定时器函数setInterval()
this.intervalId = setInterval(()=>{
let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
this.setState({curentTime})
},1000)
}

//退出登录
loginOut=()=>{
Modal.confirm({
title: '确定要退出登录吗?',
content: '是请点确定,否则点取消',
onOk:()=> {//改成前头函数,因为下面要用到this.props.history.replace()
console.log('OK');
//删除localstorage中登录信息。及内存中登录信息
storageUtils.removeUser()
memoryUtils.user={}
//跳转到登录页面,用替换因为无需退回
this.props.history.replace('/login')
}//,取消时什么也不做,所以可省略不写
// onCancel() {
// console.log('Cancel');
// },
})

}

//在第一次render()之后执行一次
//一般在此执行异步操作: 发ajax请求启动定时器
componentDidMount(){
this.getTime();
this.getWeather();
}

/*
【1】当前组件卸载之前调用清除定时器,避免其造成警告信息
*/
componentWillUnmount () {
// 清除定时器
clearInterval(this.intervalId)
}


render(){
//解构state内的数据
const {curentTime,dayPictureUrl,weather} = this.state
//获取用户名
const username = memoryUtils.user.username

// 得到当前需要显示的title
const title = this.getTitle()

return(
<div className='header'>

<div className='header-top'>
<span>欢迎,{username}</span>
<a href='javascript:' onClick={this.loginOut}>退出</a>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
<span>{title}</span>
</div>

<div className='header-bottom-right'>
<span>{curentTime}</span>
<img src={dayPictureUrl} alt='天气'/>
<span>{weather}</span>
</div>
</div>

</div>
)
}
}

export default withRouter(Header)

优化<a链接为button自定义组件

/src/component/link-button/index.jsx

1
2
3
4
5
6
import React,{component} from 'react' 
import './index.less'

export default function LinkButton(props){
return <button {...props} className='link-button'></button>
}

index.less

1
2
3
4
5
6
7
.link-button{
background-color: transparent;
border: none;
outline: none;
color: #1da57a;
cursor: pointer;
}

页面引入LinkButton

【1】引入自定按键
【2】使用自定义组件

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
import React,{Component} from 'react'
import './header.less'
import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
import storageUtils from '../../../utils/storageUtils' //删除localstorage中的用户登录数据
import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号

import {withRouter} from 'react-router-dom' //用于包装当前组件,使其具体路由的3属性history
import menuList from '../../../config/menuConfig.js' //导入导航配置菜单

import {Modal} from 'antd'
import LinkButton from '../../../components/link-button/index'//【1】引入自定按键

class Header extends Component{

state={
curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
dayPictureUrl:'', //天气小图标地址
weather:'', //天气文字
}

// 获取路径
// getPath=()=>{

// }

getTitle = () => {
// 得到当前请求路径
const path = this.props.location.pathname
let title
menuList.forEach(item => {
if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
title = item.title
} else if (item.children) {
// 在所有子item中查找匹配的
const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
// 如果有值才说明有匹配的
if(cItem) {
// 取出它的title
title = cItem.title
}
}
})
return title
}

//异步获取天气
getWeather = async () => {
//解构天气小图标,天气
const {dayPictureUrl, weather} = await reqWeather('上海')
//更新状态
this.setState({dayPictureUrl, weather})


}

// 每过一秒获取一次系统时间
getTime=()=>{
//定时器函数setInterval()
this.intervalId = setInterval(()=>{
let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
this.setState({curentTime})
},1000)
}

//退出登录
loginOut=()=>{
Modal.confirm({
title: '确定要退出登录吗?',
content: '是请点确定,否则点取消',
onOk:()=> {//改成前头函数,因为下面要用到this.props.history.replace()
console.log('OK');
//删除localstorage中登录信息。及内存中登录信息
storageUtils.removeUser()
memoryUtils.user={}
//跳转到登录页面,用替换因为无需退回
this.props.history.replace('/login')
}//,取消时什么也不做,所以可省略不写
// onCancel() {
// console.log('Cancel');
// },
})

}

//在第一次render()之后执行一次
//一般在此执行异步操作: 发ajax请求启动定时器
componentDidMount(){
this.getTime();
this.getWeather();
}

/*
当前组件卸载之前调用清除定时器,避免其造成警告信息
*/
componentWillUnmount () {
// 清除定时器
clearInterval(this.intervalId)
}


render(){
//解构state内的数据
const {curentTime,dayPictureUrl,weather} = this.state
//获取用户名
const username = memoryUtils.user.username

// 得到当前需要显示的title
const title = this.getTitle()

return(
<div className='header'>

<div className='header-top'>
<span>欢迎,{username}</span>
{/*【2】使用自定义组件*/}
<LinkButton href='javascript:' onClick={this.loginOut}>退出</LinkButton>
</div>

<div className='header-bottom'>
<div className='header-bottom-left'>
<span>{title}</span>
</div>

<div className='header-bottom-right'>
<span>{curentTime}</span>
<img src={dayPictureUrl} alt='天气'/>
<span>{weather}</span>
</div>
</div>

</div>
)
}
}

export default withRouter(Header)

效果:在这里插入图片描述