当前位置: 首页 > 工具软件 > vue-ellipsis > 使用案例 >

vue手把手带你创建聊天室(vue-native-websocket)

公瑞
2023-12-01

vue手把手带你创建聊天室(vue-native-websocket)

谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口
[1] 。

正文开始:
socket中使用了VUEX如果不打算使用状态管理器,可以忽略下文中的某些配置。

在vue中我们使用:vue-native-websocket
第一步安装:

npm install vue-native-websocket --save

然后在main.js中进行全局注册:

Vue.use(VueNativeSock,"后台地址*****",{
  // 启用Vuex集成
  store: store,
  // 数据发送/接收使用使用json
  format: "json",
  connectManually: true,
  reconnection: true,
 // 尝试重连的次数
 reconnectionAttempts: 5,
 // 重连间隔时间
 reconnectionDelay: 3000,
  passToStoreHandler: function (eventName, event) {
    if (!eventName.startsWith('SOCKET_')) { return }
    let method = 'commit';
    let target = eventName.toUpperCase();
    let msg = event;
    if (this.format === 'json' && event.data) {
      msg = JSON.parse(event.data);
      if (msg.mutation) {
        target = [msg.namespace || '', msg.mutation].filter((e) => !!e).join('/');
      } else if (msg.action) {
        method = 'dispatch';
        target = [msg.namespace || '', msg.action].filter((e) => !!e).join('/');
      }
    }
    this.store[method](target, msg);
    this.store.state.socket.message = msg;
  }
});

第三部写html:
在一个.vue结尾的文件中写入一下代码:

<!--公用组件:消息内容展示,实现群聊和单聊业务-->
<template>
    <div id="mainContent" >
        <div class="top-panel" ref="topPanel">
            <div class="title-panel">
                <!-- <p>当前在线人数: {{onlineUsers}}</p> -->
            </div>
        </div>
        <!--消息显示-->
        <div class="messages-panel" ref="messagesContainer">
            <div class="row-panel" v-for="(item,index) of senderMessageList" :key="index">
                <!--发送者消息样式-->
                <div class="sender-panel" v-if="item.userID == userID">
                    <!--昵称展示-->
                    <div class="user-name-panel sender">
                        <p>{{item.username}}</p>
                    </div>
                    <!--消息-->
                    <div class="msg-body">
                        <!--消息尾巴-->
                        <div class="tail-panel">
                            <svg class="icon" aria-hidden="true">
                                <use xlink:href="#icon-zbds30duihuakuangyou" color="#dce7dc"></use>
                            </svg>
                        </div>
                        <!--消息内容-->
                        <p v-html="item.msgText" @click="viewLargerImage($event)" ref="comment"/>
                    </div>
                    <!--头像-->
                    <div class="avatar-panel">
                        <img :src="item.avatarSrc" alt="">
                    </div>
                </div>
                <!--对方消息样式-->
                <div class="otherSide-panel" v-else>
                    <!--头像-->
                    <div class="avatar-panel">
                        <img :src="item.avatarSrc" alt="">
                    </div>
                    <!--昵称展示-->
                    <div class="user-name-panel sender">
                        <p>{{item.username}}</p>
                    </div>
                    <!--消息-->
                    <div class="msg-body">
                        <!--消息尾巴-->
                        <div class="tail-panel">
                            <svg class="icon" aria-hidden="true">
                                <use xlink:href="#icon-zbds30duihuakuangzuo"></use>
                            </svg>
                        </div>
                        <!--消息内容-->
                        <p v-html="item.msgText" @click="viewLargerImage($event)" ref="comment" />
                    </div>
                </div>
            </div>
        </div>
        <!--用户输入模块-->
        <div class="user-input-panel" @click="getEditableDivFocus()">
            <div class="toolbar-panel">
                <div class="item-panel" v-for="(item ,index) of toolbarList" :key="index">
                    <img class="emoticon" :src="require(`../assets/img/${item.src}`)"
                         @mouseenter="toolbarSwitch('hover',$event,item.src,item.hover,item.down,item.name)"
                         @mouseleave="toolbarSwitch('leave',$event,item.src,item.hover,item.down,item.name)"
                         @mousedown="toolbarSwitch('down',$event,item.src,item.hover,item.down,item.name)"
                         @mouseup="toolbarSwitch('up',$event,item.src,item.hover,item.down,item.name)" :alt="item.info">
                </div>
            </div>
            <div id="msgInputContainer" class="input-panel" ref="msgInputContainer" @keydown.enter.exact="sendMessage($event)"
                 contenteditable="true" spellcheck="false">
            </div>
             <div class="send-panel" ref="sendPanel" @click="mobileSend()">
                    <p>发送</p>
                </div>
            <!--表情面板-->
            <div class="emoticon-panel" :style="{display: emoticonShowStatus}" ref="emoticonPanel">
                <div class="row-panel">
                    <div class="item-panel" v-for="(item,index) of this.emojiList" :key="index">
                        <img :src="require(`../assets/images/emoji/${item.src}`)" :alt="item.info"
                             @mouseover="emojiConversion($event,'over',item.src,item.hover,item.info)"
                             @mouseleave="emojiConversion($event,'leave',item.src,item.hover,item.info)"
                             @click="emojiConversion($event,'click',item.src,item.hover,item.info)">
                    </div>
                </div>
                <div class="ico-panel"></div>
            </div>
        </div>
    </div>
