app.vue页面
里面的message.type 是和后台商量的类型。
1. websocket是进小程序就要启动的,在onLaunch里面但是肯定是在用户登录之后才启动,不然要报错。
2. 在onShow里面监听了一个emit,在用户登录之后,要启动websocket。
<script>
export default {
data() {
return {
wsUrl: "wss://school.qifuxiong.com/wss/",
lockReconnect: false, // 避免重复连接
formId: null,
toId: null,
heartCheck : {
timeout: 30000,
timeoutObj: null,
serverTimeoutObj: null,
reset: () => {
clearTimeout(this.heartCheck.timeoutObj);
clearTimeout(this.heartCheck.serverTimeoutObj);
return this.heartCheck;
},
start: () => {
this.heartCheck.timeoutObj = setTimeout(() => {
//这里发送一个心跳,后端收到后,返回一个心跳消息,
//onmessage拿到返回的心跳就说明连接正常
let heart = {
type: "heart",
fromId: this.formId
}
this.$store.state.wss.send({
data: JSON.stringify(heart),
success: () => {
// console.log("发送一次心跳包", heart);
}
})
this.heartCheck.serverTimeoutObj = setTimeout(() => {
this.$store.state.wss.onOpen(_ => {
this.$store.state.wss.close()
})
}, 70000) // 这里因为后台是60秒没反应就关闭,所以前端设置的是70秒关闭
}, this.heartCheck.timeout)
}
}
}
},
onLaunch: function() {
console.log('App Launch')
if(uni.getStorageSync('userInfo')) {
this.createSocket(this.wsUrl)
this.getMessageNum()
}
},
onShow: function() {
this.formId = uni.getStorageSync('userInfo').id
uni.$on('openSocket', (data) => {
this.formId = data.user_id
this.createSocket(this.wsUrl)
this.getMessageNum()
})
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
this.$store.state.wss.onOpen(_ => {
this.$store.state.wss.close()
})
},
onUnhandledRejection(event) {
console.error(event)
event.preventDefault();
},
methods: {
// 连接socket
createSocket(path) {
this.$store.state.wss = uni.connectSocket({
url: path,
success: (res) => {
// console.log(res)
},
fail: (e) => {
// 重连
this.reconnect();
},
})
// 这里需要nextTick一下,不然this.$store.state.wss获取的就是空
this.$nextTick(() => {
// 核心代码
this.initEventHandle();
})
},// 初始化开始
initEventHandle() {
// console.log(this.$store.state.wss, this.formId)
this.$store.state.wss.onOpen((data) => {
console.log("成功连接到: socket");
this.heartCheck.reset().start();
});
this.$store.state.wss.onMessage((response) => {
//如果获取到消息,心跳检测重置
//拿到任何消息都说明当前连接是正常的
this.heartCheck.reset().start();
// json转化为obj
let message = JSON.parse(response.data)
console.log('服务端返回的数据', message)
switch(message.type) {
case "init":
// 初始化发送当前人id,不然无法获取后台心跳
let build = {
type: 'bind',
fromId: this.formId
}
this.$store.state.wss.send({
data: JSON.stringify(build)
})
break;
case 'letters':
// 这里是获取聊天数目
this.getMessageNum();
break;
case "heart":
// console.log('心跳不渲染任何信息')
break;
}
this.$store.commit('setSocket', message) // 这句本来想用,后来没用,主要就是存在vuex里面
uni.$emit('newSocketMsg', message) // 主要是这句,在聊天页面就会用到
});
this.$store.state.wss.onClose((close) => {
console.log('连接关闭, 正在重连')
this.reconnect()
});
this.$store.state.wss.onError((err) => {
console.log('连接错误, 正在重连')
this.reconnect()
});
},
// 重连
reconnect() {
if(this.lockReconnect) return
this.lockReconnect = true
//没连接上会一直重连,设置延迟避免请求过多
setTimeout(() => {
this.createSocket(this.wsUrl)
this.lockReconnect = false
}, 5000)
},
// 获取数目
getMessageNum() {
this.$u.api.Meet.messageNum().then(res => {
if(res && res.countNum !== 0) {
uni.setTabBarBadge({
index: 2,
text: ''
})
}
}).catch(e => {
console.error(e)
})
}
}
}
</script>
chat.vue? 聊天页面
这里只展示跟websocket相关的代码,主要是接收消息和发送消息,注意我这里获取历史消息是一进页面就开始访问了接口;发送消息也是先调接口,毕竟数据要存在数据库,下次获取历史消息才有,后才通过websocket发送消息展示在页面上。
onLoad() {
// 这里监听app.vue存的emit,初始化方法
uni.$on('newSocketMsg', (data) => {
this.message = data
this.initMethod()
})
},
onShow() {
// 拿聊天记录
this.getHistoryMsg();
// 点对点聊天判断对方是否在线
let online = {
type: 'online',
to_id: this.toUser.id,
from_id: this.userid
}
this.$store.state.wss.send({
data: JSON.stringify(online)
});
},
methods: {
initMethod() {
let message = this.message
switch(message.type) {
case 'init':
console.log('init')
break;
case 'text':
console.log(message)
let text = {
id: message.id,
from_id: message.from_id,
to_id: message.to_id,
content: message.data,
type: 0,
text: message.type, // 表示文本信息(后台)
is_read: message.is_read,
pic: this.baseImgUrl + this.toUser.pic
}
this.talkList.push(text)
this.$nextTick(()=>{
uni.pageScrollTo({
scrollTop: 999999, // 设置一个超大值,以保证滚动条滚动到底部
duration: 0
});
})
break;
case 'online':
// 用户在线不在线, 涉及到一些业务场景需要处理
// 如果对方上线了, 后端也会主动推送到这个里面来
console.log('online:', message)
break;
}
},
// 发送信息
send(){
if(!this.content){
uni.showToast({
title:'请输入有效的内容',
icon:'none'
})
return;
}
uni.showLoading({
title:'正在发送'
})
let head_image = uni.getStorageSync('userInfo').avatar_url
let timeStame = new Date().getTime()
// 将当前发送信息 添加到消息列表。
let data = {
id: timeStame,
content: this.content,
type: 1, // 1: 发送的
pic: head_image.includes('http://') ? head_image : this.baseImgUrl + head_image
}
// 发送 调接口
this.$u.api.Meet.chatWithOneFriend({
to_id: this.toUser.id,
message: this.content,
// 类型: 0未读,1已读, 双方处于在线聊天中, 直接就是已读,对方不在线,就是未读
is_read: this.message ? this.message.type == 'online' ? this.message.status.includes('对方不在线') ? 0 : 1 : 0 : 0
}).then(res => {
uni.hideLoading();
// 页面显示
this.talkList.push(data);
this.$nextTick(()=>{
// 清空内容框中的内容
this.content = '';
// 滚动到最新一条数据
uni.pageScrollTo({
scrollTop: 999999, // 设置一个超大值,以保证滚动条滚动到底部
duration: 0,
});
})
}).catch(e => {
uni.hideLoading();
})
this.$store.state.wss.send({
data: JSON.stringify({
data: this.content,
type: 'say',
from_id: this.userid,
to_id: this.toUser.id
})
})
}
}
|