您好,登錄后才能下訂單哦!
這篇文章主要介紹vue如何實(shí)現(xiàn)web在線聊天功能,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
具體內(nèi)容如下
無限滾動(dòng)窗體的實(shí)現(xiàn)之前已經(jīng)介紹過,這里就不在贅述了,不清楚的可以通過文檔前文的傳送門進(jìn)行查看。
滾動(dòng)到兩天窗體頂部,自動(dòng)加載歷史跟多信息,數(shù)據(jù)加載的時(shí)候,需要有一個(gè)loading動(dòng)畫;
發(fā)送信息是滾動(dòng)條自動(dòng)滑動(dòng)到窗體底部,并且自己發(fā)送的信息出現(xiàn)在聊天窗體中;
收到別人發(fā)送信息時(shí),需要判斷滾動(dòng)條處于窗體中的位置,在距離底部一定范圍內(nèi)收到信息需要自動(dòng)滑動(dòng)到窗體底部;
收發(fā)的信息在聊天狀態(tài)不能重復(fù)顯示;
收發(fā)的信息在聊天窗體中需要以逆序的方式展示,即離窗體底部越近的信息為最新消息;
授信最好通過WebSocket與后端建立長(zhǎng)連接,有新消息由后端主動(dòng)向前端推送消息方式實(shí)現(xiàn),這里主要介紹前端實(shí)現(xiàn)聊天窗體思路,WebSocket部分就不展開了,采用定時(shí)器輪詢的方式簡(jiǎn)單實(shí)現(xiàn)。
話不多說,直接上代碼
我覺得所有的設(shè)計(jì)和功能實(shí)現(xiàn)都是基于數(shù)據(jù)的基礎(chǔ)上去實(shí)現(xiàn)的,所以咋們先來看一下后端返回的數(shù)據(jù)格式:
{ "code": 200, // 響應(yīng)編碼 "msg": "OK", // 響應(yīng)消息 "total": 1, "sysTime": "2020-12-16 15:23:27", // 系統(tǒng)響應(yīng)時(shí)間 "data": [{ "avatar": "", // 用戶頭像 "content": "{\"type\":\"txt\",\"msg\":\"你好!\"}", // 消息內(nèi)容 "isRead": 0, // 是否已讀 "isOneself": 0, // 是否是自己發(fā)送的消息 0否,1是 "msgId": 10, // 消息ID,用來去重 "nickName": "碧海燕魚", // 用戶昵稱 "userCode": "202012162030202232" // 用戶編碼 }] }
這里需要說明的是,content字段返回的是一個(gè)json格式的字符串?dāng)?shù)據(jù),content內(nèi)容格式如下:
// 文本消息 { "type": "txt", "msg":"你好" //消息內(nèi)容 }
// 圖片消息 { "type": "img", "url": "圖片地址", "ext":"jpg", "width":360, //寬 "height":480, //高 "size": 388245 }
// 視頻消息 { "type": 'video', "url": "http://nimtest.nos.netease.com/cbc500e8-e19c-4b0f-834b-c32d4dc1075e", "ext":"mp4", "width":360, //寬 "height":480, //高 "size": 388245 }
// 地理位置消息 { "type": "local", "address":"中國(guó) 浙江省 杭州市 網(wǎng)商路 599號(hào)", //地理位置 "longitude":120.1908686708565, // 經(jīng)度 "latitude":30.18704515647036 // 緯度 }
HTML代碼
<template> <Modal title="在線溝通" v-model="chatVisible" draggable footer-hide :width="580" @on-cancel="cancel"> <div class="chat"> <div class="chat-message-body" id ="chatform" @scroll="scroll" > <Spin v-if="loading"> <Icon type="ios-loading" size=18 class="spin-icon-load"></Icon> </Spin> <div dis-hover v-for="(item,index) in data" :key="index" class="message-card"> <div :class="item.isOneself == 1?'message-row-right': 'message-row-left'"> <img :src="item.avatar?item.avatar:defualtAvatar" height="35" width="35" > <div class="message-content"> <div :> {{item.nickName}} <span class="message-time"> {{item.createTime}}</span> </div> <div class="message-body"> {{item.content.msg}} </div> </div> </div> </div> </div> <Input v-model="form.msg" type="textarea" placeholder="主動(dòng)一點(diǎn),世界會(huì)更大!" :rows="4" /> </div> <div class="footer-btn"> <Button @click="cancel" type="text">取消</Button> <Button type="primary" @click="sendMsg">發(fā)送</Button> </div> </Modal> </template>
注:自己發(fā)的信息和別人發(fā)的信息展示樣式不一樣,所以需要通過isOneself字段進(jìn)行展示樣式的區(qū)分。
JavaScript代碼
<script> import {listMsg,sendMsg } from "@/api/index"; export default { name: "chat", props: { value: { type: Boolean, default: false } }, data() { return { chatVisible:this.value, loading:false, defualtAvatar:require('../../assets/defult-avatar.svg'), // 后端沒有返回頭像默認(rèn)頭像,注意:需要用require請(qǐng)求方式才能動(dòng)態(tài)訪問本地文件 data:[], distincData:[], // 消息去重?cái)?shù)組 offsetMax:0, // 最大偏移位,記錄當(dāng)前獲取的最大id,往后的定時(shí)輪詢數(shù)據(jù)時(shí)每次只獲取比這個(gè)id大的數(shù)據(jù) offsetMin:0, // 最小偏移位,記錄當(dāng)前獲取的最小id,往上滑動(dòng)時(shí)每次只獲取比這小id大的數(shù)據(jù) searchForm:{ // 每次定時(shí)獲取數(shù)據(jù)或首次加載數(shù)據(jù)提交的form表單數(shù)據(jù) pageNumber: 1, pageSize: 20 }, form:{ // 發(fā)送數(shù)據(jù)提交數(shù)據(jù)表單 content:"", msg:"" }, timerSwitch:0 // 定時(shí)器開關(guān),默認(rèn)關(guān)閉 }; }, methods: { init(){ }, loadMsg(){ // 窗體打開默認(rèn)加載一頁數(shù)據(jù),窗體什么周期中值運(yùn)行一次 let that = this; this.searchForm.offsetMax = this.offsetMax; listMsg(this.searchForm).then(res=>{ if (res.code == 200) { res.data.forEach(e => { // 標(biāo)記最大偏移位 if(that.offsetMax < e.msgId){ that.offsetMax = e.msgId; } e.content = JSON.parse(e.content); that.data.unshift(e) that.distincData.push(e.msgId); // 標(biāo)記最大偏移位,后端返回?cái)?shù)據(jù)是逆序,所以最后一條id最新 that.offsetMin = e.msgId; }); // 數(shù)據(jù)加載完成,滾動(dòng)條滾動(dòng)到窗體底部 this.scrollToBottom(); } }); }, show(){ // 打開窗體初始化數(shù)據(jù) // 初始化數(shù)據(jù) this.data =[]; this.distincData =[]; this.offsetMax = 0; this.offsetMin = 0; this.searchForm.pageNumber = 1; this.searchForm.pageSize = 20; this.form ={ content:"", msg:"" }; this.loadMsg(); this.chatVisible = true; // 開啟定時(shí)器 this.timerSwitch = 1; this.reloadData(); }, sendMsg(){ // 發(fā)送消息 if(!this.form.msg){ this.$Message.warning("不能發(fā)送空白信息"); return; } let content = { // 封裝消息體 type:"txt", msg:this.form.msg }; this.form.content = JSON.stringify(content); sendOrderMsg(this.form).then(res=>{ if (res.code == 200) { res.data.content = JSON.parse(res.data.content); this.data.push(res.data) this.form.msg=""; this.distincData.push(res.data.msgId); this.scrollToBottom(); // 發(fā)送信息只返回當(dāng)前一條,此時(shí)可能對(duì)方已經(jīng)發(fā)送信息,所以不修改偏移量 } }); }, scrollToBottom(){ // 滾動(dòng)到窗體底部 this.$nextTick(()=>{ let chatform = document.getElementById("chatform"); chatform.scrollTop = chatform.scrollHeight; }); }, // 滾動(dòng)到最上方,取歷史數(shù)據(jù),根據(jù)分頁參數(shù)取。不用修改偏移標(biāo)記位,但是需要判重 scroll(){ let chatform = document.getElementById("chatform"); let scrollTop = chatform.scrollTop; if(scrollTop == 0){ this.loading =true; let that = this; this.searchForm.offsetMin = this.offsetMin; this.searchForm.offsetMax = ""; listMsgByOrder(this.searchForm).then(res=>{ this.loading =false; if (res.code == 200) { res.data.forEach(e => { if(that.distincData.indexOf(e.msgId) <0){ e.content = JSON.parse(e.content); that.data.unshift(e); that.distincData.push(e.msgId); // 修改最小偏移位 if(that.offsetMin > e.msgId){ that.offsetMin = e.msgId; } } }); } }); } }, reloadData(){ // 判斷定時(shí)器開關(guān)是否開啟,如果開啟,則執(zhí)行定時(shí)器 if(this.timerSwitch){ setTimeout(() => { let params = {}; params.pageNumber = 1; params.pageSize = 20; params.offsetMax = this.offsetMax; let that = this; listMsgByOrder(params).then(res=>{ if (res.code == 200) { res.data.forEach(e => { // 修改最大偏移位,放到校驗(yàn)重復(fù)之前,防止當(dāng)前發(fā)送信息已經(jīng)放入消息列表,但是偏移值沒該的情況 if(that.offsetMax < e.msgId){ that.offsetMax = e.msgId; } if(that.distincData.indexOf(e.msgId) <0){ e.content = JSON.parse(e.content); that.data.push(e) that.distincData.push(e.msgId); // 收到新消息,判斷高度,如果當(dāng)前滾動(dòng)條高度距底部小于100,則動(dòng)滑到底部 let chatform = document.getElementById("chatform"); let gap = chatform.scrollHeight -chatform.scrollTop; if(gap >0 && gap < 400){ this.scrollToBottom(); } } }); that.reloadData(); } }); },1000*2); } }, cancel(){ // 關(guān)閉窗體需要把提示任務(wù)開關(guān)一起關(guān)閉調(diào) this.chatVisible = false; this.timerSwitch = 0; } }, mounted() { } }; </script>
CSS代碼
<style lang="less"> .message { height: 350px; } .ivu-card-body { padding:5px; } .ivu-modal-body{ padding: 0px 16px 16px 16px; } .chat-message-body { background-color:#F8F8F6; width:545px; height: 350px; overflow: auto; } .message-card { margin:5px; } .message-row-left { display: flex; flex-direction:row; } .message-row-right { display: flex; flex-direction:row-reverse; } .message-content { margin:-5px 5px 5px 5px; display: flex; flex-direction:column; } .message-body { border:1px solid #D9DAD9; padding:5px; border-radius:3px; background-color:#FFF; } .message-time { margin:0 5px; font-size:5px; color:#D9DAD9; } .footer-btn { float:right; margin-bottom: 5px; } .spin-icon-load { animation:ani-spin 1s linear infinite; } @keyframes ani-spin{ form{transform: rotate(0deg);} 50% {transform: rotate(180deg);} to {transform: rotate(360deg);} } </style>
以上是“vue如何實(shí)現(xiàn)web在線聊天功能”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。