iis默认网站路径,wordpress主题透明,什么网站可以查房屋建筑面积,wordpress postmeta文章目录 效果显示WebSocket连接使用全局变量WebSocket连接细节 最近和自己聊天的用户信息界面效果界面代码最近的聊天内容太长日期时间显示未读消息数量显示 私聊界面界面展示代码实现英文长串不换行问题聊天区域自动滑动到底部键盘呼出#xff0c;聊天区域收缩#xff0c;聊… 文章目录 效果显示WebSocket连接使用全局变量WebSocket连接细节 最近和自己聊天的用户信息界面效果界面代码最近的聊天内容太长日期时间显示未读消息数量显示 私聊界面界面展示代码实现英文长串不换行问题聊天区域自动滑动到底部键盘呼出聊天区域收缩聊天区域滑动到底部通知WebSocket服务器哪两个用户开始聊天 效果显示 WebSocket连接
使用全局变量
本小程序在用户浏览首页的时候创建WebSocket连接并将连接获得的WebSocket对象存储到全局变量中方便其他页面来使用WebSocket
首先在项目的main.js文件中声明全局变量socket Vue.prototype.$socket null对全局变量进行赋值
Vue.prototype.$socket this.$socket;后续如果需要使用全局变量直接使用this.$socket即可
WebSocket连接细节
下面的代码中有一个headbeat方法该方法主要用来定时给WebSocket服务器发送一个信号告诉WebSocket服务器当前客户端还处于连接状态。当心跳停止的时候比如客户端断网后端服务就会将用户信息从连接中移除
/*** 创建websocket连接*/
initWebsocket() {// console.log(this.socket: this.$socket)if (this.$socket null || this.$socket.readyState ! 1) {this.$socket uni.connectSocket({url: ws://10.23.17.146:8085/websocket/ uni.getStorageSync(curUser).userName,success(res) {console.log(WebSocket连接成功, res);},})// console.log(this.socket: this.$socket)// 监听WebSocket连接打开事件this.$socket.onOpen((res) {console.log(websocket连接成功)Vue.prototype.$socket this.$socket;// 连接成功开启心跳this.headbeat();});// 连接异常this.$socket.onError((res) {console.log(websocket连接出现异常);// 重连this.reconnect();})// 连接断开this.$socket.onClose((res) {console.log(websocket连接关闭);// 重连this.reconnect();})}
},
/*** 重新连接*/
reconnect() {console.log(重连);// 防止重复连接if (this.lockReconnect true) {return;}// 锁定防止重复连接this.lockReconnect true;this.initWebsocket();// 连接完成设置为falsethis.lockReconnect false;
},
// 开启心跳
headbeat() {console.log(websocket心跳);var that this;setTimeout(function() {if (that.$socket.readyState 1) {// websocket已经连接成功that.$socket.send({data: JSON.stringify({status: ping})})// 调用启动下一轮的心跳that.headbeat();} else {// websocket还没有连接成功重连that.reconnect();}}, that.heartbeatTime);
},最近和自己聊天的用户信息
界面效果 界面代码
templateview classcontainerscroll-view scrolltolowergetMoreChatUserVoview v-for(chatUserVo,index) in chatUserVoList :keyindex clicktrunToChat(chatUserVo)view styleheight: 10px;/viewview classchatUserVoItemview styledisplay: flex;align-items: center;uni-badge classuni-badge-left-margin :textchatUserVo.unReadChatNum absoluterightTopsizesmallu--image :showLoadingtrue :srcchatUserVo.userAvatar width50px height50px:fadetrue duration450view sloterror stylefont-size: 24rpx;加载失败/view/u--image/uni-badge/viewview stylemargin: 10rpx;/viewviewstyleline-height: 20px;width: 100%;display: flex;justify-content: space-between;flex-direction: column;view styledisplay: flex; justify-content: space-between;viewview classnickname{{chatUserVo.userNickname}}/viewview classcontent{{chatUserVo.lastChatContent}}/view/viewview classdate{{formatDateToString(chatUserVo.lastChatDate)}}/view/view!-- view styleheight: 10px;/view --u-line/u-line/view/view/view/scroll-view/view
/templatescriptimport {listChatUserVo} from /api/market/chat.js;import {listChat} from /api/market/chat.jsexport default {data() {return {chatUserVoList: [],page: {pageNum: 1,pageSize: 15},}},created() {},methods: {/*** 滑动到底部自动加载新一页的数据*/getMoreChatUserVo() {this.page.pageNum;this.listChatUserVo();},listChatUserVo() {listChatUserVo(this.page).then(res {// console.log(res:JSON.stringify(res.rows))// this.chatUserVoList res.rows;for (var i 0; i res.rows.length; i) {this.chatUserVoList.push(res.rows[i]);}})},/*** 格式化日期* param {Object} date*/formatDateToString(dateStr) {let date new Date(dateStr);// 今天的日期let curDate new Date();if (date.getFullYear() curDate.getFullYear() date.getMonth() curDate.getMonth() date.getDate() curDate.getDate()) {// 如果和今天的年月日都一样那就只显示时间return this.toDoubleNum(date.getHours()) : this.toDoubleNum(date.getMinutes());} else {// 如果年份一样就只显示月日return (curDate.getFullYear() date.getFullYear() ? : (date.getFullYear() -)) this.toDoubleNum((date.getMonth() 1)) - this.toDoubleNum(date.getDate());}},/*** 如果传入的数字是两位数直接返回* 否则前面拼接一个0* param {Object} num*/toDoubleNum(num) {if (num 10) {return num;} else {return 0 num;}},/*** 转到私聊页面*/trunToChat(chatUserVo) {let you {avatar: chatUserVo.userAvatar,nickname: chatUserVo.userNickname,username: chatUserVo.userName}uni.navigateTo({url: /pages/chat/chat?you encodeURIComponent(JSON.stringify(you))})},/*** 接收消息*/receiveMessage() {this.$socket.onMessage((response) {// console.log(接收消息 response.data);let message JSON.parse(response.data);// 收到消息将未读消息数量加一for (var i 0; i this.chatUserVoList.length; i) {if (this.chatUserVoList[i].userName message.from) {this.chatUserVoList[i].unReadChatNum;// 显示对方发送的最新消息listChat(message.from, {pageNum: 1,pageSize: 1}).then(res {this.chatUserVoList[i].lastChatContent res.rows[0].content});break;}}})},},onLoad(e) {this.receiveMessage();},onShow: function() {this.chatUserVoList [];this.listChatUserVo();},}
/scriptstyle langscss.container {padding: 20rpx;.chatUserVoItem {display: flex;margin: 0 5px;.nickname {font-weight: 700;}.content {color: #A7A7A7;font-size: 14px;/* 让消息只显示1行超出的文字内容使用...来代替 */overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 1;-webkit-box-orient: vertical;}.date {color: #A7A7A7;font-size: 12px;}}// .uni-badge-left-margin {// margin-left: 10px;// }}
/style最近的聊天内容太长
当最近的一条聊天内容太长的时候页面不太美观缺少整齐的感觉 解决的方式非常简单只需要添加以下样式即可
.content {/* 让消息只显示1行超出的文字内容使用...来代替 */overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 1;-webkit-box-orient: vertical;
}日期时间显示
本文显示日期时间的时候遵循以下原则
如果上次聊天时间的年月日和今天一致那就只显示时间即显示 时:分如果上次聊天时间的年份和今年一致那就只显示月-日如果上面的条件都不满足就显示年-月-日
在显示月、日、时、分的时候如果数字是一位数字就在前面补一个零具体操作如方法toDoubleNum
/*** 格式化日期* param {Object} date*/
formatDateToString(dateStr) {let date new Date(dateStr);// 今天的日期let curDate new Date();if (date.getFullYear() curDate.getFullYear() date.getMonth() curDate.getMonth() date.getDate() curDate.getDate()) {// 如果和今天的年月日都一样那就只显示时间return this.toDoubleNum(date.getHours()) : this.toDoubleNum(date.getMinutes());} else {// 如果年份一样就只显示月日return (curDate.getFullYear() date.getFullYear() ? : (date.getFullYear() -)) this.toDoubleNum((date.getMonth() 1)) - this.toDoubleNum(date.getDate());}
},
/**
* 如果传入的数字是两位数直接返回* 否则前面拼接一个0* param {Object} num*/
toDoubleNum(num) {if (num 10) {return num;} else {return 0 num;}
},未读消息数量显示
未读消息数量显示使用角标组件即uni-badge使用该组件需要下载安装插件下载链接下载之前需要看广告哈哈哈当然有钱可以不看 显示效果如下图
uni-badge classuni-badge-left-margin :textchatUserVo.unReadChatNum absoluterightTopsizesmallu--image :showLoadingtrue :srcchatUserVo.userAvatar width50px height50px:fadetrue duration450view sloterror stylefont-size: 24rpx;加载失败/view/u--image
/uni-badge私聊界面
界面展示
【微信公众平台模拟的手机界面】 【手机端键盘呼出之后的聊天区域】
代码实现
templateview styleheight:100vh;!-- scrolltoupper上滑到顶部执行事件此处用来加载历史消息 --!-- scroll-with-animationtrue 设置滚动条位置的时候使用动画过渡让动作更加自然 --scroll-view :scroll-into-viewscrollToView scroll-ytrue classmessageListScrollView:style{height:scrollViewHeight} scrolltouppergetHistoryChat():scroll-with-animation!isFirstListChat refchatScrollViewview v-for(message,index) in messageList :keymessage.id :idmessagemessage.idstylewidth: 750rpx;min-height: 60px;view styleheight: 10px;/viewview v-ifmessage.type0 classmessageItemLeftview stylewidth: 8px;/viewu--image :showLoadingtrue :srcyou.avatar width50px height50px radius3/u--imageview stylewidth: 7px;/viewview classmessageContent left{{message.content}}/view/viewview v-ifmessage.type1 classmessageItemRightview classmessageContent right{{message.content}}/viewview stylewidth: 7px;/viewu--image :showLoadingtrue :srcme.avatar width50px height50px radius3/u--imageview stylewidth: 8px;/view/view/view/scroll-viewview classmessageSendview classmessageInputu--textarea v-modelmessageInput placeholder请输入消息内容 autoHeight/u--textarea/viewview stylewidth:5px/viewview classcommmitButton clicksend()发 送/view/view/view/templatescriptimport {getUserProfileVo} from /api/user;import {listChat} from /api/market/chat.jslet socket;export default {data() {return {webSocketUrl: ,socket: null,messageInput: ,// 我自己的信息me: {},// 对方信息you: {},scrollViewHeight: undefined,messageList: [],// 底部滑动到哪里scrollToView: ,page: {pageNum: 1,pageSize: 15},isFirstListChat: true,loadHistory: false,// 消息总条数total: 0,}},created() {this.me uni.getStorageSync(curUser);},beforeDestroy() {console.log(执行销毁方法);this.endChat();},onLoad(e) {// 设置初始高度this.scrollViewHeight calc(100vh - 20px - 44px);this.you JSON.parse(decodeURIComponent(e.you));uni.setNavigationBarTitle({title: this.you.nickname,})this.startChat();this.listChat();this.receiveMessage();},onReady() {// 监听键盘高度变化以便设置输入框的高度uni.onKeyboardHeightChange(res {let keyBoardHeight res.height;console.log(keyBoardHeight: keyBoardHeight);this.scrollViewHeight calc(100vh - 20px - 44px - ${keyBoardHeight}px);this.scrollToView ;setTimeout(() {this.scrollToView message this.messageList[this.messageList.length - 1].id;}, 150)})},methods: {/*** 发送消息*/send() {if (this.messageInput ! ) {let message {from: this.me.userName,to: this.you.username,text: this.messageInput}// console.log(this.socket.send: this.$socket)// 将组装好的json发送给服务端由服务端进行转发this.$socket.send({data: JSON.stringify(message)});this.total;let newMessage {// code: this.messageList.length,type: 1,content: this.messageInput};this.messageList.push(newMessage);this.messageInput ;this.toBottom();}},/*** 开始聊天*/startChat() {let message {from: this.me.userName,to: this.you.username,text: ,status: start}// 告诉服务端要开始聊天了this.$socket.send({data: JSON.stringify(message)});},/*** 结束聊天*/endChat() {let message {from: this.me.userName,to: this.you.username,text: ,status: end}// 告诉服务端要结束聊天了this.$socket.send({data: JSON.stringify(message)});},/*** 接收消息*/receiveMessage() {this.$socket.onMessage((response) {// console.log(接收消息 response.data);let message JSON.parse(response.data);let newMessage {// code: this.messageList.length,type: 0,content: message.text};this.messageList.push(newMessage);this.total;// 让scroll-view自动滚动到最新的数据那里// this.$nextTick(() {// // 滑动到聊天区域最底部// this.scrollToView message this.messageList[this// .messageList.length - 1].id;// });this.toBottom();})},/*** 查询对方和自己最近的聊天数据*/listChat() {return new Promise((resolve, reject) {listChat(this.you.username, this.page).then(res {for (var i 0; i res.rows.length; i) {this.total res.total;if (res.rows[i].fromWho this.me.userName) {res.rows[i].type 1;} else {res.rows[i].type 0;}// 将消息放到数组的首位this.messageList.unshift(res.rows[i]);}if (this.isFirstListChat true) {// this.$nextTick(function() {// // 滑动到聊天区域最底部// this.scrollToView message this.messageList[this// .messageList.length - 1].id;// })this.toBottom();this.isFirstListChat false;}resolve();})})},/*** 滑到最顶端分页加一拉取更早的数据*/getHistoryChat() {// console.log(获取历史消息)this.loadHistory true;if (this.messageList.length this.total) {// 当目前的消息条数小于消息总量的时候才去查历史消息this.page.pageNum;this.listChat().then(() {})}},/*** 滑动到聊天区域最底部*/toBottom() {// 让scroll-view自动滚动到最新的数据那里this.scrollToView ;setTimeout(() {// 滑动到聊天区域最底部this.scrollToView message this.messageList[this.messageList.length - 1].id;}, 150)}}}
/scriptstyle langscss.messageListScrollView {background: #F5F5F5;overflow: auto;.messageItemLeft {display: flex;align-items: flex-start;justify-content: flex-start;.messageContent {max-width: calc(750rpx - 10px - 50px - 15px - 10px - 50px - 15px);padding: 10px;// margin-top: 10px;border-radius: 7px;font-family: sans-serif;// padding: 10px;// 让view只包裹文字width: auto;// display: inline-block !important;// display: inline;// 解决英文字符串、数字不换行的问题word-break: break-all;word-wrap: break-word;}}.messageItemRight {display: flex;align-items: flex-start;justify-content: flex-end;.messageContent {max-width: calc(750rpx - 10px - 50px - 15px - 10px - 50px - 15px);padding: 10px;// margin-top: 10px;border-radius: 7px;font-family: sans-serif;// padding: 10px;// 让view只包裹文字width: auto;// display: inline-block !important;// display: inline;// 解决长英文字符串、数字不换行的问题word-wrap: break-word;}}.right {background-color: #94EA68;}.left {background-color: #ffffff;}}.messageSend {display: flex;background: #ffffff;padding-top: 5px;padding-bottom: 15px;.messageInput {border: 1px #EBEDF0 solid;border-radius: 5px;width: calc(750rpx - 65px);margin-left: 5px;}.commmitButton {height: 38px;border-radius: 5px;width: 50px;display: flex;align-items: center;justify-content: center;color: #ffffff;background: #3C9CFF;}}
/style英文长串不换行问题 这个问题属于是整串英文被以为是一个单词了所以没有换行看下面的句子英文单词可以比较短的所以会自动换行
解决这个问题只需要添加下面的css即可
// 解决长英文字符串、数字不换行的问题
word-wrap: break-word;下面是添加之后的效果 聊天区域自动滑动到底部
在聊天的时候无论是发送一条新的消息还是接收到一条新的消息聊天区域都需要自动滑动到最新的消息那里。本文使用scroll-view组件来包裹显示聊天消息在scroll-view组件中可以通过给scroll-into-view属性赋值来指定聊天区域所显示到的位置。使用时需要注意如下问题
需要先给每一条消息设置一个id属性id属性存储的内容不能以数字开头因此本文在id之间拼接了一个字符串’message’scroll-view需要被设置好高度本文通过绑定一个变量来设置高度如:style{height:scrollViewHeight}因为手机端使用小程序打字时键盘呼出会影响聊天区域的高度 后续通过给scrollToView设置不同的值即可控制聊天区域的滑动比如每接收到一条新的消息就调用toBottom方法该方法通过设置scrollToView为message this.messageList[this.messageList.length - 1].id将聊天区域滑动到最新的消息处。需要注意的是在进行该值的设置之前需要延迟一段时间否则滑动可能不成功本文延迟150ms读者也可以探索不同的值该值不能太大或者太小。
通过设置scroll-view的属性scroll-with-animation的值为true可以让消息区域在滑动的时候使用动画过渡这样滑动更加自然。
键盘呼出聊天区域收缩聊天区域滑动到底部
当键盘呼出时需要将聊天区域的高度减去键盘的高度。同时将scrollToView赋值为最后一条消息的id。需要注意的是在设置scrollToView之前需要先将scrollToView设置为空字符串否则滑动效果可能不成功
onReady() {// 监听键盘高度变化以便设置输入框的高度uni.onKeyboardHeightChange(res {let keyBoardHeight res.height;console.log(keyBoardHeight: keyBoardHeight);this.scrollViewHeight calc(100vh - 20px - 44px - ${keyBoardHeight}px);this.scrollToView ;setTimeout(() {this.scrollToView message this.messageList[this.messageList.length - 1].id;}, 150)})
},通知WebSocket服务器哪两个用户开始聊天
为了便于后端在存储聊天数据的时候辨别消息是否为已读状态。比如在小王开始聊天之前需要先告诉后端“小王要开始和小明聊天了”如果正好小明也告诉后端“我要和小王聊天了”那小王发出去的消息就会被设置为已读状态因为他们两个此时此刻正在同时和对方聊天那小王发出去的消息就默认被小明看到了因此设置为已读状态
/**
* 开始聊天
*/
startChat() {let message {from: this.me.userName,to: this.you.username,text: ,status: start}// 告诉服务端要开始聊天了this.$socket.send({data: JSON.stringify(message)});
},
/*** 结束聊天*/
endChat() {let message {from: this.me.userName,to: this.you.username,text: ,status: end}// 告诉服务端要结束聊天了this.$socket.send({data: JSON.stringify(message)});
},