├── app.js ├── app.json ├── app.wxss ├── config.js ├── pages ├── chat │ ├── chat.js │ ├── chat.json │ ├── chat.wxml │ ├── chat.wxss │ └── images │ │ ├── avator.png │ │ └── say.jpg └── index │ ├── index.js │ ├── index.wxml │ ├── index.wxss │ └── user-unlogin.png ├── project.config.json ├── utils ├── util.js └── websocket.js └── vendor └── wafer2-client-sdk ├── index.js ├── lib ├── constants.js ├── login.js ├── request.js ├── session.js ├── tunnel.js ├── utils.js └── wxTunnel.js └── package.json /app.js: -------------------------------------------------------------------------------- 1 | //app.js 2 | App({ 3 | onLaunch: function () { 4 | // 展示本地存储能力 5 | var logs = wx.getStorageSync('logs') || [] 6 | logs.unshift(Date.now()) 7 | wx.setStorageSync('logs', logs) 8 | 9 | // 登录 10 | wx.login({ 11 | success: res => { 12 | wx.showToast({ 13 | title: '已登陆', 14 | icon: "none", 15 | duration: 2000 16 | }) 17 | if (res.code) { 18 | //发起网络请求 19 | // wx.request({ 20 | // url: 'https://rylvdab1.qcloud.la', 21 | // data: { 22 | // code: res.code 23 | // }, 24 | // success:function(res){ 25 | // console.log(res.data) 26 | // } 27 | // }) 28 | } else { 29 | console.log('获取用户登录态失败!' + res.errMsg) 30 | } 31 | // 发送 res.code 到后台换取 openId, sessionKey, unionId 32 | } 33 | }) 34 | // 获取用户信息 35 | wx.getSetting({ 36 | success: res => { 37 | if (res.authSetting['scope.userInfo']) { 38 | // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 39 | wx.getUserInfo({ 40 | success: res => { 41 | // 可以将 res 发送给后台解码出 unionId 42 | // console.log(res) 43 | this.globalData.userInfo = res.userInfo 44 | 45 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 46 | // 所以此处加入 callback 以防止这种情况 47 | if (this.userInfoReadyCallback) { 48 | this.userInfoReadyCallback(res) 49 | } 50 | } 51 | }) 52 | } 53 | } 54 | }) 55 | }, 56 | globalData: { 57 | userInfo: null 58 | } 59 | }) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/index/index", 4 | "pages/chat/chat" 5 | ], 6 | "window":{ 7 | "backgroundTextStyle":"black", 8 | "navigationBarBackgroundColor": "#eee", 9 | "navigationBarTitleText": "首页", 10 | "navigationBarTextStyle":"black", 11 | "navigationStyle":"default" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 200rpx 0; 9 | box-sizing: border-box; 10 | } 11 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 小程序配置文件 3 | */ 4 | 5 | // 此处主机域名修改成腾讯云解决方案分配的域名 6 | var host = 'https://rylvdab1.qcloud.la'; 7 | 8 | var config = { 9 | 10 | // 下面的地址配合云端 Demo 工作 11 | service: { 12 | host, 13 | 14 | // 登录地址,用于建立会话 15 | loginUrl: `${host}/weapp/login`, 16 | 17 | // 测试的请求地址,用于测试会话 18 | requestUrl: `${host}/weapp/user`, 19 | 20 | // 测试的信道服务地址 21 | tunnelUrl: `${host}/weapp/tunnel`, 22 | 23 | // 上传图片接口 24 | uploadUrl: `${host}/weapp/upload` 25 | } 26 | }; 27 | 28 | module.exports = config; 29 | -------------------------------------------------------------------------------- /pages/chat/chat.js: -------------------------------------------------------------------------------- 1 | // pages/chat/chat.js 2 | const app = getApp() 3 | var websocket = require('../../utils/websocket.js'); 4 | var utils = require('../../utils/util.js'); 5 | Page({ 6 | 7 | /** 8 | * 页面的初始数据 9 | */ 10 | data: { 11 | newslist:[], 12 | userInfo: {}, 13 | scrollTop: 0, 14 | increase:false,//图片添加区域隐藏 15 | aniStyle: true,//动画效果 16 | message:"", 17 | previewImgList:[] 18 | }, 19 | /** 20 | * 生命周期函数--监听页面加载 21 | */ 22 | onLoad: function () { 23 | var that = this 24 | if (app.globalData.userInfo) { 25 | this.setData({ 26 | userInfo: app.globalData.userInfo 27 | }) 28 | } 29 | //调通接口 30 | websocket.connect(this.data.userInfo, function (res) { 31 | // console.log(JSON.parse(res.data)) 32 | var list = [] 33 | list = that.data.newslist 34 | list.push(JSON.parse(res.data)) 35 | that.setData({ 36 | newslist: list 37 | }) 38 | }) 39 | }, 40 | // 页面卸载 41 | onUnload(){ 42 | wx.closeSocket(); 43 | wx.showToast({ 44 | title: '连接已断开~', 45 | icon: "none", 46 | duration: 2000 47 | }) 48 | }, 49 | //事件处理函数 50 | send: function () { 51 | var flag = this 52 | if (this.data.message.trim() == ""){ 53 | wx.showToast({ 54 | title: '消息不能为空哦~', 55 | icon: "none", 56 | duration: 2000 57 | }) 58 | }else { 59 | setTimeout(function(){ 60 | flag.setData({ 61 | increase: false 62 | }) 63 | },500) 64 | websocket.send('{ "content": "' + this.data.message + '", "date": "' + utils.formatTime(new Date()) + '","type":"text", "nickName": "' + this.data.userInfo.nickName + '", "avatarUrl": "' + this.data.userInfo.avatarUrl+'" }') 65 | this.bottom() 66 | } 67 | }, 68 | //监听input值的改变 69 | bindChange(res) { 70 | this.setData({ 71 | message : res.detail.value 72 | }) 73 | }, 74 | cleanInput(){ 75 | //button会自动清空,所以不能再次清空而是应该给他设置目前的input值 76 | this.setData({ 77 | message: this.data.message 78 | }) 79 | }, 80 | increase() { 81 | this.setData({ 82 | increase: true, 83 | aniStyle: true 84 | }) 85 | }, 86 | //点击空白隐藏message下选框 87 | outbtn(){ 88 | this.setData({ 89 | increase: false, 90 | aniStyle: true 91 | }) 92 | }, 93 | chooseImage() { 94 | var that = this 95 | wx.chooseImage({ 96 | count: 1, // 默认9 97 | sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有 98 | sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有 99 | success: function (res) { 100 | // 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片 101 | var tempFilePaths = res.tempFilePaths 102 | // console.log(tempFilePaths) 103 | wx.uploadFile({ 104 | url: 'http://192.168.137.91/index/index/upload', //仅为示例,非真实的接口地址 105 | filePath: tempFilePaths[0], 106 | name: 'file', 107 | headers: { 108 | 'Content-Type': 'form-data' 109 | }, 110 | success: function (res) { 111 | if (res.data){ 112 | that.setData({ 113 | increase: false 114 | }) 115 | websocket.send('{"images":"'+ res.data +'","date":"'+utils.formatTime(new Date())+'","type":"image","nickName":"'+that.data.userInfo.nickName+'","avatarUrl":"'+that.data.userInfo.avatarUrl+'"}') 116 | that.bottom() 117 | } 118 | } 119 | }) 120 | 121 | } 122 | }) 123 | }, 124 | //图片预览 125 | previewImg(e){ 126 | var that = this 127 | //必须给对应的wxml的image标签设置data-set=“图片路径”,否则接收不到 128 | var res = e.target.dataset.src 129 | var list = this.data.previewImgList //页面的图片集合数组 130 | 131 | //判断res在数组中是否存在,不存在则push到数组中, -1表示res不存在 132 | if (list.indexOf(res) == -1 ) { 133 | this.data.previewImgList.push(res) 134 | } 135 | wx.previewImage({ 136 | current: res, // 当前显示图片的http链接 137 | urls: that.data.previewImgList // 需要预览的图片http链接列表 138 | }) 139 | 140 | }, 141 | //聊天消息始终显示最底端 142 | bottom: function () { 143 | var query = wx.createSelectorQuery() 144 | query.select('#flag').boundingClientRect() 145 | query.selectViewport().scrollOffset() 146 | query.exec(function (res) { 147 | wx.pageScrollTo({ 148 | scrollTop: res[0].bottom // #the-id节点的下边界坐标 149 | }) 150 | res[1].scrollTop // 显示区域的竖直滚动位置 151 | }) 152 | }, 153 | }) -------------------------------------------------------------------------------- /pages/chat/chat.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": " ♥ 聊天室" 3 | } -------------------------------------------------------------------------------- /pages/chat/chat.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 系统消息: 欢迎 {{ userInfo.nickName }} 加入群聊 4 | 5 | 6 | 7 | 8 | 9 | 23 | {{item.date}} 24 | 25 | 26 | 27 | {{ item.nickName }} 28 | 29 | 30 | 31 | 32 | {{item.content}} 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {{ item.nickName }} 44 | 45 | 46 | 47 | {{item.content}} 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 | -------------------------------------------------------------------------------- /pages/chat/chat.wxss: -------------------------------------------------------------------------------- 1 | /* pages/chat/chat.wxss */ 2 | page { 3 | background-color: #f7f7f7; 4 | height: 100%; 5 | } 6 | /* 聊天内容 */ 7 | .news { 8 | padding-top: 30rpx; 9 | text-align: center; 10 | /* height:100%; */ 11 | box-sizing:border-box; 12 | } 13 | #flag{ 14 | margin-bottom: 100rpx; 15 | height: 30rpx; 16 | } 17 | .chat-notice{ 18 | text-align: center; 19 | font-size: 30rpx; 20 | padding: 10rpx 0; 21 | color: #666; 22 | } 23 | .historycon { 24 | height: 100%; 25 | width: 100%; 26 | /* flex-direction: column; */ 27 | display: flex; 28 | border-top: 0px; 29 | } 30 | /* .img_null { 31 | height: 60rpx; 32 | } */ 33 | 34 | /* .l { 35 | height: 5rpx; 36 | width: 20%; 37 | margin-top: 30rpx; 38 | color: #000; 39 | } */ 40 | 41 | /* 聊天 */ 42 | .chat-news{ 43 | width: 100%; 44 | overflow: hidden; 45 | } 46 | .chat-news .my_right { 47 | float: right; 48 | /* right: 40rpx; */ 49 | padding: 10rpx 10rpx; 50 | } 51 | .chat-news .name{ 52 | margin-right: 10rpx; 53 | } 54 | .chat-news .you_left { 55 | float: left; 56 | /* left: 5rpx; */ 57 | padding: 10rpx 10rpx; 58 | } 59 | .selectImg{ 60 | display: inline-block; 61 | width: 150rpx; 62 | height: 150rpx; 63 | margin-left: 50rpx; 64 | } 65 | .my_right .selectImg{ 66 | margin-right: 80rpx; 67 | } 68 | .new_img { 69 | width: 60rpx; 70 | height: 60rpx; 71 | border-radius: 50%; 72 | vertical-align: middle; 73 | margin-right: 10rpx; 74 | } 75 | .new_txt { 76 | max-width: 300rpx; 77 | display: inline-block; 78 | border-radius: 6rpx; 79 | line-height: 60rpx; 80 | background-color: #95d4ff; 81 | padding: 5rpx 20rpx; 82 | margin: 0 10rpx; 83 | margin-left: 50rpx; 84 | } 85 | .my_right .new_txt{ 86 | margin-right:60rpx; 87 | } 88 | .you{ 89 | background-color: lightgreen; 90 | } 91 | 92 | /* .sanjiao { 93 | top: 70rpx; 94 | position: relative; 95 | width: 0px; 96 | height: 0px; 97 | border-width: 10px; 98 | border-style: solid; 99 | } */ 100 | 101 | .my { 102 | border-color: transparent transparent transparent #95d4ff; 103 | } 104 | 105 | .you { 106 | border-color: transparent #95d4ff transparent transparent; 107 | } 108 | 109 | .hei{ 110 | margin-top: 50px; 111 | height: 20rpx; 112 | } 113 | .history { 114 | height: 100%; 115 | margin-top: 15px; 116 | padding: 10rpx; 117 | font-size: 14px; 118 | line-height: 40px; 119 | word-break: break-all; 120 | } 121 | ::-webkit-scrollbar { 122 | width: 0; 123 | height: 0; 124 | color: transparent; 125 | z-index: -1; 126 | } 127 | 128 | /* 信息输入区域 */ 129 | .message{ 130 | position: fixed; 131 | bottom:0; 132 | width: 100%; 133 | } 134 | .sendMessage{ 135 | display: block; 136 | height: 80rpx; 137 | padding: 10rpx 10rpx; 138 | background-color: #fff; 139 | border-top: 2rpx solid #eee; 140 | border-bottom: 2rpx solid #eee; 141 | z-index:3; 142 | } 143 | .sendMessage input{ 144 | float:left; 145 | width: 66%; 146 | height: 100%; 147 | line-height: 80rpx; 148 | border-bottom: 1rpx solid #ccc; 149 | padding:0 10rpx; 150 | font-size: 35rpx; 151 | color: #666; 152 | } 153 | .sendMessage view{ 154 | display: inline-block; 155 | width: 80rpx; 156 | height: 80rpx; 157 | line-height: 80rpx; 158 | font-size: 60rpx; 159 | text-align: center; 160 | color: #999; 161 | border: 1rpx solid #ccc; 162 | border-radius: 50%; 163 | margin-left: 10rpx; 164 | } 165 | .sendMessage button { 166 | float: right; 167 | font-size: 35rpx; 168 | } 169 | .increased{ 170 | width:100%; 171 | /* height: 150rpx; */ 172 | padding: 40rpx 30rpx; 173 | background-color: #fff; 174 | } 175 | .increased .image{ 176 | width: 100rpx; 177 | height: 100rpx; 178 | border: 3rpx solid #ccc; 179 | line-height: 100rpx; 180 | text-align: center; 181 | border-radius: 8rpx; 182 | font-size:35rpx; 183 | } 184 | @keyframes slidedown { 185 | from { 186 | transform: translateY(0); 187 | } 188 | to { 189 | transform: translateY(100%); 190 | } 191 | } 192 | .slidedown { 193 | animation: slidedown 0.5s linear ; 194 | } 195 | .slideup { 196 | animation: slideup 0.5s linear ; 197 | } 198 | @keyframes slideup { 199 | from { 200 | transform: translateY(100%); 201 | } 202 | to { 203 | transform: translateY(0); 204 | } 205 | } -------------------------------------------------------------------------------- /pages/chat/images/avator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chongwenwen/weixin_chat/8928a7f9c37082edf058d359c9d3984b20eb791b/pages/chat/images/avator.png -------------------------------------------------------------------------------- /pages/chat/images/say.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chongwenwen/weixin_chat/8928a7f9c37082edf058d359c9d3984b20eb791b/pages/chat/images/say.jpg -------------------------------------------------------------------------------- /pages/index/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | //获取应用实例 3 | const app = getApp() 4 | 5 | Page({ 6 | data: { 7 | userInfo: {}, 8 | hasUserInfo: false, 9 | canIUse: wx.canIUse('button.open-type.getUserInfo') 10 | }, 11 | //事件处理函数 12 | // bindViewTap: function() { 13 | // wx.navigateTo({ 14 | // url: '../chat/chat' 15 | // }) 16 | // }, 17 | toChat: function () { 18 | wx.navigateTo({ 19 | url: '../chat/chat' 20 | }) 21 | }, 22 | onLoad: function () { 23 | console.log(app.globalData.userInfo) 24 | if (app.globalData.userInfo) { 25 | this.setData({ 26 | userInfo: app.globalData.userInfo, 27 | hasUserInfo: true 28 | }) 29 | } else if (this.data.canIUse){ 30 | // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 31 | // 所以此处加入 callback 以防止这种情况 32 | app.userInfoReadyCallback = res => { 33 | this.setData({ 34 | userInfo: res.userInfo, 35 | hasUserInfo: true 36 | }) 37 | } 38 | } else { 39 | // 在没有 open-type=getUserInfo 版本的兼容处理 40 | wx.getUserInfo({ 41 | success: res => { 42 | app.globalData.userInfo = res.userInfo 43 | this.setData({ 44 | userInfo: res.userInfo, 45 | hasUserInfo: true 46 | }) 47 | } 48 | }) 49 | } 50 | }, 51 | getUserInfo: function(e) { 52 | console.log(e) 53 | app.globalData.userInfo = e.detail.userInfo 54 | this.setData({ 55 | userInfo: e.detail.userInfo, 56 | hasUserInfo: true 57 | }) 58 | } 59 | }) 60 | -------------------------------------------------------------------------------- /pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | {{userInfo.nickName}} 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .userinfo { 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .userinfo-avatar { 9 | width: 128rpx; 10 | height: 128rpx; 11 | margin: 20rpx; 12 | border-radius: 50%; 13 | } 14 | 15 | .userinfo-nickname { 16 | color: #aaa; 17 | } 18 | .chat{ 19 | font-size: 35rpx; 20 | margin: 40rpx 0; 21 | } -------------------------------------------------------------------------------- /pages/index/user-unlogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chongwenwen/weixin_chat/8928a7f9c37082edf058d359c9d3984b20eb791b/pages/index/user-unlogin.png -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件。", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": true, 9 | "postcss": true, 10 | "minified": true, 11 | "newFeature": true 12 | }, 13 | "compileType": "miniprogram", 14 | "libVersion": "2.0.4", 15 | "appid": "wx73b9fecfd1047b8f", 16 | "projectname": "%E7%88%B1%E5%BF%83%E8%81%8A%E5%A4%A9%E5%AE%A4", 17 | "condition": { 18 | "search": { 19 | "current": -1, 20 | "list": [] 21 | }, 22 | "conversation": { 23 | "current": -1, 24 | "list": [] 25 | }, 26 | "game": { 27 | "currentL": -1, 28 | "list": [] 29 | }, 30 | "miniprogram": { 31 | "current": -1, 32 | "list": [] 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /utils/util.js: -------------------------------------------------------------------------------- 1 | const formatTime = date => { 2 | // const year = date.getFullYear() 3 | // const month = date.getMonth() + 1 4 | // const day = date.getDate() 5 | const hour = date.getHours() 6 | const minute = date.getMinutes() 7 | const second = date.getSeconds() 8 | 9 | // return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':') 10 | return [hour, minute].map(formatNumber).join(':') 11 | } 12 | 13 | const formatNumber = n => { 14 | n = n.toString() 15 | return n[1] ? n : '0' + n 16 | } 17 | //数组去重 18 | function contains(arr, obj) { 19 | var i = arr.length; 20 | while (i--) { 21 | if (arr[i] === obj) { 22 | return true; 23 | } 24 | } 25 | return false; 26 | } 27 | 28 | module.exports = { 29 | formatTime: formatTime, 30 | contains: contains 31 | } 32 | 33 | -------------------------------------------------------------------------------- /utils/websocket.js: -------------------------------------------------------------------------------- 1 | // 聊天室 2 | var url = 'ws://192.168.137.91:2346'; 3 | // var utils = require('./util.js'); 4 | 5 | function connect(user,func) { 6 | wx.connectSocket({ 7 | url: url, 8 | header:{'content-type': 'application/json'}, 9 | success: function () { 10 | console.log('信道连接成功~') 11 | }, 12 | fail: function () { 13 | console.log('信道连接失败~') 14 | } 15 | }) 16 | wx.onSocketOpen(function (res) { 17 | wx.showToast({ 18 | title: '信道已开通~', 19 | icon: "success", 20 | duration: 2000 21 | }) 22 | //接受服务器消息 23 | wx.onSocketMessage(func);//func回调可以拿到服务器返回的数据 24 | }); 25 | wx.onSocketError(function (res) { 26 | wx.showToast({ 27 | title: '信道连接失败,请检查!', 28 | icon: "none", 29 | duration: 2000 30 | }) 31 | }) 32 | } 33 | //发送消息 34 | function send(msg) { 35 | wx.sendSocketMessage({ 36 | data: msg 37 | }); 38 | } 39 | module.exports = { 40 | connect: connect, 41 | send: send 42 | } -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/index.js: -------------------------------------------------------------------------------- 1 | var constants = require('./lib/constants'); 2 | var login = require('./lib/login'); 3 | var Session = require('./lib/session'); 4 | var request = require('./lib/request'); 5 | var Tunnel = require('./lib/tunnel'); 6 | 7 | var exports = module.exports = { 8 | login: login.login, 9 | setLoginUrl: login.setLoginUrl, 10 | LoginError: login.LoginError, 11 | 12 | clearSession: Session.clear, 13 | 14 | request: request.request, 15 | RequestError: request.RequestError, 16 | 17 | Tunnel: Tunnel, 18 | }; 19 | 20 | // 导出错误类型码 21 | Object.keys(constants).forEach(function (key) { 22 | if (key.indexOf('ERR_') === 0) { 23 | exports[key] = constants[key]; 24 | } 25 | }); -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | WX_HEADER_CODE: 'X-WX-Code', 3 | WX_HEADER_ENCRYPTED_DATA: 'X-WX-Encrypted-Data', 4 | WX_HEADER_IV: 'X-WX-IV', 5 | WX_HEADER_ID: 'X-WX-Id', 6 | WX_HEADER_SKEY: 'X-WX-Skey', 7 | 8 | WX_SESSION_MAGIC_ID: 'F2C224D4-2BCE-4C64-AF9F-A6D872000D1A', 9 | 10 | ERR_INVALID_PARAMS: 'ERR_INVALID_PARAMS', 11 | 12 | ERR_WX_LOGIN_FAILED: 'ERR_WX_LOGIN_FAILED', 13 | ERR_WX_GET_USER_INFO: 'ERR_WX_GET_USER_INFO', 14 | ERR_LOGIN_TIMEOUT: 'ERR_LOGIN_TIMEOUT', 15 | ERR_LOGIN_FAILED: 'ERR_LOGIN_FAILED', 16 | ERR_LOGIN_SESSION_NOT_RECEIVED: 'ERR_LOGIN_MISSING_SESSION', 17 | 18 | ERR_SESSION_INVALID: 'ERR_SESSION_INVALID', 19 | ERR_CHECK_LOGIN_FAILED: 'ERR_CHECK_LOGIN_FAILED', 20 | }; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/login.js: -------------------------------------------------------------------------------- 1 | var utils = require('./utils'); 2 | var constants = require('./constants'); 3 | var Session = require('./session'); 4 | 5 | /*** 6 | * @class 7 | * 表示登录过程中发生的异常 8 | */ 9 | var LoginError = (function () { 10 | function LoginError(type, message) { 11 | Error.call(this, message); 12 | this.type = type; 13 | this.message = message; 14 | } 15 | 16 | LoginError.prototype = new Error(); 17 | LoginError.prototype.constructor = LoginError; 18 | 19 | return LoginError; 20 | })(); 21 | 22 | /** 23 | * 微信登录,获取 code 和 encryptData 24 | */ 25 | var getWxLoginResult = function getLoginCode(callback) { 26 | wx.login({ 27 | success: function (loginResult) { 28 | wx.getUserInfo({ 29 | success: function (userResult) { 30 | callback(null, { 31 | code: loginResult.code, 32 | encryptedData: userResult.encryptedData, 33 | iv: userResult.iv, 34 | userInfo: userResult.userInfo, 35 | }); 36 | }, 37 | 38 | fail: function (userError) { 39 | var error = new LoginError(constants.ERR_WX_GET_USER_INFO, '获取微信用户信息失败,请检查网络状态'); 40 | error.detail = userError; 41 | callback(error, null); 42 | }, 43 | }); 44 | }, 45 | 46 | fail: function (loginError) { 47 | var error = new LoginError(constants.ERR_WX_LOGIN_FAILED, '微信登录失败,请检查网络状态'); 48 | error.detail = loginError; 49 | callback(error, null); 50 | }, 51 | }); 52 | }; 53 | 54 | var noop = function noop() {}; 55 | var defaultOptions = { 56 | method: 'GET', 57 | success: noop, 58 | fail: noop, 59 | loginUrl: null, 60 | }; 61 | 62 | /** 63 | * @method 64 | * 进行服务器登录,以获得登录会话 65 | * 66 | * @param {Object} options 登录配置 67 | * @param {string} options.loginUrl 登录使用的 URL,服务器应该在这个 URL 上处理登录请求 68 | * @param {string} [options.method] 请求使用的 HTTP 方法,默认为 "GET" 69 | * @param {Function} options.success(userInfo) 登录成功后的回调函数,参数 userInfo 微信用户信息 70 | * @param {Function} options.fail(error) 登录失败后的回调函数,参数 error 错误信息 71 | */ 72 | var login = function login(options) { 73 | options = utils.extend({}, defaultOptions, options); 74 | 75 | if (!defaultOptions.loginUrl) { 76 | options.fail(new LoginError(constants.ERR_INVALID_PARAMS, '登录错误:缺少登录地址,请通过 setLoginUrl() 方法设置登录地址')); 77 | return; 78 | } 79 | 80 | var doLogin = () => getWxLoginResult(function (wxLoginError, wxLoginResult) { 81 | if (wxLoginError) { 82 | options.fail(wxLoginError); 83 | return; 84 | } 85 | 86 | var userInfo = wxLoginResult.userInfo; 87 | 88 | // 构造请求头,包含 code、encryptedData 和 iv 89 | var code = wxLoginResult.code; 90 | var encryptedData = wxLoginResult.encryptedData; 91 | var iv = wxLoginResult.iv; 92 | var header = {}; 93 | 94 | header[constants.WX_HEADER_CODE] = code; 95 | header[constants.WX_HEADER_ENCRYPTED_DATA] = encryptedData; 96 | header[constants.WX_HEADER_IV] = iv; 97 | 98 | // 请求服务器登录地址,获得会话信息 99 | wx.request({ 100 | url: options.loginUrl, 101 | header: header, 102 | method: options.method, 103 | data: options.data, 104 | success: function (result) { 105 | var data = result.data; 106 | 107 | // 成功地响应会话信息 108 | if (data && data.code === 0 && data.data.skey) { 109 | var res = data.data 110 | if (res.userinfo) { 111 | Session.set(res.skey); 112 | options.success(userInfo); 113 | } else { 114 | var errorMessage = '登录失败(' + data.error + '):' + (data.message || '未知错误'); 115 | var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, errorMessage); 116 | options.fail(noSessionError); 117 | } 118 | 119 | // 没有正确响应会话信息 120 | } else { 121 | var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, JSON.stringify(data)); 122 | options.fail(noSessionError); 123 | } 124 | }, 125 | 126 | // 响应错误 127 | fail: function (loginResponseError) { 128 | var error = new LoginError(constants.ERR_LOGIN_FAILED, '登录失败,可能是网络错误或者服务器发生异常'); 129 | options.fail(error); 130 | }, 131 | }); 132 | }); 133 | 134 | var session = Session.get(); 135 | if (session) { 136 | wx.checkSession({ 137 | success: function () { 138 | options.success(session.userInfo); 139 | }, 140 | 141 | fail: function () { 142 | Session.clear(); 143 | doLogin(); 144 | }, 145 | }); 146 | } else { 147 | doLogin(); 148 | } 149 | }; 150 | 151 | var setLoginUrl = function (loginUrl) { 152 | defaultOptions.loginUrl = loginUrl; 153 | }; 154 | 155 | module.exports = { 156 | LoginError: LoginError, 157 | login: login, 158 | setLoginUrl: setLoginUrl, 159 | }; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/request.js: -------------------------------------------------------------------------------- 1 | var constants = require('./constants'); 2 | var utils = require('./utils'); 3 | var Session = require('./session'); 4 | var loginLib = require('./login'); 5 | 6 | var noop = function noop() {}; 7 | 8 | var buildAuthHeader = function buildAuthHeader(session) { 9 | var header = {}; 10 | 11 | if (session) { 12 | header[constants.WX_HEADER_SKEY] = session; 13 | } 14 | 15 | return header; 16 | }; 17 | 18 | /*** 19 | * @class 20 | * 表示请求过程中发生的异常 21 | */ 22 | var RequestError = (function () { 23 | function RequestError(type, message) { 24 | Error.call(this, message); 25 | this.type = type; 26 | this.message = message; 27 | } 28 | 29 | RequestError.prototype = new Error(); 30 | RequestError.prototype.constructor = RequestError; 31 | 32 | return RequestError; 33 | })(); 34 | 35 | function request(options) { 36 | if (typeof options !== 'object') { 37 | var message = '请求传参应为 object 类型,但实际传了 ' + (typeof options) + ' 类型'; 38 | throw new RequestError(constants.ERR_INVALID_PARAMS, message); 39 | } 40 | 41 | var requireLogin = options.login; 42 | var success = options.success || noop; 43 | var fail = options.fail || noop; 44 | var complete = options.complete || noop; 45 | var originHeader = options.header || {}; 46 | 47 | // 成功回调 48 | var callSuccess = function () { 49 | success.apply(null, arguments); 50 | complete.apply(null, arguments); 51 | }; 52 | 53 | // 失败回调 54 | var callFail = function (error) { 55 | fail.call(null, error); 56 | complete.call(null, error); 57 | }; 58 | 59 | // 是否已经进行过重试 60 | var hasRetried = false; 61 | 62 | if (requireLogin) { 63 | doRequestWithLogin(); 64 | } else { 65 | doRequest(); 66 | } 67 | 68 | // 登录后再请求 69 | function doRequestWithLogin() { 70 | loginLib.login({ success: doRequest, fail: callFail }); 71 | } 72 | 73 | // 实际进行请求的方法 74 | function doRequest() { 75 | var authHeader = buildAuthHeader(Session.get()); 76 | 77 | wx.request(utils.extend({}, options, { 78 | header: utils.extend({}, originHeader, authHeader), 79 | 80 | success: function (response) { 81 | var data = response.data; 82 | 83 | var error, message; 84 | if (data && data.code === -1) { 85 | Session.clear(); 86 | // 如果是登录态无效,并且还没重试过,会尝试登录后刷新凭据重新请求 87 | if (!hasRetried) { 88 | hasRetried = true; 89 | doRequestWithLogin(); 90 | return; 91 | } 92 | 93 | message = '登录态已过期'; 94 | error = new RequestError(data.error, message); 95 | 96 | callFail(error); 97 | return; 98 | } else { 99 | callSuccess.apply(null, arguments); 100 | } 101 | }, 102 | 103 | fail: callFail, 104 | complete: noop, 105 | })); 106 | }; 107 | 108 | }; 109 | 110 | module.exports = { 111 | RequestError: RequestError, 112 | request: request, 113 | }; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/session.js: -------------------------------------------------------------------------------- 1 | var constants = require('./constants'); 2 | var SESSION_KEY = 'weapp_session_' + constants.WX_SESSION_MAGIC_ID; 3 | 4 | var Session = { 5 | get: function () { 6 | return wx.getStorageSync(SESSION_KEY) || null; 7 | }, 8 | 9 | set: function (session) { 10 | wx.setStorageSync(SESSION_KEY, session); 11 | }, 12 | 13 | clear: function () { 14 | wx.removeStorageSync(SESSION_KEY); 15 | }, 16 | }; 17 | 18 | module.exports = Session; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/tunnel.js: -------------------------------------------------------------------------------- 1 | var requestLib = require('./request'); 2 | var wxTunnel = require('./wxTunnel'); 3 | 4 | /** 5 | * 当前打开的信道,同一时间只能有一个信道打开 6 | */ 7 | var currentTunnel = null; 8 | 9 | // 信道状态枚举 10 | var STATUS_CLOSED = Tunnel.STATUS_CLOSED = 'CLOSED'; 11 | var STATUS_CONNECTING = Tunnel.STATUS_CONNECTING = 'CONNECTING'; 12 | var STATUS_ACTIVE = Tunnel.STATUS_ACTIVE = 'ACTIVE'; 13 | var STATUS_RECONNECTING = Tunnel.STATUS_RECONNECTING = 'RECONNECTING'; 14 | 15 | // 错误类型枚举 16 | var ERR_CONNECT_SERVICE = Tunnel.ERR_CONNECT_SERVICE = 1001; 17 | var ERR_CONNECT_SOCKET = Tunnel.ERR_CONNECT_SOCKET = 1002; 18 | var ERR_RECONNECT = Tunnel.ERR_RECONNECT = 2001; 19 | var ERR_SOCKET_ERROR = Tunnel.ERR_SOCKET_ERROR = 3001; 20 | 21 | // 包类型枚举 22 | var PACKET_TYPE_MESSAGE = 'message'; 23 | var PACKET_TYPE_PING = 'ping'; 24 | var PACKET_TYPE_PONG = 'pong'; 25 | var PACKET_TYPE_TIMEOUT = 'timeout'; 26 | var PACKET_TYPE_CLOSE = 'close'; 27 | 28 | // 断线重连最多尝试 5 次 29 | var DEFAULT_MAX_RECONNECT_TRY_TIMES = 5; 30 | 31 | // 每次重连前,等待时间的增量值 32 | var DEFAULT_RECONNECT_TIME_INCREASE = 1000; 33 | 34 | function Tunnel(serviceUrl) { 35 | if (currentTunnel && currentTunnel.status !== STATUS_CLOSED) { 36 | throw new Error('当前有未关闭的信道,请先关闭之前的信道,再打开新信道'); 37 | } 38 | 39 | currentTunnel = this; 40 | 41 | // 等确认微信小程序全面支持 ES6 就不用那么麻烦了 42 | var me = this; 43 | 44 | //========================================================================= 45 | // 暴露实例状态以及方法 46 | //========================================================================= 47 | this.serviceUrl = serviceUrl; 48 | this.socketUrl = null; 49 | this.status = null; 50 | 51 | this.open = openConnect; 52 | this.on = registerEventHandler; 53 | this.emit = emitMessagePacket; 54 | this.close = close; 55 | 56 | this.isClosed = isClosed; 57 | this.isConnecting = isConnecting; 58 | this.isActive = isActive; 59 | this.isReconnecting = isReconnecting; 60 | 61 | 62 | //========================================================================= 63 | // 信道状态处理,状态说明: 64 | // closed - 已关闭 65 | // connecting - 首次连接 66 | // active - 当前信道已经在工作 67 | // reconnecting - 断线重连中 68 | //========================================================================= 69 | function isClosed() { return me.status === STATUS_CLOSED; } 70 | function isConnecting() { return me.status === STATUS_CONNECTING; } 71 | function isActive() { return me.status === STATUS_ACTIVE; } 72 | function isReconnecting() { return me.status === STATUS_RECONNECTING; } 73 | 74 | function setStatus(status) { 75 | var lastStatus = me.status; 76 | if (lastStatus !== status) { 77 | me.status = status; 78 | } 79 | } 80 | 81 | // 初始为关闭状态 82 | setStatus(STATUS_CLOSED); 83 | 84 | 85 | //========================================================================= 86 | // 信道事件处理机制 87 | // 信道事件包括: 88 | // connect - 连接已建立 89 | // close - 连接被关闭(包括主动关闭和被动关闭) 90 | // reconnecting - 开始重连 91 | // reconnect - 重连成功 92 | // error - 发生错误,其中包括连接失败、重连失败、解包失败等等 93 | // [message] - 信道服务器发送过来的其它事件类型,如果事件类型和上面内置的事件类型冲突,将在事件类型前面添加前缀 `@` 94 | //========================================================================= 95 | var preservedEventTypes = 'connect,close,reconnecting,reconnect,error'.split(','); 96 | var eventHandlers = []; 97 | 98 | /** 99 | * 注册消息处理函数 100 | * @param {string} messageType 支持内置消息类型("connect"|"close"|"reconnecting"|"reconnect"|"error")以及业务消息类型 101 | */ 102 | function registerEventHandler(eventType, eventHandler) { 103 | if (typeof eventHandler === 'function') { 104 | eventHandlers.push([eventType, eventHandler]); 105 | } 106 | } 107 | 108 | /** 109 | * 派发事件,通知所有处理函数进行处理 110 | */ 111 | function dispatchEvent(eventType, eventPayload) { 112 | eventHandlers.forEach(function (handler) { 113 | var handleType = handler[0]; 114 | var handleFn = handler[1]; 115 | 116 | if (handleType === '*') { 117 | handleFn(eventType, eventPayload); 118 | } else if (handleType === eventType) { 119 | handleFn(eventPayload); 120 | } 121 | }); 122 | } 123 | 124 | /** 125 | * 派发事件,事件类型和系统保留冲突的,事件名会自动加上 '@' 前缀 126 | */ 127 | function dispatchEscapedEvent(eventType, eventPayload) { 128 | if (preservedEventTypes.indexOf(eventType) > -1) { 129 | eventType = '@' + eventType; 130 | } 131 | 132 | dispatchEvent(eventType, eventPayload); 133 | } 134 | 135 | 136 | //========================================================================= 137 | // 信道连接控制 138 | //========================================================================= 139 | var isFirstConnection = true; 140 | var isOpening = false; 141 | 142 | /** 143 | * 连接信道服务器,获取 WebSocket 连接地址,获取地址成功后,开始进行 WebSocket 连接 144 | */ 145 | function openConnect() { 146 | if (isOpening) return; 147 | isOpening = true; 148 | 149 | // 只有关闭状态才会重新进入准备中 150 | setStatus(isFirstConnection ? STATUS_CONNECTING : STATUS_RECONNECTING); 151 | 152 | requestLib.request({ 153 | url: serviceUrl, 154 | method: 'GET', 155 | success: function (response) { 156 | if (+response.statusCode === 200 && response.data && response.data.data.connectUrl) { 157 | openSocket(me.socketUrl = response.data.data.connectUrl); 158 | } else { 159 | dispatchConnectServiceError(response); 160 | } 161 | }, 162 | fail: dispatchConnectServiceError, 163 | complete: () => isOpening = false, 164 | }); 165 | 166 | function dispatchConnectServiceError(detail) { 167 | if (isFirstConnection) { 168 | setStatus(STATUS_CLOSED); 169 | 170 | dispatchEvent('error', { 171 | code: ERR_CONNECT_SERVICE, 172 | message: '连接信道服务失败,网络错误或者信道服务没有正确响应', 173 | detail: detail || null, 174 | }); 175 | 176 | } else { 177 | startReconnect(detail); 178 | } 179 | } 180 | } 181 | 182 | /** 183 | * 打开 WebSocket 连接,打开后,注册微信的 Socket 处理方法 184 | */ 185 | function openSocket(url) { 186 | wxTunnel.listen({ 187 | onOpen: handleSocketOpen, 188 | onMessage: handleSocketMessage, 189 | onClose: handleSocketClose, 190 | onError: handleSocketError, 191 | }); 192 | 193 | wx.connectSocket({ url: url }); 194 | isFirstConnection = false; 195 | } 196 | 197 | 198 | //========================================================================= 199 | // 处理消息通讯 200 | // 201 | // packet - 数据包,序列化形式为 `${type}` 或者 `${type}:${content}` 202 | // packet.type - 包类型,包括 message, ping, pong, close 203 | // packet.content? - 当包类型为 message 的时候,会附带 message 数据 204 | // 205 | // message - 消息体,会使用 JSON 序列化后作为 packet.content 206 | // message.type - 消息类型,表示业务消息类型 207 | // message.content? - 消息实体,可以为任意类型,表示消息的附带数据,也可以为空 208 | // 209 | // 数据包示例: 210 | // - 'ping' 表示 Ping 数据包 211 | // - 'message:{"type":"speak","content":"hello"}' 表示一个打招呼的数据包 212 | //========================================================================= 213 | 214 | // 连接还没成功建立的时候,需要发送的包会先存放到队列里 215 | var queuedPackets = []; 216 | 217 | /** 218 | * WebSocket 打开之后,更新状态,同时发送所有遗留的数据包 219 | */ 220 | function handleSocketOpen() { 221 | /* istanbul ignore else */ 222 | if (isConnecting()) { 223 | dispatchEvent('connect'); 224 | 225 | } 226 | else if (isReconnecting()) { 227 | dispatchEvent('reconnect'); 228 | resetReconnectionContext(); 229 | } 230 | 231 | setStatus(STATUS_ACTIVE); 232 | emitQueuedPackets(); 233 | nextPing(); 234 | } 235 | 236 | /** 237 | * 收到 WebSocket 数据包,交给处理函数 238 | */ 239 | function handleSocketMessage(message) { 240 | resolvePacket(message.data); 241 | } 242 | 243 | /** 244 | * 发送数据包,如果信道没有激活,将先存放队列 245 | */ 246 | function emitPacket(packet) { 247 | if (isActive()) { 248 | sendPacket(packet); 249 | } else { 250 | queuedPackets.push(packet); 251 | } 252 | } 253 | 254 | /** 255 | * 数据包推送到信道 256 | */ 257 | function sendPacket(packet) { 258 | var encodedPacket = [packet.type]; 259 | 260 | if (packet.content) { 261 | encodedPacket.push(JSON.stringify(packet.content)); 262 | } 263 | 264 | wx.sendSocketMessage({ 265 | data: encodedPacket.join(':'), 266 | fail: handleSocketError, 267 | }); 268 | } 269 | 270 | function emitQueuedPackets() { 271 | queuedPackets.forEach(emitPacket); 272 | 273 | // empty queued packets 274 | queuedPackets.length = 0; 275 | } 276 | 277 | /** 278 | * 发送消息包 279 | */ 280 | function emitMessagePacket(messageType, messageContent) { 281 | var packet = { 282 | type: PACKET_TYPE_MESSAGE, 283 | content: { 284 | type: messageType, 285 | content: messageContent, 286 | }, 287 | }; 288 | 289 | emitPacket(packet); 290 | } 291 | 292 | /** 293 | * 发送 Ping 包 294 | */ 295 | function emitPingPacket() { 296 | emitPacket({ type: PACKET_TYPE_PING }); 297 | } 298 | 299 | /** 300 | * 发送关闭包 301 | */ 302 | function emitClosePacket() { 303 | emitPacket({ type: PACKET_TYPE_CLOSE }); 304 | } 305 | 306 | /** 307 | * 解析并处理从信道接收到的包 308 | */ 309 | function resolvePacket(raw) { 310 | var packetParts = raw.split(':'); 311 | var packetType = packetParts.shift(); 312 | var packetContent = packetParts.join(':') || null; 313 | var packet = { type: packetType }; 314 | 315 | if (packetContent) { 316 | try { 317 | packet.content = JSON.parse(packetContent); 318 | } catch (e) {} 319 | } 320 | 321 | switch (packet.type) { 322 | case PACKET_TYPE_MESSAGE: 323 | handleMessagePacket(packet); 324 | break; 325 | case PACKET_TYPE_PONG: 326 | handlePongPacket(packet); 327 | break; 328 | case PACKET_TYPE_TIMEOUT: 329 | handleTimeoutPacket(packet); 330 | break; 331 | case PACKET_TYPE_CLOSE: 332 | handleClosePacket(packet); 333 | break; 334 | default: 335 | handleUnknownPacket(packet); 336 | break; 337 | } 338 | } 339 | 340 | /** 341 | * 收到消息包,直接 dispatch 给处理函数 342 | */ 343 | function handleMessagePacket(packet) { 344 | var message = packet.content; 345 | dispatchEscapedEvent(message.type, message.content); 346 | } 347 | 348 | 349 | //========================================================================= 350 | // 心跳、断开与重连处理 351 | //========================================================================= 352 | 353 | /** 354 | * Ping-Pong 心跳检测超时控制,这个值有两个作用: 355 | * 1. 表示收到服务器的 Pong 相应之后,过多久再发下一次 Ping 356 | * 2. 如果 Ping 发送之后,超过这个时间还没收到 Pong,断开与服务器的连接 357 | * 该值将在与信道服务器建立连接后被更新 358 | */ 359 | let pingPongTimeout = 15000; 360 | let pingTimer = 0; 361 | let pongTimer = 0; 362 | 363 | /** 364 | * 信道服务器返回 Ping-Pong 控制超时时间 365 | */ 366 | function handleTimeoutPacket(packet) { 367 | var timeout = packet.content * 1000; 368 | /* istanbul ignore else */ 369 | if (!isNaN(timeout)) { 370 | pingPongTimeout = timeout; 371 | ping(); 372 | } 373 | } 374 | 375 | /** 376 | * 收到服务器 Pong 响应,定时发送下一个 Ping 377 | */ 378 | function handlePongPacket(packet) { 379 | nextPing(); 380 | } 381 | 382 | /** 383 | * 发送下一个 Ping 包 384 | */ 385 | function nextPing() { 386 | clearTimeout(pingTimer); 387 | clearTimeout(pongTimer); 388 | pingTimer = setTimeout(ping, pingPongTimeout); 389 | } 390 | 391 | /** 392 | * 发送 Ping,等待 Pong 393 | */ 394 | function ping() { 395 | /* istanbul ignore else */ 396 | if (isActive()) { 397 | emitPingPacket(); 398 | 399 | // 超时没有响应,关闭信道 400 | pongTimer = setTimeout(handlePongTimeout, pingPongTimeout); 401 | } 402 | } 403 | 404 | /** 405 | * Pong 超时没有响应,信道可能已经不可用,需要断开重连 406 | */ 407 | function handlePongTimeout() { 408 | startReconnect('服务器已失去响应'); 409 | } 410 | 411 | // 已经重连失败的次数 412 | var reconnectTryTimes = 0; 413 | 414 | // 最多允许失败次数 415 | var maxReconnectTryTimes = Tunnel.MAX_RECONNECT_TRY_TIMES || DEFAULT_MAX_RECONNECT_TRY_TIMES; 416 | 417 | // 重连前等待的时间 418 | var waitBeforeReconnect = 0; 419 | 420 | // 重连前等待时间增量 421 | var reconnectTimeIncrease = Tunnel.RECONNECT_TIME_INCREASE || DEFAULT_RECONNECT_TIME_INCREASE; 422 | 423 | var reconnectTimer = 0; 424 | 425 | function startReconnect(lastError) { 426 | if (reconnectTryTimes >= maxReconnectTryTimes) { 427 | close(); 428 | 429 | dispatchEvent('error', { 430 | code: ERR_RECONNECT, 431 | message: '重连失败', 432 | detail: lastError, 433 | }); 434 | } 435 | else { 436 | wx.closeSocket(); 437 | waitBeforeReconnect += reconnectTimeIncrease; 438 | setStatus(STATUS_RECONNECTING); 439 | reconnectTimer = setTimeout(doReconnect, waitBeforeReconnect); 440 | } 441 | 442 | if (reconnectTryTimes === 0) { 443 | dispatchEvent('reconnecting'); 444 | } 445 | 446 | reconnectTryTimes += 1; 447 | } 448 | 449 | function doReconnect() { 450 | openConnect(); 451 | } 452 | 453 | function resetReconnectionContext() { 454 | reconnectTryTimes = 0; 455 | waitBeforeReconnect = 0; 456 | } 457 | 458 | /** 459 | * 收到服务器的关闭请求 460 | */ 461 | function handleClosePacket(packet) { 462 | close(); 463 | } 464 | 465 | function handleUnknownPacket(packet) { 466 | // throw away 467 | } 468 | 469 | var isClosing = false; 470 | 471 | /** 472 | * 收到 WebSocket 断开的消息,处理断开逻辑 473 | */ 474 | function handleSocketClose() { 475 | /* istanbul ignore if */ 476 | if (isClosing) return; 477 | 478 | /* istanbul ignore else */ 479 | if (isActive()) { 480 | // 意外断开的情况,进行重连 481 | startReconnect('链接已断开'); 482 | } 483 | } 484 | 485 | function close() { 486 | isClosing = true; 487 | closeSocket(); 488 | setStatus(STATUS_CLOSED); 489 | resetReconnectionContext(); 490 | isFirstConnection = false; 491 | clearTimeout(pingTimer); 492 | clearTimeout(pongTimer); 493 | clearTimeout(reconnectTimer); 494 | dispatchEvent('close'); 495 | isClosing = false; 496 | } 497 | 498 | function closeSocket(emitClose) { 499 | if (isActive() && emitClose !== false) { 500 | emitClosePacket(); 501 | } 502 | 503 | wx.closeSocket(); 504 | } 505 | 506 | 507 | //========================================================================= 508 | // 错误处理 509 | //========================================================================= 510 | 511 | /** 512 | * 错误处理 513 | */ 514 | function handleSocketError(detail) { 515 | switch (me.status) { 516 | case Tunnel.STATUS_CONNECTING: 517 | dispatchEvent('error', { 518 | code: ERR_SOCKET_ERROR, 519 | message: '连接信道失败,网络错误或者信道服务不可用', 520 | detail: detail, 521 | }); 522 | break; 523 | } 524 | } 525 | 526 | } 527 | 528 | module.exports = Tunnel; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 拓展对象 4 | */ 5 | exports.extend = function extend(target) { 6 | var sources = Array.prototype.slice.call(arguments, 1); 7 | 8 | for (var i = 0; i < sources.length; i += 1) { 9 | var source = sources[i]; 10 | for (var key in source) { 11 | if (source.hasOwnProperty(key)) { 12 | target[key] = source[key]; 13 | } 14 | } 15 | } 16 | 17 | return target; 18 | }; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/lib/wxTunnel.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | const noop = () => void(0); 3 | 4 | let onOpen, onClose, onMessage, onError; 5 | 6 | /* istanbul ignore next */ 7 | function listen(listener) { 8 | if (listener) { 9 | onOpen = listener.onOpen; 10 | onClose = listener.onClose; 11 | onMessage = listener.onMessage; 12 | onError = listener.onError; 13 | } else { 14 | onOpen = noop; 15 | onClose = noop; 16 | onMessage = noop; 17 | onError = noop; 18 | } 19 | } 20 | 21 | /* istanbul ignore next */ 22 | function bind() { 23 | wx.onSocketOpen(result => onOpen(result)); 24 | wx.onSocketClose(result => onClose(result)); 25 | wx.onSocketMessage(result => onMessage(result)); 26 | wx.onSocketError(error => onError(error)); 27 | } 28 | 29 | listen(null); 30 | bind(); 31 | 32 | module.exports = { listen }; -------------------------------------------------------------------------------- /vendor/wafer2-client-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_from": "wafer2-client-sdk", 3 | "_id": "wafer2-client-sdk@1.0.0", 4 | "_inBundle": false, 5 | "_integrity": "sha1-4hExQwJ+2YIN3LOn0EtbBd8uTYg=", 6 | "_location": "/wafer2-client-sdk", 7 | "_phantomChildren": {}, 8 | "_requested": { 9 | "type": "tag", 10 | "registry": true, 11 | "raw": "wafer2-client-sdk", 12 | "name": "wafer2-client-sdk", 13 | "escapedName": "wafer2-client-sdk", 14 | "rawSpec": "", 15 | "saveSpec": null, 16 | "fetchSpec": "latest" 17 | }, 18 | "_requiredBy": [ 19 | "#USER", 20 | "/" 21 | ], 22 | "_resolved": "http://r.tnpm.oa.com/wafer2-client-sdk/download/wafer2-client-sdk-1.0.0.tgz", 23 | "_shasum": "e2113143027ed9820ddcb3a7d04b5b05df2e4d88", 24 | "_spec": "wafer2-client-sdk", 25 | "_where": "/Users/Jason/Tencent/ide-test/wafer-client-demo", 26 | "author": { 27 | "name": "CFETeam" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/tencentyun/wafer2-client-sdk/issues" 31 | }, 32 | "bundleDependencies": false, 33 | "deprecated": false, 34 | "description": "Wafer client SDK", 35 | "directories": { 36 | "lib": "lib" 37 | }, 38 | "homepage": "https://github.com/tencentyun/wafer2-client-sdk#readme", 39 | "license": "MIT", 40 | "main": "index.js", 41 | "name": "wafer2-client-sdk", 42 | "repository": { 43 | "type": "git", 44 | "url": "git+https://github.com/tencentyun/wafer2-client-sdk.git" 45 | }, 46 | "version": "1.0.0" 47 | } 48 | --------------------------------------------------------------------------------