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>

 

posted @ 2023-07-07 15:36  奔向太阳的向日葵  阅读(284)  评论(0编辑  收藏  举报