项目需求:
1、服务侧推送给消息给前端,前端需要展示在右下角
2、根据不同的消息类型,提供不同的操作按钮‘同意’、‘拒绝’等
代码设计:
1、使用websocket方式建立通道
2、前端基于umi+antd+reconnecting-websocket.js开发
3、使用express+express-ws+mockjs建立websocket服务通道,模拟服务端推送消息
使用方法:
1、项目中已引入reconnecting-websocket.min.js,详见其官方文档
2、登录成功后,接着调用websocket初始化:
1
2
3
4
5
6
|
yield put({ type: 'websocket/init', payload: { authToken } }); |
核心代码:
1、/service/websocket.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
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
|
/** * 基于reconnecting-websocket库已引入 * 封装service文件 */ class Websocket{ /** * websocket逻辑 * 2021-10-28 */ constructor(){ this.websocket=null; this.url='ws://127.0.0.1:30001/websocket-im'; this.options={ connectionTimeout: 5000, maxRetries: 10, }; } init=()=>{ this.websocket = new ReconnectingWebSocket(this.url,[], this.options); } close=()=>{ this.websocket && this.websocket.close(); } onMessage=(callback)=>{ this.websocket && this.websocket.addEventListener('message', (e) => { callback&&callback(e) }); } } const websocket = new Websocket(); // 初始化连接 export function openWs() { return websocket.init(); } // 关闭连接 export function closeWs() { return websocket.close(); } // 监听websocket消息 export function onMessage() { let deferred; websocket.onMessage(function(e){ if(deferred) { deferred.resolve(e) deferred = null } }); return { message() { if(!deferred) { deferred = {} deferred.promise = new Promise(resolve => deferred.resolve = resolve) } return deferred.promise; } } } |
2、/model/websocket.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
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
|
/** * 封装model文件 * moment、immutable、antd、nanoid组件请自行学习 */ import {openWs,onMessage,closeWs} from 'services/websocket' import moment from 'moment' import { Map, fromJS } from 'immutable' import { notification } from 'antd' import nanoid from 'nanoid' ; const initState = Map({ message:Map(), //收到的消息 }); export default { namespace: 'websocket' , state: initState, subscriptions: { setup({ dispatch, history }) { dispatch({ type: 'listener' }); return history.listen(({ pathname, query }) => { }); }, }, effects: { * listener({ payload }, { take, put, call }) { while ( true ) { const { type, payload } = yield take([ 'logout' ]); // 监听退出系统,则关闭websocket if (type === 'logout' ) { // 关闭websocket yield call(closeWs); notification.destroy(); yield put({ type: 'clearAllMessage' , payload:{ } }); } } }, // 启动websocket * init ({ payload, }, { put, call, select }) { yield call(openWs); const listener = yield call(onMessage); yield put({type: 'receiveMsg' , payload:{listener}}); }, // 接受消息 * receiveMsg ({ payload: {listener} }, { call, select, put}) { while ( true ){ const event = yield call(listener.message); yield put({ type: 'progressMsg' , payload:{ msg:JSON.parse(event.data) } }); } }, // 统筹消息 * progressMsg ({ payload: {msg} }, { call, select, put}) { console.log(msg) yield put({ type: 'addOneMessage' , payload:{ msg } }); }, }, reducers: { addOneMessage(state, { payload:{msg} }) { const msgId = nanoid()+ '-' +moment().format( 'x' ); return state.setIn([ 'message' ,msgId], fromJS({...msg,msgId})) }, removeOneMessage(state, { payload:{msgId} }) { return state.deleteIn([ 'message' ,msgId]) }, clearAllMessage(state, { payload:{} }) { return state.setIn([ 'message' ],Map()) }, }, } |
3、Notification组件封装,结构及代码
(1)package.json
1
2
3
4
5
6
|
{ "name" : "Notification" , "version" : "0.0.0" , "private" : true , "main" : "./index.js" } |
(2) index.less
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.Notification{ .btns{ padding: 0; margin: 15px 0 0 0; list-style: none; width: 100%; display: flex; justify-content: flex-end; li{ margin-left: 10px; } } } |
(3)index.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
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
|
/** * 右下角弹窗组件封装 */ import React from 'react' import { injectIntl } from 'react-intl' ; import moment from 'moment' import { connect } from 'dva' import { notification } from 'antd' ; import Demo1 from './Demo1' import Demo2 from './Demo2' @injectIntl @connect(({ websocket, }) => ({ websocket })) export default class Notification extends React.Component { componentWillReceiveProps(nextProps) { const {websocket,dispatch,intl, intl: { formatMessage }} = nextProps; let message=websocket.get( 'message' ); message.forEach((note)=>{ let object=note.getIn([ 'object' ]); let msgId=note.getIn([ 'msgId' ]); let title=note.getIn([ 'title' ]); let content=note.getIn([ 'content' ]); let format = 'YYYY-MM-DD HH:mm:ss' ; let time=note.getIn([ 'ts' ])?moment(note.getIn([ 'ts' ]), 'x' ).format(format):moment().format(format); switch (object) { case 'demo1' : content=<Demo1 dispatch={dispatch} intl={intl} note={note} onClose={()=> this .onClose(msgId)} />; break ; case 'demo2' : content=<Demo2 dispatch={dispatch} intl={intl} note={note} onClose={()=> this .onClose(msgId)} />; break ; default : break ; } notification.open({ message: <span>{title} <small>{time}</small></span>, duration:30, key: msgId, description:content, placement: 'bottomRight' , onClick: () => { }, onClose: () => { this .onClose(msgId); } }); }) } // 关闭消息 onClose=(msgId)=>{ const {dispatch} = this .props; dispatch({ type: 'websocket/removeOneMessage' , payload:{ msgId } }) return notification.close(msgId); } render(){ return ( null ) } } Notification.propTypes = { } |
(4)Demo1.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import React from 'react' import styles from './index.less' export default class NotificationSon extends React.Component { render(){ const {note,intl:{formatMessage}} = this .props; let content=note.getIn([ 'content' ]); return ( <div className={styles.Notification}> <div>{content}</div> </div> ) } } NotificationSon.propTypes = { } |
(5)Demo2.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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
import React from 'react' import styles from './index.less' import { config } from 'utils' import { Button } from 'antd' ; const { defaultStyleSize } = config; export default class NotificationSon extends React.Component { dealApproval=(type,data)=>{ const {dispatch,onClose} = this .props; if (type== 'refuse' ){ console.log( '拒绝' ) onClose(); } else if (type== 'agree' ){ console.log( '同意' ) onClose(); } } render(){ const {note,intl:{formatMessage}} = this .props; let content=note.getIn([ 'content' ]); return ( <div className={styles.Notification}> <div>{content}</div> <ul className={styles.btns}> <li> <Button style={{ marginLeft: '12px' }} type={ 'primary' } size={defaultStyleSize} onClick={() => { this .dealApproval( 'agree' ,note.get( 'data' ))}}>{formatMessage({id: 'Global.agree' })}</Button> </li> <li> <Button style={{ marginLeft: '12px' }} type={ 'danger' } size={defaultStyleSize} onClick={() => { this .dealApproval( 'refuse' ,note.get( 'data' ))}}>{formatMessage({id: 'Global.refuse' })}</Button> </li> </ul> </div> ) } } NotificationSon.propTypes = { } |
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容