一、布局及排版
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 30
| 1import React,{Component} from 'react'
2import './header.less'
3
4export default class Header extends Component{
5 render(){
6 return(
7 <div className='header'>
8
9 <div className='header-top'>
10 <span>欢迎,admin</span>
11 <a href='javascript:'>退出</a>
12 </div>
13
14 <div className='header-bottom'>
15 <div className='header-bottom-left'>
16 <span>首页</span>
17 </div>
18
19 <div className='header-bottom-right'>
20 <span>2020-2-6 10:10:10</span>
21 <img src='http://www.weather.com.cn/m2/i/icon_weather/21x15/d14.gif' alt='天气'/>
22 <span>雪</span>
23 </div>
24 </div>
25
26 </div>
27 )
28 }
29}
30 |
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 51
| 1.header{
2 height: 80px;
3 .header-top{
4 height: 40px;
5 line-height: 40px;
6 // 以上两行控制行内文字居中
7 text-align:right;
8 border-bottom: 2px solid lightseagreen;
9 padding-right: 30px;
10 span{
11 margin-right:10px;
12 }
13 }
14
15 .header-bottom{
16 height: 40px;
17 background-color: white;
18 display:flex;
19 align-items: center;
20 padding:0 30px;
21 .header-bottom-left{
22 width: 25%;
23 text-align:center;
24 font-size: 18px;
25 position: relative;
26 //以下用伪元素::after及transform来实现下指三角形
27 &::after {
28 content: '';
29 position: absolute;
30 right: 50%;
31 top: 100%;
32 transform: translateX(50%);
33 border-top: 20px solid white;
34 border-right: 20px solid transparent;
35 border-bottom: 20px solid transparent;
36 border-left: 20px solid transparent;
37 }
38 }
39 .header-bottom-right{
40 width:75%;
41 text-align:right;
42 img{
43 margin: 0 10px;
44 height:4px;
45 width:3px;
46 }
47 }
48 }
49
50}
51 |
效果:
二、天气请求接口函数
1. 基础知识
jsonp原理
1 2 3 4 5 6 7 8 9 10 11
| 11). jsonp只能解决GET类型的ajax请求跨域问题
22). jsonp请求不是ajax请求, 而是一般的get请求
33). 基本原理
4 浏览器端:
5 动态生成<script>来请求后台接口(src就是接口的url)
6 定义好用于接收响应数据的函数(fn), 并将函数名通过请求参数提交给后台(如: callback=fn)
7 服务器端:
8 接收到请求处理产生结果数据后, 返回一个函数调用的js代码, 并将结果数据作为实参传入函数调用
9 浏览器端:
10 收到响应自动执行函数调用的js代码, 也就执行了提前定义好的回调函数, 并得到了需要的结果数据
11 |
-
jsonp本质是get 请求
-
用来解决跨域问题
-
浏览器端通过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,它指定了返回时用来处理数据的函数名
-
服务器返回一个函数执行语句代码类似__jp0&&__jp0({"error":0,"status":"success",…})在network里查看请求,注意看它的名字和请求时的指定一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 1jsonp(url, opts, fn)
2url (String) url to fetch
3opts (Object), optional
4 param (String) name of the query string parameter to specify the callback (defaults to callback)
5 timeout (Number) how long after a timeout error is emitted. 0 to disable (defaults to 60000)
6 prefix (String) prefix for the global callback functions that handle jsonp responses (defaults to __jp)
7 name (String) name of the global callback functions that handle jsonp responses (defaults to prefix + incremented counter)
8fn callback
9
10The callback is called with err, data parameters.
11If it times out, the err will be an Error object whose message is Timeout.
12Returns a function that, when called, will cancel the in-progress jsonp
13
14request (fn won't be called).
15 |
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 20
| 1// 【1】天气接口函数
2export const reqWeather=(city) => {
3 //百度天气api接口
4 const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
5 //【2】返回一个promise函数
6 return new Promise((resolve,reject) => {
7 //【3】发送一个jsonp请求,{}内为空意思是使用默认配置选项
8 jsonp(url,{},(err,data) => {
9 //【3.1】如果请求成功
10 if(!err && data.status==='success'){
11 //请求成功处理语句编写处
12 resolve({obj}) //【4】异步返回成功数据给调用者
13 }else{//【3.2】如果请求失败
14 //请求失败处理语句编写处
15 reject() //【5】异步返回失败信息给调用者
16 }
17 })
18 })
19}
20 |
1.3. 获取天气信息(支持jsonp)
请求URL:
1 2
| 1http://api.map.baidu.com/telematics/v3/weather
2 |
请求方式:
参数类型:
1 2 3 4 5
| 1|参数 |是否必选 |类型 |说明
2|location |Y |string |城市名称
3|output |Y |string |返回数据格式: json
4|ak |Y |string |唯一的应用key(3p49MVra6urFRGOT9s8UBWr2)
5 |
返回示例:
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
| 1{
2 "error": 0,
3 "status": "success",
4 "date": "2019-06-02",
5 "results": [
6 {
7 "currentCity": "北京",
8 "pm25": "119",
9 "index": [
10 {
11 "des": "建议着长袖T恤、衬衫加单裤等服装。年老体弱者宜着针织长袖衬衫、马甲和长裤。",
12 "tipt": "穿衣指数",
13 "title": "穿衣",
14 "zs": "舒适"
15 },
16 {
17 "des": "不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。",
18 "tipt": "洗车指数",
19 "title": "洗车",
20 "zs": "不宜"
21 },
22 {
23 "des": "各项气象条件适宜,无明显降温过程,发生感冒机率较低。",
24 "tipt": "感冒指数",
25 "title": "感冒",
26 "zs": "少发"
27 },
28 {
29 "des": "天气较好,赶快投身大自然参与户外运动,尽情感受运动的快乐吧。",
30 "tipt": "运动指数",
31 "title": "运动",
32 "zs": "适宜"
33 },
34 {
35 "des": "紫外线强度较弱,建议出门前涂擦SPF在12-15之间、PA+的防晒护肤品。",
36 "tipt": "紫外线强度指数",
37 "title": "紫外线强度",
38 "zs": "弱"
39 }
40 ],
41 "weather_data": [
42 {
43 "date": "周日 06月02日 (实时:30℃)",
44 "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png",
45 "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/leizhenyu.png",
46 "weather": "多云转雷阵雨",
47 "wind": "西南风3-4级",
48 "temperature": "31 ~ 20℃"
49 },
50 {
51 "date": "周一",
52 "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png",
53 "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png",
54 "weather": "多云",
55 "wind": "南风微风",
56 "temperature": "34 ~ 20℃"
57 },
58 {
59 "date": "周二",
60 "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/leizhenyu.png",
61 "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/leizhenyu.png",
62 "weather": "雷阵雨",
63 "wind": "东风微风",
64 "temperature": "28 ~ 21℃"
65 },
66 {
67 "date": "周三",
68 "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/duoyun.png",
69 "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png",
70 "weather": "多云",
71 "wind": "北风3-4级",
72 "temperature": "33 ~ 19℃"
73 }
74 ]
75 }
76 ]
77}
78 |
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 43
| 1import ajax from './ajax'
2import jsonp from 'jsonp'
3import {message} from 'antd' //【0】借用antd返回信息组件 默认暴露不要大括号,否则需要大括号
4// const BASE = 'http://localhost:5000'
5const BASE = ''
6
7//导出一个函数,第1种写法
8//登录接口函数
9// export function reqLogin(username,password){
10// return ajax('login',{username,password},'POST')
11// }
12
13//导出一个函数,第2种写法
14// 登录接口函数
15export const reqLogin=(username,password)=>ajax(BASE+'login',{username,password},'POST')
16
17//添加用户接口
18export const AddUser=(user)=>ajax(BASE+'/manage/user/add',user,'POST')
19
20// 【1】天气请求接口函数编写
21export const reqWeather=(city) => {
22 const url = `http://api.map.baidu.com/telematics/v3/weather?location=${city}&output=json&ak=3p49MVra6urFRGOT9s8UBWr2`
23 //返回一个promise函数
24 return new Promise((resolve,reject) => {
25 //发送一个jsonp请求
26 jsonp(url,{},(err,data) => {
27 //输出请求的数据到控制台
28 console.log('jsonp()', err, data)
29 //如果请求成功
30 if(!err && data.status==='success'){
31 //【2】从数据中解构取出图片、天气
32 const {dayPictureUrl,weather}=data.results[0].weather_data[0]
33 //【3】异步返回图片、天气给调用函数者
34 resolve({dayPictureUrl,weather})
35 }else{//如果请求失败
36 //【4】返回错误信息给调用者
37 message.error('天气信息获取失败')
38 }
39 })
40 })
41}
42reqWeather('上海')
43 |
刷新页面即会在控制台输入【1.3 的 返回示例】类似的信息,之所以会直接运行,原因是login页面请求了api,从而触发reqWeather('上海')直接运行
三、header.jsx组件时间部分
3.1时间格式处理工具编写
时间获取:
1 2
| 1Date.now() //返回样式:1581070286188
2 |
时间格式化工具编写 src/utils/dateUtils.js
功能:把【时间获取Date.now()】的时间处理成如下格式 2020-2-6 10:10:10
1 2 3 4 5 6 7 8 9 10 11 12 13
| 1/*
2包含n个日期时间处理的工具函数模块
3*/
4/*
5 格式化日期
6*/
7export function formateDate(time) {
8 if (!time) return ''
9 let date = new Date(time)
10 return date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate()
11 + ' ' + date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds()
12 }
13 |
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 61
| 1import React,{Component} from 'react'
2import './header.less'
3import {formateDate} from '../../../utils/dateUtils.js' //【1】时间格式化工具
4import memoryUtils from '../../../utils/memoryUtils' //【2】内存中存取用户信息工具
5
6export default class Header extends Component{
7
8 state={
9 curentTime:formateDate(Date.now()), //【3】当前时间格式化后的字符串
10 dayPictureUrl:'', //天气小图标地址
11 weather:'', //天气文字
12 }
13
14
15 // 【4】每过一秒获取一次系统时间
16 getTime=()=>{
17 //定时器函数setInterval()
18 this.intervalId = setInterval(()=>{
19 let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
20 this.setState({curentTime}) //更新到state里,实现每秒更新一次时间
21 },1000)
22 }
23
24/* 【5】在第一次render()之后执行一次
25 一般在此执行异步操作: 发ajax请求/启动定时器*/
26 componentDidMount(){
27 this.getTime()
28 }
29
30
31 render(){
32 //【6】解构state内的数据
33 const {curentTime,dayPictureUrl,weather} = this.state
34 //【7】获取memoryUtils中的用户名
35 const username = memoryUtils.user.username
36
37 return(
38 <div className='header'>
39 //【8】把变量填入标签内:用户名、当前时间、天气图标、天气
40 <div className='header-top'>
41 <span>欢迎,{username}</span>
42 <a href='javascript:'>退出</a>
43 </div>
44
45 <div className='header-bottom'>
46 <div className='header-bottom-left'>
47 <span>首页</span>
48 </div>
49
50 <div className='header-bottom-right'>
51 <span>{curentTime}</span>
52 <img src={dayPictureUrl} alt='天气'/>
53 <span>{weather}</span>
54 </div>
55 </div>
56
57 </div>
58 )
59 }
60}
61 |
效果: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 70
| 1import React,{Component} from 'react'
2import './header.less'
3import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
4import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
5import {reqWeather} from '../../../api/index' //【1】引入接口函数,非默认导出,加花括号
6
7export default class Header extends Component{
8
9 state={
10 curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
11 dayPictureUrl:'', //【2】天气小图标地址
12 weather:'', //【3】天气文字
13 }
14
15 //【4】异步获取天气
16 getWeather = async () => {
17 //解构天气小图标,天气
18 const {dayPictureUrl, weather} = await reqWeather('上海')
19 //更新状态
20 this.setState({dayPictureUrl, weather})
21 }
22
23 // 每过一秒获取一次系统时间
24 getTime=()=>{
25 //定时器函数setInterval()
26 this.intervalId = setInterval(()=>{
27 let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
28 this.setState({curentTime})
29 },1000)
30 }
31
32/* 在第一次render()之后执行一次
33 一般在此执行异步操作: 发ajax请求/启动定时器*/
34 componentDidMount(){
35 this.getTime() //调用时间,因为getTime内部写了定时,所以每隔1秒会更新一次时间state,页面就会重新渲染一次
36 this.getWeather() //【5】渲染之后调用一次天气
37 }
38
39
40 render(){
41 //【6】解构state内的数据
42 const {curentTime,dayPictureUrl,weather} = this.state
43 //获取用户名
44 const username = memoryUtils.user.username
45
46 return(
47 <div className='header'>
48
49 <div className='header-top'>
50 <span>欢迎,{username}</span>
51 <a href='javascript:'>退出</a>
52 </div>
53
54 <div className='header-bottom'>
55 <div className='header-bottom-left'>
56 <span>首页</span>
57 </div>
58 {/*【7】写入页面*/}
59 <div className='header-bottom-right'>
60 <span>{curentTime}</span>
61 <img src={dayPictureUrl} alt='天气'/>
62 <span>{weather}</span>
63 </div>
64 </div>
65
66 </div>
67 )
68 }
69}
70 |
效果:
五、显示当前路径模块
【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 106
| 1import React,{Component} from 'react'
2import './header.less'
3import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
4import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
5import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号
6
7//import {message} from 'antd'
8import {withRouter} from 'react-router-dom' //【1】用于包装当前组件,使其具有路由的3属性history
9import menuList from '../../../config/menuConfig.js' //【2】导入导航配置菜单
10
11class Header extends Component{
12
13 state={
14 curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
15 dayPictureUrl:'', //天气小图标地址
16 weather:'', //天气文字
17 }
18
19 // 获取路径
20 // getPath=()=>{
21
22 // }
23
24//【3】根据当前网址,在menuListConfig内找到对应的title
25 getTitle = () => {
26 // 得到当前请求路径
27 const path = this.props.location.pathname
28 let title
29 menuList.forEach(item => {
30 if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
31 title = item.title
32 } else if (item.children) {
33 // 在所有子item中查找匹配的
34 const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
35 // 如果有值才说明有匹配的
36 if(cItem) {
37 // 取出它的title
38 title = cItem.title
39 }
40 }
41 })
42 return title //返回title
43 }
44
45 //异步获取天气
46 getWeather = async () => {
47 //解构天气小图标,天气
48 const {dayPictureUrl, weather} = await reqWeather('上海')
49 //更新状态
50 this.setState({dayPictureUrl, weather})
51 }
52
53 // 每过一秒获取一次系统时间
54 getTime=()=>{
55 //定时器函数setInterval()
56 this.intervalId = setInterval(()=>{
57 let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
58 this.setState({curentTime})
59 },1000)
60 }
61
62/* 在第一次render()之后执行一次
63 一般在此执行异步操作: 发ajax请求/启动定时器*/
64 componentDidMount(){
65 this.getTime()
66 this.getWeather()
67 }
68
69
70 render(){
71 //解构state内的数据
72 const {curentTime,dayPictureUrl,weather} = this.state
73 //获取用户名
74 const username = memoryUtils.user.username
75
76 // 【4】得到当前需要显示的title
77 const title = this.getTitle()
78
79 return(
80 <div className='header'>
81
82 <div className='header-top'>
83 <span>欢迎,{username}</span>
84 <a href='javascript:'>退出</a>
85 </div>
86
87 <div className='header-bottom'>
88 <div className='header-bottom-left'>
89 {/*【5】显示title*/}
90 <span>{title}</span>
91 </div>
92
93 <div className='header-bottom-right'>
94 <span>{curentTime}</span>
95 <img src={dayPictureUrl} alt='天气'/>
96 <span>{weather}</span>
97 </div>
98 </div>
99
100 </div>
101 )
102 }
103}
104
105export default withRouter(Header)//【1.1】包装起来,使当前组件具有路由组件的属性
106 |
六、退出登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 1import {Modal} from 'antd'
2
3//退出登录
4 loginOut=()=>{
5 Modal.confirm({
6 title: '确定要退出登录吗?',
7 content: '是请点确定,否则点取消',
8 onOk() {
9 console.log('OK');
10 },
11 onCancel() {
12 console.log('Cancel');
13 },
14 })
15 }
16 |
完整代码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 127
| 1import React,{Component} from 'react'
2import './header.less'
3import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
4import memoryUtils from '../../../utils/memoryUtils' //【1】内存中存取用户信息工具 默认导出,不用加花括号
5import storageUtils from '../../../utils/storageUtils' //【2】删除localstorage中的用户登录数据
6import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号
7
8import {withRouter} from 'react-router-dom' //用于包装当前组件,使其具体路由的3属性history
9import menuList from '../../../config/menuConfig.js' //导入导航配置菜单
10
11import {Modal} from 'antd' //【3】引入对话框模块
12
13class Header extends Component{
14
15 state={
16 curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
17 dayPictureUrl:'', //天气小图标地址
18 weather:'', //天气文字
19 }
20
21 // 获取路径
22 // getPath=()=>{
23
24 // }
25
26 getTitle = () => {
27 // 得到当前请求路径
28 const path = this.props.location.pathname
29 let title
30 menuList.forEach(item => {
31 if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
32 title = item.title
33 } else if (item.children) {
34 // 在所有子item中查找匹配的
35 const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
36 // 如果有值才说明有匹配的
37 if(cItem) {
38 // 取出它的title
39 title = cItem.title
40 }
41 }
42 })
43 return title
44 }
45
46 //异步获取天气
47 getWeather = async () => {
48 //解构天气小图标,天气
49 const {dayPictureUrl, weather} = await reqWeather('上海')
50 //更新状态
51 this.setState({dayPictureUrl, weather})
52 }
53
54 // 每过一秒获取一次系统时间
55 getTime=()=>{
56 //定时器函数setInterval()
57 this.intervalId = setInterval(()=>{
58 let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
59 this.setState({curentTime})
60 },1000)
61 }
62
63 //【4】退出登录函数
64 loginOut=()=>{
65 Modal.confirm({
66 title: '确定要退出登录吗?',
67 content: '是请点确定,否则点取消',
68 onOk:()=> {//【5】改成前头函数,因为下面要用到this.props.history.replace()
69 console.log('OK');
70 //【6】删除localstorage中登录信息。及内存中登录信息
71 storageUtils.removeUser()
72 memoryUtils.user={}//【7】删除内存中user信息
73 //【8】跳转到登录页面,用替换因为无需退回
74 this.props.history.replace('/login')
75 }//,取消时什么也不做,所以可省略不写
76 // onCancel() {
77 // console.log('Cancel');
78 // },
79 })
80
81 }
82
83//在第一次render()之后执行一次
84 //一般在此执行异步操作: 发ajax请求启动定时器
85 componentDidMount(){
86 this.getTime();
87 this.getWeather();
88 }
89
90
91 render(){
92 //解构state内的数据
93 const {curentTime,dayPictureUrl,weather} = this.state
94 //获取用户名
95 const username = memoryUtils.user.username
96
97 // 得到当前需要显示的title
98 const title = this.getTitle()
99
100 return(
101 <div className='header'>
102
103 <div className='header-top'>
104 <span>欢迎,{username}</span>
105 {/*【9】*/}
106 <a href='javascript:' onClick={this.loginOut}>退出</a>
107 </div>
108
109 <div className='header-bottom'>
110 <div className='header-bottom-left'>
111 <span>{title}</span>
112 </div>
113
114 <div className='header-bottom-right'>
115 <span>{curentTime}</span>
116 <img src={dayPictureUrl} alt='天气'/>
117 <span>{weather}</span>
118 </div>
119 </div>
120
121 </div>
122 )
123 }
124}
125
126export default withRouter(Header)
127 |
效果:
七、警告完善
退出后控制台会提示如下信息:
此为定时器函数未清除造成的问题
1 2 3 4 5 6 7 8 9 10 11
| 1Warning: 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.
2 in Header (created by Context.Consumer)
3 in withRouter(Header) (at admin.jsx:42)
4 in section (created by BasicLayout)
5 in BasicLayout (created by Context.Consumer)
6 in Adapter (at admin.jsx:41)
7 in section (created by BasicLayout)
8 in BasicLayout (created by Context.Consumer)
9 in Adapter (at admin.jsx:37)
10 in Admin (created by Context.Consumer)
11 |
解决:清除定时器函数
1 2 3 4 5 6 7 8
| 1/*
2 当前组件卸载之前调用
3 */
4 componentWillUnmount () {
5 // 清除定时器
6 clearInterval(this.intervalId)
7 }
8 |
完整代码
【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
| 1import React,{Component} from 'react'
2import './header.less'
3import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
4import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
5import storageUtils from '../../../utils/storageUtils' //删除localstorage中的用户登录数据
6import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号
7
8import {withRouter} from 'react-router-dom' //用于包装当前组件,使其具体路由的3属性history
9import menuList from '../../../config/menuConfig.js' //导入导航配置菜单
10
11import {Modal} from 'antd'
12
13class Header extends Component{
14
15 state={
16 curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
17 dayPictureUrl:'', //天气小图标地址
18 weather:'', //天气文字
19 }
20
21 // 获取路径
22 // getPath=()=>{
23
24 // }
25
26 getTitle = () => {
27 // 得到当前请求路径
28 const path = this.props.location.pathname
29 let title
30 menuList.forEach(item => {
31 if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
32 title = item.title
33 } else if (item.children) {
34 // 在所有子item中查找匹配的
35 const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
36 // 如果有值才说明有匹配的
37 if(cItem) {
38 // 取出它的title
39 title = cItem.title
40 }
41 }
42 })
43 return title
44 }
45
46 //异步获取天气
47 getWeather = async () => {
48 //解构天气小图标,天气
49 const {dayPictureUrl, weather} = await reqWeather('上海')
50 //更新状态
51 this.setState({dayPictureUrl, weather})
52
53
54 }
55
56 // 每过一秒获取一次系统时间
57 getTime=()=>{
58 //定时器函数setInterval()
59 this.intervalId = setInterval(()=>{
60 let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
61 this.setState({curentTime})
62 },1000)
63 }
64
65 //退出登录
66 loginOut=()=>{
67 Modal.confirm({
68 title: '确定要退出登录吗?',
69 content: '是请点确定,否则点取消',
70 onOk:()=> {//改成前头函数,因为下面要用到this.props.history.replace()
71 console.log('OK');
72 //删除localstorage中登录信息。及内存中登录信息
73 storageUtils.removeUser()
74 memoryUtils.user={}
75 //跳转到登录页面,用替换因为无需退回
76 this.props.history.replace('/login')
77 }//,取消时什么也不做,所以可省略不写
78 // onCancel() {
79 // console.log('Cancel');
80 // },
81 })
82
83 }
84
85//在第一次render()之后执行一次
86 //一般在此执行异步操作: 发ajax请求启动定时器
87 componentDidMount(){
88 this.getTime();
89 this.getWeather();
90 }
91
92/*
93 【1】当前组件卸载之前调用清除定时器,避免其造成警告信息
94 */
95 componentWillUnmount () {
96 // 清除定时器
97 clearInterval(this.intervalId)
98 }
99
100
101 render(){
102 //解构state内的数据
103 const {curentTime,dayPictureUrl,weather} = this.state
104 //获取用户名
105 const username = memoryUtils.user.username
106
107 // 得到当前需要显示的title
108 const title = this.getTitle()
109
110 return(
111 <div className='header'>
112
113 <div className='header-top'>
114 <span>欢迎,{username}</span>
115 <a href='javascript:' onClick={this.loginOut}>退出</a>
116 </div>
117
118 <div className='header-bottom'>
119 <div className='header-bottom-left'>
120 <span>{title}</span>
121 </div>
122
123 <div className='header-bottom-right'>
124 <span>{curentTime}</span>
125 <img src={dayPictureUrl} alt='天气'/>
126 <span>{weather}</span>
127 </div>
128 </div>
129
130 </div>
131 )
132 }
133}
134
135export default withRouter(Header)
136 |
优化<a链接为button自定义组件
/src/component/link-button/index.jsx
1 2 3 4 5 6 7
| 1import React,{component} from 'react'
2import './index.less'
3
4export default function LinkButton(props){
5 return <button {...props} className='link-button'></button>
6}
7 |
index.less
1 2 3 4 5 6 7 8
| 1.link-button{
2 background-color: transparent;
3 border: none;
4 outline: none;
5 color: #1da57a;
6 cursor: pointer;
7}
8 |
页面引入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 138
| 1import React,{Component} from 'react'
2import './header.less'
3import {formateDate} from '../../../utils/dateUtils.js' //时间格式化工具
4import memoryUtils from '../../../utils/memoryUtils' //内存中存取用户信息工具 默认导出,不用加花括号
5import storageUtils from '../../../utils/storageUtils' //删除localstorage中的用户登录数据
6import {reqWeather} from '../../../api/index' //引入接口函数,非默认导出,加花括号
7
8import {withRouter} from 'react-router-dom' //用于包装当前组件,使其具体路由的3属性history
9import menuList from '../../../config/menuConfig.js' //导入导航配置菜单
10
11import {Modal} from 'antd'
12import LinkButton from '../../../components/link-button/index'//【1】引入自定按键
13
14class Header extends Component{
15
16 state={
17 curentTime:formateDate(Date.now()), //当前时间格式化后的字符串
18 dayPictureUrl:'', //天气小图标地址
19 weather:'', //天气文字
20 }
21
22 // 获取路径
23 // getPath=()=>{
24
25 // }
26
27 getTitle = () => {
28 // 得到当前请求路径
29 const path = this.props.location.pathname
30 let title
31 menuList.forEach(item => {
32 if (item.key===path) { // 如果当前item对象的key与path一样,item的title就是需要显示的title
33 title = item.title
34 } else if (item.children) {
35 // 在所有子item中查找匹配的
36 const cItem = item.children.find(cItem => path.indexOf(cItem.key)===0)
37 // 如果有值才说明有匹配的
38 if(cItem) {
39 // 取出它的title
40 title = cItem.title
41 }
42 }
43 })
44 return title
45 }
46
47 //异步获取天气
48 getWeather = async () => {
49 //解构天气小图标,天气
50 const {dayPictureUrl, weather} = await reqWeather('上海')
51 //更新状态
52 this.setState({dayPictureUrl, weather})
53
54
55 }
56
57 // 每过一秒获取一次系统时间
58 getTime=()=>{
59 //定时器函数setInterval()
60 this.intervalId = setInterval(()=>{
61 let curentTime=formateDate(Date.now()) //获取当前时间并格式化为字符串
62 this.setState({curentTime})
63 },1000)
64 }
65
66 //退出登录
67 loginOut=()=>{
68 Modal.confirm({
69 title: '确定要退出登录吗?',
70 content: '是请点确定,否则点取消',
71 onOk:()=> {//改成前头函数,因为下面要用到this.props.history.replace()
72 console.log('OK');
73 //删除localstorage中登录信息。及内存中登录信息
74 storageUtils.removeUser()
75 memoryUtils.user={}
76 //跳转到登录页面,用替换因为无需退回
77 this.props.history.replace('/login')
78 }//,取消时什么也不做,所以可省略不写
79 // onCancel() {
80 // console.log('Cancel');
81 // },
82 })
83
84 }
85
86//在第一次render()之后执行一次
87 //一般在此执行异步操作: 发ajax请求启动定时器
88 componentDidMount(){
89 this.getTime();
90 this.getWeather();
91 }
92
93/*
94 当前组件卸载之前调用清除定时器,避免其造成警告信息
95 */
96 componentWillUnmount () {
97 // 清除定时器
98 clearInterval(this.intervalId)
99 }
100
101
102 render(){
103 //解构state内的数据
104 const {curentTime,dayPictureUrl,weather} = this.state
105 //获取用户名
106 const username = memoryUtils.user.username
107
108 // 得到当前需要显示的title
109 const title = this.getTitle()
110
111 return(
112 <div className='header'>
113
114 <div className='header-top'>
115 <span>欢迎,{username}</span>
116 {/*【2】使用自定义组件*/}
117 <LinkButton href='javascript:' onClick={this.loginOut}>退出</LinkButton>
118 </div>
119
120 <div className='header-bottom'>
121 <div className='header-bottom-left'>
122 <span>{title}</span>
123 </div>
124
125 <div className='header-bottom-right'>
126 <span>{curentTime}</span>
127 <img src={dayPictureUrl} alt='天气'/>
128 <span>{weather}</span>
129 </div>
130 </div>
131
132 </div>
133 )
134 }
135}
136
137export default withRouter(Header)
138 |
效果: