uniapp webSocket聊天功能
<template> <view> <sc-nav back-color="white" :opacity="1" is-back> <text slot="content" class="sc-page-title">{{friendName}}</text> </sc-nav> <scroll-view :scroll-into-view="scrollViewTo" scroll-y scroll-anchoring @scrolltoupper="scrollToTop" class="bg-white" style="overflow-anchor: auto;" :style="{height: scrollHeight + 'px'}"> <view class="flex justify-center align-center" style="width: 100%;background-color: aliceblue;padding: 10rpx 0;" v-if="historyLoading"> 正在刷新 </view> <view class="cu-chat bg-white"> <view v-for="(item,index) in messageList" :key="index" :id="`msg-${item.id}`"> <view class="cu-item self" v-if="friendName !== item.senderName"> <view class="main"> <view class="content bg-grey shadow"> <text :class="{'content-min': item.sendContent.length < 2}">{{item.sendContent}}</text> </view> </view> <view class="cu-avatar radius"> <sc-image :src="mySrc" style="width: 100%;height: 100%;"></sc-image> </view> <view class="date">{{timeFormat(item.receivingTime)}}</view> </view> <view class="cu-item" v-if="friendName === item.senderName"> <view class="cu-avatar radius"> <sc-image :src="friendSrc" style="width: 100%;height: 100%;"></sc-image> </view> <view class="main"> <view class="content bg-blue shadow"> <text :class="{'content-min': item.sendContent.length < 2}">{{item.sendContent}}</text> </view> </view> <view class="date">{{timeFormat(item.receivingTime)}}</view> </view> </view> </view> </scroll-view> <view class="cu-bar foot input" :style="[{bottom:InputBottom+'px'}]"> <input class="" placeholder="请输入" :class="{'input1':msg.sendContent === ''}" :adjust-position="false" :focus="false" maxlength="300" cursor-spacing="10" v-model="msg.sendContent"></input> <button class="cu-btn bg-green shadow animation-slide-top" v-if="msg.sendContent !== ''" @click="sendSocketMessage(msg)">发送</button> <!-- <image v-else style="width:66rpx;height:66rpx;" src="../../static/images/icon/addPhone@2x.png" mode=""> --> </image> </view> </view> </template> <script> import { baseUrl } from '@/config.js' import { mapGetters } from 'vuex' import { recordMessage } from '@/api/application.js' import WS from '@/utils/websocket.js' export default { data() { return { baseUrl: baseUrl, InputBottom: 0, msg: { linkType: 'msg', senderName: '', senderId: '', sendContent: '', recipientId: '', recipientName: '', receivingTime: '' }, messageList: [], friendName: '', friendSrc: '', mySrc: '', pagination: { pageSize: 15, pageNum: 1, flag: true }, ws: null, wsUrl: '', curDate: '', scrollViewTo: null, historyLoading: false, nomore: false, screnHeight: 0, CustomBar: this.CustomBar }; }, computed: { ...mapGetters(['userInfo']), timeFormat() { return function(time) { if (time.substr(0, 10) === this.curDate) { return time.substr(11) } return time } }, scrollHeight() { return this.screnHeight - this.InputBottom - uni.upx2px(152) - this.CustomBar } }, onLoad(page) { var data = JSON.parse(decodeURIComponent(page.data)) this.friendName = data.name this.friendSrc = data.avatar this.mySrc = this.userInfo.avatar this.initMsg(data) this.initDate() this.createWebScoket() this.restKeyboardHeught() }, mounted() { this.$nextTick(() => { this.loadRecordMessage() }); let sys = uni.getSystemInfoSync() this.screnHeight = sys.windowHeight }, onUnload() { this.ws.closeSocket() }, methods: { restKeyboardHeught(){ uni.onKeyboardHeightChange(res=>{ this.InputBottom = res.height // 键盘弹起,自动跳转到最新消息 if(res.height > 0 && this.messageList.length) { this.scrollViewTo = `msg-${this.messageList[this.messageList.length - 1].id}` } }) }, initDate() { this.curDate = this.$time.formatDate(Date.now()) }, // msg初始化 initMsg(data) { if (data.appMessage !== null) { //从消息记录进入聊天详情 this.msg.recipientId = data.userId this.msg.recipientName = data.name if (data.appMessage.senderName === data.name) { this.msg.senderName = data.appMessage.recipientName this.msg.senderId = data.appMessage.recipientId } else { this.msg.senderName = data.appMessage.senderName this.msg.senderId = data.appMessage.senderId } } else { //从人员列表进入聊天详情 this.msg.senderName = this.userInfo.studentName this.msg.senderId = this.userInfo.userId.toString() this.msg.recipientName = data.name this.msg.recipientId = data.userId } }, initWsUrl() { if (this.baseUrl === 'http://10.148.12.191/sc-api') { //测试 this.wsUrl = 'ws://10.148.12.196:8099/realtimeMessageWebsocket' } else { this.wsUrl = this.baseUrl.replace(/http/g, 'ws') + '/realtimeMessageWebsocket' } }, // 创建websockt createWebScoket() { this.initWsUrl() this.ws && this.ws.closeSocket() this.ws = new WS(this.wsUrl) this.ws.getWebSocketMsg(data => { // this.pagination.pageNum = 1 this.nomore = false // this.messageList = [] this.loadRecordMessage(true) }) }, scrollToTop() { if(this.historyLoading || this.nomore) return this.historyLoading = true this.loadRecordMessage() }, //获取聊天记录 loadRecordMessage(isAdd = false) { if (!this.pagination.flag) return if (this.nomore) return const requestParams = { ...this.pagination, userId: this.userInfo.userId, personId: this.msg.recipientId, } if (isAdd) requestParams.pageNum = 1 // this.pagination.flag = false recordMessage(requestParams).then(rs => { if (rs.code === 200) { let data = rs.rows let selector = '', scrollToBottom = false if (this.pagination.pageNum > 1 && !isAdd) { // 非第一页,则取历史消息数据的第一条信息元素 selector = `msg-${this.messageList[0].id}` } else { // 第一页,则取当前消息数据的最后一条信息元素 selector = data.length ? `msg-${data[data.length-1].id}` : '' } // 发送,接受消息已有消息比较插入 if (isAdd) { data.forEach(item => { let index = this.messageList.findIndex(v => v.id === item.id) if (index === -1) { this.messageList.push(item) } }) this.$nextTick(() => { this.scrollViewTo = selector this.pagination.flag = true }) } else { let time = this.pagination.pageNum === 1? 0: 300 setTimeout(() => { this.messageList = [...data, ...this.messageList] this.historyLoading = false this.$nextTick(() => { this.scrollViewTo = selector this.pagination.flag = true if (data.length < this.pagination.pageSize) { // 当前消息数据条数小于请求要求条数时,则无更多消息,不再允许请求。 this.nomore = true } else { this.pagination.pageNum++ } }) }, time) } // this.$nextTick(() => { // this.scrollViewTo = selector // this.pagination.flag = true // if (data.length < this.pagination.pageSize) { // // 当前消息数据条数小于请求要求条数时,则无更多消息,不再允许请求。 // this.nomore = true // } else { // this.pagination.pageNum++ // } // }) this.pagination.flag = false } }) }, //发送消息 sendSocketMessage(msg) { this.ws.webSocketSendMsg(JSON.stringify(msg), ({ success, data }) => { if (success) { // this.messageList = [] // this.pagination.pageNum = 1 this.nomore = false this.loadRecordMessage(true) this.msg.sendContent = '' } }) } } } </script> <style lang="less" scoped> page { // padding-bottom: 100upx;s } .cu-chat .cu-item.self>.main .content::after, .cu-chat .cu-item.self>.main .content::before { display: none; } .cu-chat .cu-item>.main .content::after, .cu-chat .cu-item>.main .content::before { display: none; } .cu-chat .cu-item>.main { margin: 0 16rpx; } /deep/ .cu-chat .cu-item>.main .content { border-radius: 30rpx; word-break: break-all; } .cu-chat .cu-item>.cu-avatar { border-radius: 16rpx; } .cu-bar.input { height: 152rpx; background: #F1F2F3; } .cu-bar.input uni-input { width: 612rpx; height: 84rpx; } /deep/ .uni-input-input { width: 238 * 2rpx; padding: 0 20rpx; background: #FFFFFF; border-radius: 20rpx; } .uni-input-placeholder { width: 238 * 2rpx; z-index: 10; margin-left: 38rpx; height: 38rpx; font-size: 32rpx; font-family: LXGWWenKai-Bold, LXGWWenKai; font-weight: bold; color: rgba(0, 0, 0, 0.45); line-height: 38rpx; } .input1 { /deep/ .uni-input-input { width: 309 * 2rpx; } } /deep/ .cu-bar.foot { box-shadow: none; } // .cu-avatar{ // background-image: url(https://ossweb-img.qq.com/images/lol/web201310/skin/big143004.jpg); // } // .self{ // .cu-avatar{ // background-image: url(https://ossweb-img.qq.com/images/lol/web201310/skin/big107000.jpg); // } // } // .bg-white { // // min-height: calc(100vh - 260rpx); // // height: auto !important; // // padding-bottom: 20rpx; // border-top: 2rpx solid rgba(33, 40, 51, 0.06); // } .bg-grey { font-size: 32rpx; font-family: LXGWWenKai-Bold, LXGWWenKai; font-weight: bold; color: #000000; line-height: 38rpx; background: #F5F5F5; border-top-right-radius: 0 !important; } .bg-blue { font-size: 32rpx; font-family: LXGWWenKai-Bold, LXGWWenKai; font-weight: bold; color: #FFFFFF; line-height: 38rpx; background: #5A8EEA; border-top-left-radius: 0 !important; } .date { height: 28rpx; font-size: 24rpx; font-family: LXGWWenKai-Bold, LXGWWenKai; font-weight: bold; color: rgba(0, 0, 0, 0.25); line-height: 28rpx; width: calc(100% - 250upx) !important; left: 125rpx !important; letter-spacing: -0.17px; } .content-min { min-width: 40rpx; text-align: center !important; } </style>