项目需求,记录一下:
1.要求websocket实时返回会话结果
我项目这边是后端一次返回一个字,–finish–结束,所以实现方法是每获取到数据时就setData一次,直到获取到的数据为finish,停止setData
后端返回结果如图
2.会话自动回滚到底部
用wx.createSelectorQuery()获取页面上的节点信息,可获得相对于显示区域的上下左右边界坐标,详情可点开链接查看
注意:每次发出问题,或者接受结果时都要重新计算节点信息
3.会话缓存(我这里用到的是wx.setStorageSync())
话不多说,上代码
wxml
<view >
<!-- isHe是为了首次进入页面时,要不要计算节点信息,如果没有缓存默认高度撑起来;有缓存,计算页面节点信息,获取滚动高度 -->
<view id="idView" style="height:{{isHe?'90vh':'auto'}};padding-bottom: 15vh;overflow: scroll;">
<block wx:key wx:for='{{msgList}}' wx:for-index="index">
<!-- 单个消息1 客服发出(左) -->
<view wx:if='{{item.speaker=="server"}}' class="servers" id='msg-{{index}}' style='display: flex; padding: 2vw 11vw 2vw 2vw;'>
<view style='width: 11vw; height: 11vw;'>
<!-- 机器人图片 -->
<image style='width: 11vw; height: 11vw; border-radius: 10rpx;' src='../../../assets/images/member/robot.png'></image>
</view>
<view style='width: 4vw; height: 11vw; margin-left: 0.5vw; display: flex; align-items: center; z-index: 9;'>
<image style='width: 4vw;' src='../../../assets/images/member/left.png' mode='widthFix'></image>
</view>
<block>
<!-- 发出消息等待返回结果时,三点加载动画 -->
<view wx:if="{{isDisable && item.content==''}}" user-select='{{true}}' class='leftMsg loadView' space="nbsp" decode="{{true}}">
<view class="dot1"> </view>
<view class="dot2"></view>
<view class="dot3"></view>
</view>
<text user-select='{{true}}' class='leftMsg' space="nbsp" decode="{{true}}">{{item.content}}</text>
</block>
</view>
<!-- 单个消息2 用户发出(右) -->
<view wx:else id='msg-{{index}}' style='display: flex; justify-content: flex-end; padding: 2vw 2vw 2vw 11vw;'>
<view class='rightMsg'>{{item.content}}</view>
<view style='width: 4vw; height: 11vw; margin-right: 0.5vw; display: flex; align-items: center; z-index: 9;'>
<!-- 左右箭头,见下图 -->
<image style='width: 4vw;' src='../../../assets/images/member/right.png' mode='widthFix'></image>
</view>
<view style='width: 11vw; height: 11vw;'>
<image style='width: 11vw; height: 11vw; border-radius: 10rpx;' src='{{cusHeadIcon}}'></image>
</view>
</view>
</block>
</view>
<view class='inputRoom'>
<input data-value="{{inputVal}}" disabled='{{isDisable}}' data-type="1" type="text" placeholder="请输入你的问题" confirm-type="send" bindconfirm='sendClick' value='{{inputVal}}' bindinput="ontext"></input>
<image wx:if="{{!isDisable}}" data-type="2" bindtap='sendClick' class="sendIcon" src='../../../assets/images/member/send.png' mode='widthFix'></image>
<image wx:else class="sendIcon" src='../../../assets/images/member/send.png' mode='widthFix'></image>
</view>
</view>
用到的api
wx.connectSocket()创建websocket连接
wx.onSocketOpen()监听websocket连接打开事件
wx.sendSocketMessage()发送数据,需要先 wx.connectSocket,并在 wx.onSocketOpen 回调之后才能发送
wx.onSocketMessage()监听websocket接收到服务器的消息事件
wx.createSelectorQuery()获取节点信息,如节点位置,文本内容等
wx.pageScrollTo()将页面滚动到目标位置,支持选择器和滚动距离两种方式定位
wx.closeSocket()关闭websocket连接
wx.onSocketClose()监听websocket连接关闭事件
js
const commonMixin = require('../../../utils/commonMixin')
const app = getApp();
var inputVal = '';
var msgList = [];
var windowWidth = wx.getSystemInfoSync().windowWidth;
var windowHeight = wx.getSystemInfoSync().windowHeight;
var keyHeight = 0;
let socketOpen = false;
let socketMsgQueue = [];
/**
* 初始化数据
*/
function initData(that) {
inputVal = '';
msgList = [{
speaker: 'server',
contentType: 'text',
content: '你好,我是人工智能助手,请问有什么可以帮你?'
}, ]
that.setData({
msgList,
inputVal
})
}
function sendSocketMessage(msg) {
console.log(socketOpen,'socketOpen',msg);
if (socketOpen) {
wx.sendSocketMessage({
data: msg
})
} else {
socketMsgQueue.push(msg)
}
}
Page(Object.assign({
/**
* 页面的初始数据
*/
data: {
scrollTop: 0,
isHe: false,
text:'',
isDisable:false, // 发送消息等待回话时,input框禁用
inputBottom: 0
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
initData(this);
this.setData({
cusHeadIcon: app.globalData.userInfo.avatar || app.globalData.userInfo.avatarUrl,
});
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
let that = this;
let list =wx.getStorageSync('memberList')
console.log(list,'list');
// 判断是否有缓存,有缓存时,计算页面节点信息,设置滚动距离
if (list?.length>0) { // 有缓存
that.setData({
msgList:JSON.parse(list)
})
wx.createSelectorQuery().select('#idView').boundingClientRect(function (rect) {
// 使页面滚动到底部
wx.pageScrollTo({
scrollTop: rect.height+rect.bottom // 滚动距离
})
that.setData({
scrollTop: rect.height+rect.bottom,
isHe: false
});
}).exec()
}else{ //没有缓存,默认高度是90vh
wx.setStorageSync('memberList', JSON.stringify(this.data.msgList))
that.setData({
isHe: true,
})
}
wx.connectSocket({
url: 'wss://' + 连接地址
success(){
console.log('连接成功')
}
})
wx.onSocketOpen((res) => {
socketOpen = true
console.log("打开socket");
socketMsgQueue = []
wx.onSocketMessage((result) => {
result.data = result.data.replace(" ", " ");
var str = result.data.indexOf("--finish--") != -1 // 判断返回的数据中是否包含‘--finish--’
wx.createSelectorQuery().select('#idView').boundingClientRect(function (rect) {
// 使页面滚动到底部
wx.pageScrollTo({
scrollTop: rect.height+rect.bottom
})
that.setData({
scrollTop: rect.height+rect.bottom,
isHe: false
});
}).exec()
if (!!result.data) {
result.data=str?result.data.replace('--finish--',''):result.data
console.log(that.data.msgList,'-');
that.data.msgList[that.data.msgList.length - 1].content = that.data.msgList[that.data.msgList.length - 1].content + result.data
that.setData({
msgList:that.data.msgList
})
if (str) {
// 包含‘finish’结束字符,将返回结果存入缓存
let list =wx.getStorageSync('memberList')?JSON.parse(wx.getStorageSync('memberList')):[]
list.push({
speaker: 'server',
contentType: 'text',
content: that.data.msgList[that.data.msgList.length - 1].content
})
wx.setStorageSync('memberList',JSON.stringify(list))
}
}
if(str){
that.setData({
isDisable:false,
})
}
})
})
},
onHide: function () {
console.log('onhide');
wx.closeSocket()
wx.onSocketClose((result) => {
console.log("socket关闭成功");
wx.showToast({
icon: 'none',
title: '会话关闭成功',
duration: 500
})
})
},
onUnload:function(){
console.log('onUnload',);
wx.closeSocket()
wx.onSocketClose((result) => {
console.log("socket关闭成功");
})
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
ontext:function(e){
this.setData({
text:e.detail.value
})
},
sendClick:function(e){
// 发送问题计算页面信息,滚动距离
let that = this
wx.createSelectorQuery().select('#idView').boundingClientRect(function (rect) {
// 使页面滚动到底部
wx.pageScrollTo({
scrollTop: rect.height+rect.bottom
})
that.setData({
height: 'auto',
scrollTop: rect.height+rect.bottom
});
}).exec()
that.setData({
isDisable:true
})
sendSocketMessage(that.data.text) //发送问题
msgList.push({
speaker: 'customer',
contentType: 'text',
content: that.data.text
})
let list =wx.getStorageSync('memberList')?JSON.parse(wx.getStorageSync('memberList')):[]
list.push({
speaker: 'customer',
contentType: 'text',
content: that.data.text
})
// 将发送的问题存入缓存
wx.setStorageSync('memberList', JSON.stringify(list))
msgList.push({
speaker: 'server',
contentType: 'text',
content: ''
})
inputVal = '';
// 这里list.push一个空内容,是为了让三个点加载出现,相当于一个占位符
list.push({
speaker: 'server',
contentType: 'text',
content: ''
})
that.setData({
msgList:list,
text:'',
inputVal
});
}
}, commonMixin))
wxss
page {
background-color: #f1f1f1;
}
.inputRoom{
height: 10vh;
display: flex;
align-items: center;
z-index: 999;
background: #f1f1f1;
position: fixed;
bottom: 0;
width: 100%;
}
input {
width: 76vw;
height: 9.33vw;
background-color: #fff;
border-radius: 40rpx;
margin-left: 2vw;
padding: 0 3vw;
font-size: 28rpx;
color: #444;
}
.leftMsg {
font-size: 35rpx;
color: #444;
line-height: 7vw;
padding: 2vw 2.5vw;
background-color: #fff;
margin-left: -1.6vw;
border-radius: 10rpx;
z-index: 10;
word-break: break-all;
}
.rightMsg {
font-size: 35rpx;
color: #444;
line-height: 7vw;
padding: 2vw 2.5vw;
background-color: #96EB6A;
margin-right: -1.6vw;
border-radius: 10rpx;
z-index: 10;
}
input{
height: 12vw !important;
margin-bottom: 3vh;
}
.servers:nth-last-of-type(2){
margin-bottom: 5vw;
}
.servers:nth-last-of-type(2)>.leftMsg{
padding-bottom: 5vw;
}
.sendIcon{
width: 7vw;
margin-left: 3.2vw;
margin-bottom: 3vh
}
.loadView{
display: flex;
padding-top: 4vw;
}
.dot1, .dot2, .dot3 {
background: #fff;
width: 10rpx;
height: 10rpx;
border-radius: 50%;
margin: 10rpx;
}
.dot1 {
animation: jump 1.6s -0.32s linear infinite;
background: #ccc;
}
.dot2 {
animation: jump 1.6s -0.16s linear infinite;
background: #ccc;
}
.dot3 {
animation: jump 1.6s linear infinite;
background: #ccc;
}
@keyframes jump {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(2.0);
transform: scale(2.0);
}
}
至此就结束啦!有不足之处欢迎指出~