</template>

<script src="../assets/js/message-display.js"></script>

<style lang="scss" src="../assets/css/message-display.scss" scoped></style>

style文件:

#mainContent {
  width: 100%;
  height: 100%;

  .top-panel {
    width: 100%;
    height: 30px;
    display: flex;
    align-items: center;
    border-bottom: 1px solid #cecece;

    .title-panel {
      width: 70%;
      height: 25px;
      display: flex;
      align-items: center;

      .equipmentType {
        width: 18px;
        height: 18px;
        margin-left: 5px;

        img {
          width: 100%;
          height: 100%;
        }
      }
    }

    /*操作栏样式:单聊*/
    .operate-panel {
      width: 29%;
      height: 25px;

      .ico-panel {
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: flex-end;
        align-items: center;

        .item-panel {
          width: 20px;
          height: 20px;

          img {
            width: 100%;
            height: 100%;
          }
        }
      }
    }

    /*操作栏样式:群聊*/
    // .operate-group-panel{

    // }
  }

  ::-webkit-scrollbar {
    width: 2px;
    /*滚动条宽度*/
    height: 6px;
    /*滚动条高度*/
  }

  .messages-panel {
    width: 100%;
    min-height: 400px !important;
    overflow-y: auto;
    max-height: 800px;
    overflow-x: hidden;
    padding-top: 5px;
    padding-bottom: 15px;

    .row-panel {
      width: 100%;
      min-height: 50px;

      /*对方消息样式*/
      .otherSide-panel {
        width: 96%;
        min-height: 50px;
        display: flex;
        margin-bottom: 15px;
        position: relative;

        .avatar-panel {
          width: 30px;
          min-width: 30px;
          height: 30px;
          border-radius: 50%;
          overflow: hidden;

          img {
            width: 100%;
            height: 100%;
          }
        }

        .user-name-panel {
          width: 240px;
          height: 20px;
          position: absolute;
          left: 42px;
          top: 2px;
          display: flex;
          justify-content: flex-start;

          align-items: center;

          p {
            color: #9da9c6;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }
        }

        .msg-body {
          max-width: 95%;
          min-height: 40px;
          background: #f4f3f3;
          border-radius: 5px;
          display: flex;
          align-items: center;
          padding: 10px;
          box-sizing: border-box;
          margin-top: 28px;
          margin-left: 16px;
          position: relative;

          /*消息尾巴*/
          .tail-panel {
            width: 20px;
            height: 100%;
            position: absolute;
            left: -10px;
            svg {
              margin-top: 8px;
              color: #f3f3f3;
            }
           
          }

          p {
            font-size: 12px;
            /*自动换行*/
            word-wrap: break-word;
            overflow: hidden;
            cursor: pointer;

            img {
              width: 100%;
              height: 100%;
            }
          }
        }
      }

      /*发送者消息样式*/
      .sender-panel {
        width: 96%;
        min-height: 50px;
        float: right;
        margin-right: 12px;
        display: flex;
        justify-content: flex-end;
        margin-bottom: 15px;
        position: relative;

        .avatar-panel {
          width: 30px;
          min-width: 30px;
          height: 30px;
          border-radius: 50%;
          overflow: hidden;

          img {
            width: 100%;
            height: 100%;
          }
        }

        .user-name-panel {
          width: 240px;
          height: 20px;
          position: absolute;
          right: 42px;
          top: 2px;
          overflow: hidden;
          text-overflow: ellipsis;
          display: flex;
          justify-content: flex-end;
          align-items: center;

          p {
            color: #9da9c6;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
          }
        }

        .msg-body {
          max-width: 95%;
          min-height: 40px;
          background: #d8e8dc;
          border-radius: 5px;
          display: flex;
          align-items: center;
          padding: 10px;
          box-sizing: border-box;
          margin-top: 28px;
          margin-right: 16px;
          position: relative;
          /*消息尾巴*/
          .tail-panel {
            width: 20px;
            height: 100%;
            position: absolute;
            right: -18px;
            svg {
              margin-top: 8px;
              color: #f3f3f3;
            }
          }

          p {
            font-size: 12px;
            line-height: 23px;
            // 强制换行
            word-break: break-all;
            display: flex;
            align-items: center;
            cursor: pointer;
            img {
              width: 100%;
              height: 100%;
              display: block;
            }
          }
        }
      }
    }
  }

  .send-panel {
    width: 70px;
    height: 30px;
    background-image: linear-gradient(-90deg, #29bdd9 0%, #276ace 100%);
    text-align: center;
    color: white;
    border-radius: 5px;
    line-height: 30px;
    cursor: pointer;
    float: right;
    margin-right: 5px;
  }
  .user-input-panel {
    width: 100%;
    height: 160px;
    position: relative;
    border-top: 1px solid #cecece;

    .toolbar-panel {
      width: 100%;
      height: 40px;
      display: flex;
      align-items: center;

      .item-panel {
        width: 24px;
        height: 24px;
        margin-right: 20px;
        display: flex;
        justify-content: center;
        align-items: center;

        img {
          width: 100%;
          height: 100%;
        }
      }

     
    }
   
    .input-panel {
      width: 100%;
      min-height: 30px;
      max-height: 120px;
      overflow-y: auto;
      outline: none;
      display: flex;
      align-items: center;
      flex-flow: row wrap;
      // 强制换行
      word-break: break-all;
    }

    /*表情面板*/
    .emoticon-panel {
      width: 290px;
      height: 250px;
      border-radius: 5px;
      background: white;
      border: solid 1px #dfe0e0;
      padding: 20px;
      box-sizing: border-box;
      position: absolute;
      top: -260px;
      // left: -194px;
      display: flex;
      justify-content: flex-start;
      z-index: 9999999;
     
      .row-panel {
        width: 100%;
        height: 30px;
        display: flex;
        align-items: center;
        flex-flow: row wrap;

        .item-panel {
          width: 25px;
          height: 25px;
          margin-right: 6px;
          margin-bottom: 7px;
          position: relative;
          // 取消12的倍数的元素的右外边距
          // &:nth-child(12n){
          //   margin-right: 0;
          // }

          img {
            width: 100%;
            height: 100%;

            &:hover {
              width: 26px;
              height: 26px;
            }
          }
        }
      }

      .ico-panel {
        width: 0;
        height: 0;
        border-right: 6px solid transparent;
        border-left: 6px solid transparent;
        border-top: 6px solid #dfe0e0;
        position: absolute;
        bottom: -6px;
        img {
          width: 100%;
          height: 100%;
        }
      }
    }
  }
}

JS:

import emoji from '../json/emoji';
import toolbar from '../json/toolbar';
import lodash from 'lodash';
import base from "./base";
import VueCookies from "vue-cookies";



export default {
    name: "message-display",
    data() {
        return {
            danmu: null,
            id: '1',
            roomid: '',
            images: [],
            userID: '',
            worker:null,
            ws:null,
            isHide:true,
            messagesContainerTimer: "",
            onlineUsers: this.$store.state.onlineUsers,
            createDisSrc: require("../img/titlebar_function_createDis_normal@2x.png"),
            resourceObj: {
                createDisNormal: require("../img/titlebar_function_createDis_normal@2x.png"),
                createDisHover: require("../img/titlebar_function_createDis_hover@2x.png"),
                createDisClick: require("../img/titlebar_function_createDis_normal_p@2x.png"),
                phoneNormal: require("../img/phone_normal_ap@2x.png"),
                groupMsgImg: require("../img/group-msg-img.png"),
                avatarImg: require("../img/avatar.jpg"),
                msgImgTest: require("../img/msg-img-test.gif"),
                msgImgTestB: require("../img/msg-img-testB.gif"),
            },
            // 消息内容
            messageContent: "",
            InputContent: "",
            emoticonShowStatus: "none",
            emojiList: emoji,
            toolbarList: toolbar,
            senderMessageList: [],
            audioCtx: null,
            // 声音频率
            arrFrequency: [
                196.00, 220.00, 246.94, 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.46, 783.99, 880.00, 987.77, 1046.50
            ],
            testWorker: null
        }
    },
    mounted: function () {
        let that = this
        let uid = this.$cookies.get('u_id')
        let course_id = this.$route.query.id
        let s_id = this.$cookies.get('s_id')
        let tokens = this.$cookies.get('PHPSESSID')
        this.$store.commit("changeCourse", {
            uid: uid,
            s_id: s_id,
            name: course_id
        });
        function work() {
            onmessage = ({ data: { jobId, message ,classID} }) => {
                postMessage({ jobId, result: message});
            };
        }
        const makeWorker = f => {
            let hide = that.isHide
            let pendingJobs = {};
            let worker = new Worker(
                URL.createObjectURL(new Blob([`(${f.toString()})()`])) //Blob = Binary Large Object的缩写,直译为二进制大对象
            );
            worker.onmessage = ({ data: { result, jobId,classID } }) => {
              
                let  ws = new WebSocket('wss://apps.beiqujy.com/count' +`?room_id=${result[0][0]}&uid=${result[0][1]}&type=3&from=2&class_id=${result[1]}`)
                ws.onopen = function () {
                    let login_data = '{"type":3,"from":2,"client_name":"' + '' + '","room_id":"' + result[0][0]+ '","class_id":"' + result[1] + '","uid":"' + result[0][1] + '"}';
                    console.log(login_data)
                    ws.send('首次发送消息'+login_data);
                };
                ws.onmessage = function (evt) {
                    var received_msg = evt.data;
                
                };
                ws.onclose = function () {
                    if(that.isHide){
                        reconnect('wss://apps.beiqujy.com/count' +`?room_id=${result[0][0]}&uid=${result[0][1]}&type=3&from=2&class_id=${result[1]}`)
                    }
                };
                function reconnect(url) {
                    var connects
                    clearTimeout(connects);
                    connects = setTimeout(function () {     //没连接上会一直重连,设置延迟避免请求过多
                        var ws = new WebSocket(url);
                        ws.onopen = function () {
                            let login_data = '{"type":3,"from":2,"client_name":"' + '' + '","room_id":"' + result[0][0]+ '","class_id":"' + result[1] + '","uid":"' + result[0][1] + '"}';
                            console.log('重连后发送的消息'+login_data)
                            ws.send(login_data);
                        };
                        ws.onmessage = function (evt) {
                            var received_msgs = evt.data;
                          
                        };
                        ws.onclose = function () {
                         
                            if(that.isHide){
                                reconnect('wss://apps.beiqujy.com/count' +`?room_id=${result[0][0]}&uid=${result[0][1]}&type=3&from=2&class_id=${result[1]}`)
                            }
                        };
                    }, 5000);
                }
                // 调用resolve,改变Promise状态
                pendingJobs[jobId](result);
                // 删掉,防止key冲突
                delete pendingJobs[jobId];
            };
            return (...message) =>
                new Promise(resolve => {
                    const jobId = String(Math.random());
                    pendingJobs[jobId] = resolve;
                    worker.postMessage({ jobId, message });
                });
        };
        this.testWorker = makeWorker(work);    
        this.$nextTick(() => {
            this.testWorker([VueCookies.get("roomid"), this.$store.state.uid],this.$route.query.id).then(message => {
            });
        });
        //链接socket
        this.$connect(process.env.VUE_APP_SOCKET + `?course_id=${course_id}&uid=${uid}&type='students'&from=2&listen_id=123`);
        this.userID = this.$store.state.uid
        var i = 0
        // webAudioAPI兼容性处理
        window.AudioContext = window.AudioContext || window.webkitAudioContext;
        // 设置列容器高度
        this.$refs.messagesContainer.style.height = this.getThisWindowHeight() - 350 + "px";
        // 全局点击事件,点击表情框以外的地方,隐藏当前表情框
        document.addEventListener('click', (e) => {
            let thisClassName = e.target.className;
            if (thisClassName !== "emoticon-panel" && thisClassName !== "emoticon") {
                this.emoticonShowStatus = "none";
            }
        });
        //从本地存储中获取数据渲染页面
        this.renderPage("", "", 1);
        // 监听消息接收
        this.$options.sockets.onmessage = (res) => {
            const data = JSON.parse(res.data);
            console.log(data)
            // this.messageChange()
            this.$store.commit("changeMessage", {
                msg: data.data.message
            });
            if (data.code == 201 || data.code == 202  ) {
                return
            } else {
                // this.$store.state.onlineUsers = data.onlineUsers;
                // 更新在线人数
                // this.onlineUsers = data.onlineUsers;
                // 获取服务端推送的消息
                const msgObj = {
                    msg: data.data.msg,
                    avatarSrc: data.data.user_img,
                    userID: data.data.uid,
                    username: data.data.user_nicename
                };
                if (lodash.isEmpty(localStorage.getItem("msgArray"))) {
                    this.renderPage(JSON.parse(localStorage.getItem("msgArray")), msgObj, 0);
                } else {
                    this.renderPage(JSON.parse(localStorage.getItem("msgArray")), msgObj, 0);
                }
            }
        };
    },
    beforeDestroy() {
        // 页面销毁时,断开连接
        localStorage.setItem("msgArray", '[]')
        this.isHide = false
        this.closeWorker()
        this.$disconnect();
    },
    methods: {
        closeWorker() {
            // console.log(this.testWorker)
            // this.worker.terminate()
            this.testWorker=null
        },
        createDisEventFun: function (status) {
            if (status === "hover") {
                this.createDisSrc = this.resourceObj.createDisHover
            } else if (status === "leave") {
                this.createDisSrc = this.resourceObj.createDisNormal
            } else {
                this.createDisSrc = this.resourceObj.createDisClick
            }
        },
        getThisWindowHeight: () => window.innerHeight,
        getThisWindowWidth: () => window.innerWidth,
        sendMessage: function (event) {
            if (event.keyCode === 13) {
                // 阻止编辑框默认生成div事件
                event.preventDefault();
                let msgText = "";
                // 获取输入框下的所有子元素
                let allNodes = event.target.childNodes;
                for (let item of allNodes) {
                    // 判断当前元素是否为img元素
                    if (item.nodeName === "IMG") {
                        msgText += `/${item.alt}/`;
                    } else {
                        // 获取text节点的值
                        if (item.nodeValue !== null) {
                            msgText += item.nodeValue;
                        }
                    }
                }
                // 消息发送: 发送文字,为空则不发送
                if (msgText.trim().length > 0) {
                    var obj = {
                        uid: parseInt( this.$cookies.get('u_id')),
                        message: msgText,
                        type:'students',
                        from:2,
                        event:1,
                        listen_id:123,
                        course_id: parseInt( this.$route.query.id)
                        
                    }
                    this.$socket.sendObj(obj)
                    event.target.innerHTML = "";
                }
            }
        },
        mobileSend: function () {
            // 模拟触发回车事件
            this.fireKeyEvent(this.$refs.msgInputContainer, 'keydown', 13);
        },
        //  渲染页面
        renderPage: function (msgArray, msgObj, status) {
            if (status === 1) {
                // 页面第一次加载,如果本地存储中有数据则渲染至页面
                let msgArray = [];
                if (localStorage.getItem("msgArray") !== null) {
                    msgArray = JSON.parse(localStorage.getItem("msgArray"));
                    for (let i = 0; i < msgArray.length; i++) {
                        const thisSenderMessageObj = {
                            "msgText": msgArray[i].msg,
                            "msgId": i,
                            "avatarSrc": msgArray[i].avatarSrc,
                            "userID": msgArray[i].userID,
                            "username": msgArray[i].username
                        };
                        // 更新消息内容
                        this.messageContent = msgArray[i].msg;
                        // 向父组件传值
                        this.$emit('updateLastMessage', this.messageContent);
                        // 解析并渲染
                        this.messageParsing(thisSenderMessageObj);
                    }
                }
            } else {
                // 判断本地存储中是否有数据
                if (localStorage.getItem("msgArray") === null) {
                    // 新增记录
                    msgArray.push(msgObj);
                    // 更新消息内容
                    this.messageContent = msgObj.msg;
                    // 向父组件传值
                    this.$emit('updateLastMessage', this.messageContent);
                    localStorage.setItem("msgArray", JSON.stringify(msgArray));
                    for (let i = 0; i < msgArray.length; i++) {
                        const thisSenderMessageObj = {
                            "msgText": msgArray[i].msg,
                            "msgId": i,
                            "avatarSrc": msgArray[i].avatarSrc,
                            "userID": msgArray[i].userID,
                            "username": msgArray[i].username
                        };
                        // 解析并渲染
                        this.messageParsing(thisSenderMessageObj);
                    }
                } else {
                    // 更新记录
                    msgArray = JSON.parse(localStorage.getItem("msgArray"));
                    msgArray.push(msgObj);
                    localStorage.setItem("msgArray", JSON.stringify(msgArray));
                    // 更新消息内容
                    this.messageContent = msgObj.msg;
                    // 向父组件传值
                    this.$emit('updateLastMessage', this.messageContent);
                    const thisSenderMessageObj = {
                        "msgText": msgObj.msg,
                        "msgId": Date.now(),
                        "avatarSrc": msgObj.avatarSrc,
                        "userID": msgObj.userID,
                        "username": msgObj.username
                    };
                    // 解析并渲染
                    this.messageParsing(thisSenderMessageObj);
                }
            }
        },
        // 模拟触发事件
        fireKeyEvent: function (el, evtType, keyCode) {
            let doc = el.ownerDocument,
                win = doc.defaultView || doc.parentWindow,
                evtObj;
            if (doc.createEvent) {
                if (win.KeyEvent) {
                    evtObj = doc.createEvent('KeyEvents');
                    evtObj.initKeyEvent(evtType, true, true, win, false, false, false, false, keyCode, 0);
                } else {
                    evtObj = doc.createEvent('UIEvents');
                    Object.defineProperty(evtObj, 'keyCode', {
                        get: function () {
                            return this.keyCodeVal;
                        }
                    });
                    Object.defineProperty(evtObj, 'which', {
                        get: function () {
                            return this.keyCodeVal;
                        }
                    });
                    evtObj.initUIEvent(evtType, true, true, win, 1);
                    evtObj.keyCodeVal = keyCode;
                    if (evtObj.keyCode !== keyCode) {
                        console.log("keyCode " + evtObj.keyCode + " 和 (" + evtObj.which + ") 不匹配");
                    }
                }
                el.dispatchEvent(evtObj);
            } else if (doc.createEventObject) {
                evtObj = doc.createEventObject();
                evtObj.keyCode = keyCode;
                el.fireEvent('on' + evtType, evtObj);
            }
        },
        // 消息解析
        messageParsing: function (msgObj) {
            console.log(msgObj)
            // 解析接口返回的数据进行渲染
            let separateReg = /(\/[^/]+\/)/g;
            let msgText = msgObj.msgText;
            let finalMsgText = "";
            if(msgText && msgText != undefined){
                // 将符合条件的字符串放到数组里
                const resultArray = msgText.match(separateReg);
           
            if (resultArray !== null) {
                for (let item of resultArray) {
                    // 删除字符串中的/符号
                    item = item.replace(/\//g, "");
                    // 判断是否为图片: 后缀为.jpeg
                    if (this.isImg(item)) {
                        const imgSrc = `${base.lkBaseURL}/uploads/chatImg/${item}`;
                        // 获取图片宽高
                        let imgInfo = {
                            "imgWidth": this.getQueryVariable(imgSrc, "width"),
                            "imgHeight": this.getQueryVariable(imgSrc, "height")
                        };
                        let thisImgWidth = 0;
                        let thisImgHeight = 0;
                        if (imgInfo.imgWidth < 400) {
                            thisImgWidth = imgInfo.imgWidth;
                            thisImgHeight = imgInfo.imgHeight;
                        } else {
                            // 缩放四倍
                            thisImgWidth = imgInfo.imgWidth / 4;
                            thisImgHeight = imgInfo.imgHeight / 4;
                        }
                        // 找到item中?位置,在?之前添加\\进行转义,解决正则无法匹配特殊字符问题
                        const charIndex = item.indexOf("?");
                        // 生成正则表达式条件,添加\\用于对?的转义
                        const regularItem = this.insertStr(item, charIndex, "\\");
                        // 解析为img标签
                        const imgTag = `<img width="${thisImgWidth}" height="${thisImgHeight}" src="${imgSrc}" alt="聊天图片">`;
                        // 替换匹配的字符串为img标签:全局替换
                        msgText = msgText.replace(new RegExp(`/${regularItem}/`, 'g'), imgTag);
                    }
                    // 表情渲染: 遍历表情配置文件
                    for (let emojiItem of this.emojiList) {
                        // 判断捕获到的字符串与配置文件中的字符串是否相同
                        if (emojiItem.info === item) {
                            const imgSrc = require(`../img/emoji/${emojiItem.hover}`);
                            const imgTag = `<img src="${imgSrc}" width="28" height="28" alt="${item}">`;
                            // 替换匹配的字符串为img标签:全局替换
                            msgText = msgText.replace(new RegExp(`/${item}/`, 'g'), imgTag);
                        }
                    }
                }
                finalMsgText = msgText;
            } else {
                finalMsgText = msgText;
            }
        }
            msgObj.msgText = finalMsgText;
            // 渲染页面
            this.senderMessageList.push(msgObj);
            let hash = {}
            this.senderMessageList = this.senderMessageList.reduce((prev, array) => {
                if (array.username == undefined) {
                    hash[array.username] ? '' : hash[array.username] = true && prev.push(array)
                } else {
                    prev.push(array)
                }
                return prev
            }, [])
            // 修改滚动条位置
            this.$nextTick(function () {
                this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
            });
        },
        // 显示表情
        toolbarSwitch: function (status, event, path, hoverPath, downPath, toolItemName) {
            if (status === "hover" || status === "up") {
                event.target.src = require(`../img/${hoverPath}`);
            } else if (status === "leave") {
                event.target.src = require(`../img/${path}`);
            } else {
                // 可编辑div获取焦点
                this.getEditableDivFocus();
                event.target.src = require(`../img/${downPath}`);
                // 表情框显示条件
                if (toolItemName === "emoticon") {
                    if (this.emoticonShowStatus === "flex") {
                        this.emoticonShowStatus = "none";
                    } else {
                        this.emoticonShowStatus = "flex";
                    }
                } else {
                    this.emoticonShowStatus = "none";
                }
            }
        },
        // 判断一个对象是否为函数类型
        isFunction: function (obj) {
            return typeof obj === "function" && typeof obj.nodeType !== "number";
        },
        // 表情框鼠标悬浮显示动态表情
        emojiConversion: function (event, status, path, hoverPath, info) {
            if (status === "over") {
                event.target.src = require(`../img/emoji/${hoverPath}`);
            } else if (status === "click") {
                // 表情输入
                const imgSrc = require(`../img/emoji/${hoverPath}`);
                const imgTag = `<img src="${imgSrc}" width="28" height="28" alt="${info}">`;
                document.execCommand("insertHTML", false, imgTag);
            } else {
                event.target.src = require(`../img/emoji/${path}`);
            }
        },
        // base64转file
        convertBase64UrlToImgFile: function (urlData, fileName, fileType) {
            // 转换为byte
            let bytes = window.atob(urlData);
            // 处理异常,将ascii码小于0的转换为大于0
            let ab = new ArrayBuffer(bytes.length);
            let ia = new Int8Array(ab);
            for (let i = 0; i < bytes.length; i++) {
                ia[i] = bytes.charCodeAt(i);
            }
            // 转换成文件,添加文件的type,name,lastModifiedDate属性
            let blob = new Blob([ab], { type: fileType });
            blob.lastModifiedDate = new Date();
            blob.name = fileName;
            return blob;
        },
        // 判断是否为图片
        isImg: function (str) {
            return str.indexOf(".jpeg") !== -1;
        },
        viewLargerImage: function (event) {
            const imgSrc = event.target.src;
            if (typeof imgSrc !== "undefined") {
                // 清空图片数组
                this.images = [];
                this.images.push(imgSrc);
                this.show();
            }
        },
        // 获取url参数
        getQueryVariable: function (url, variable) {
            // 对url进行截取
            url = url.substring(url.indexOf("?"), url.length);
            var query = url.substring(1);
            var vars = query.split("&");
            for (var i = 0; i < vars.length; i++) {
                var pair = vars[i].split("=");
                if (pair[0] == variable) { return pair[1]; }
            }
            return false;
        },
        // 字符串指定位置添加字符
        insertStr: function (source, start, newStr) {
            return source.slice(0, start) + newStr + source.slice(start);
        },
        // 可编辑div获取焦点
        getEditableDivFocus: function () {
            // 开头获取焦点
            this.$refs.msgInputContainer.focus();
        },
        // 图片查看插件
        show() {
            const viewer = this.$el.querySelector('.images').$viewer
            viewer.show()
        }
    },
}

至于表情包和一些聊天小组件,有需要的童鞋可以留言。

 类似资料: