vue之websocket聊天功能实现

  • Post author:
  • Post category:vue


一、首先配置全局websocket

创建webSocket.js:

// global.js
export default {
  ws: {},
  setWs: function(newWs) {
      this.ws = newWs
  }
}

main.js引入:

import webSocket from './utils/webSocket.js'
Vue.prototype.$webSocket = webSocket

在其他页面使用:

this.$webSocket.ws

二、App.vue监听websocket

 created() {
        this.initWebsocket();
    }
 methods: {
initWebsocket() {
            let that = this
            if ('WebSocket' in window) {
                that.ws = new WebSocket('ws://127.0.0.1:30000/ws')
                that.$webSocket.setWs(that.ws)
                that.ws.onopen = function () {
                    setTimeout(() => {
                        that.queryMessage()
                    }, 500)
                }
                //that.onopen(); 这个地方未定义是会报错,所以我写成了  that.ws.onopen = function() {console.log('开始连接')};
                that.ws.onclose = function () {
                    // 关闭 websocket
                    console.log('连接已关闭...')
                    setTimeout(() => {
                        that.initWebsocket()
                    }, 2000)
                }
            } else {
                // 浏览器不支持 WebSocket
                console.log('您的浏览器不支持 WebSocket!')
            }
        },
        queryMessage() {
            let that = this;
            let customerList = this.$store.state.permission.customerList;
            let expertList = this.$store.state.permission.expertList;
            for (let index = 0; index < customerList.length; index++) {
                let msg = {
                    type: "login",
                    uid: customerList[index].id
                };
                if (that.$webSocket.ws && that.$webSocket.ws.readyState == 1) {
                    that.$webSocket.ws.send(JSON.stringify(msg));
                }
            }
            for (let index = 0; index < expertList.length; index++) {
                let msg = {
                    type: "login",
                    uid: expertList[index].id
                };
                if (that.$webSocket.ws && that.$webSocket.ws.readyState == 1) {
                    that.$webSocket.ws.send(JSON.stringify(msg));
                }
            }
            that.$webSocket.ws.onmessage = function (res) {
                let jsonData = JSON.parse(res.data);
                let data = jsonData.data;
                //新推过来条数
                let customerList = that.$store.state.permission.customerList;
                for (let index = 0; index < customerList.length; index++) {
                    if(jsonData.uid == customerList[index].id){
                        axios.post("获取总条数接口",{uid:customerList[index].id}).then(res=>{
                            if(res.data.success == true){
                                if(customerList[index].msgNum == null){
                                    customerList[index].msgNum=0;
                                    customerList[index].msgNum+=res.data.data;
                                }
                            }
                        })
                        if (data.type == "newConv") {
                            if (customerList[index].id != data.send_uid) {
                                customerList[index].msgNum++;
                                break;
                            }
                        } else if (data.type == "newMsg") {
                            if (customerList[index].id != data.send_uid) {
                                customerList[index].msgNum++;
                                break;
                            }
                        }
                    }
                }
                that.$store.state.permission.customerList = customerList;
                //新推过来条数
                let expertList = that.$store.state.permission.expertList;
                for (let index = 0; index < expertList.length; index++) {
                    if(jsonData.uid == expertList[index].id){
                        axios.post("获取总条数接口",{uid:expertList[index].id}).then(res=>{
                            if(res.data.success == true){
                                if(expertList[index].msgNum == null){
                                    expertList[index].msgNum=0;
                                    expertList[index].msgNum+=res.data.data;
                                }
                            }
                        })
                        if (data.type == "newConv") {
                            if (expertList[index].id != data.send_uid) {
                                expertList[index].msgNum++;
                                break;
                            }
                        } else if (data.type == "newMsg") {
                            if (expertList[index].id != data.send_uid) {
                                expertList[index].msgNum++;
                                break;
                            }
                        }
                    }
                }
                that.$store.state.permission.expertList = expertList;
            };
        }
}
}

三、聊天界面

<template>
  <div class="ContactWrap" v-if="uid != 0">
    <div class="Contact">
      <div class="ContactSide">
        <!-- <div class="ContactSide-tip"></div> -->
        <div >
            <div v-for="(item,index) in userList" :key="index" >
                <div class="ContactItem" @click="chooseUser(item)" :class="{ChooseItem : item.uid == chooseId}">
                    <img class="UserAvator" :src="item.avatar"  />
                    <div class="UserContent">
                    <div class="UserMsg">
                        <span class="UserName" style="padding-left:5px;">{{item.nickname}}</span>
                        <span class="MsgTime">{{item.last_msg_time.split(' ')[0]}}</span>
                    </div>
                    <div class="UserSnippet" style="padding-top:8px;" :title="title">{{customMethod(item.msg_type,item.msg_content)}}</div>
                    <span v-if="item.unreadCount != 0" style="float:right; display:inline-block; border-radius:5px 5px 5px 5px;line-height: 90px;height:15px;margin-left:100px;margin-top:-15px;color:rgb(255 255 255);font-size:10px;background-color:#cccccc;display:flex;justify-content:center;align-items:center;">{{item.unreadCount}}</span>
                    </div>
                </div>
            </div>
        </div>
      </div>
      <div class="ContactBox">
        <div class="ContactBox-header" style="font-size:18px;">{{titename}}</div>
        <div class="MessageBox" ref="MessageBox">
          <div
            v-for="(item,index) in chatList"
            :key="index"
            class="Message"
            :style="item.send_uid == uid?'flex-direction:row-reverse':''"
          >
            <!-- <div class="UserHead"> -->
            <img :src="item.send_uid == uid? pcimg:duifangimg" class="UserAvator" />
            <!-- </div> -->
            <div class="UserMsg" :class="item.send_uid == uid?'RightMessage':'LeftMessage'">
              <span v-if="item.msg_type == 1" :style="item.send_uid == uid?' float: right;':''">{{item.msg_content}}</span>
              <span
                        :style="item.send_uid == uid?' float: right;':''"
                        v-if=" item.msg_type == 2 "
                    >
                        <video
                            style="width: 160px"
                            controls="controls"
                            :src="item.msg_content"
                            alt=""
                            srcset=""
                        ></video>
                    </span>
                <span
                    :style="item.send_uid == uid?' float: right;':''"
                    v-if="item.msg_type == 4 "
                >
                     <img
                          v-image-preview
                          :src="item.msg_content"
                          alt=""
                          srcset=""
                          style="
                              vertical-align: text-top;
                              display: inline-block;
                              width: 40px;
                              height:40px
                          "
                      />
                </span>
                <span
                    :style="item.send_uid == uid?' float: right;':''"
                    v-if="item.msg_type == 3 "
                >
                     <audio
                        style="width: 200px"
                        controls="controls"
                        :src="item.msg_content"
                        alt=""
                        srcset=""
                    ></audio>
                </span>
            </div>
          </div>
        </div>
        <!-- 输入框 -->
        <div class="InputBox">
          <div v-if="titename != ''">
            <textarea v-model="msg" style="resize:none" class="InputTextarea" rows="3" ></textarea>
            <div class="InputBox-footer">
              <!-- <div class="FooterDesc">按Enter键发送</div> -->
              <button class="sendButton"  @click="sendmsg($event)">发送</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import util from "@/utils/index.js";
import axios from "axios";
export default {
  name: "Chat",
  components: {},
  data() {
    return {
      uid: 0,
      ws: null,
      msg: "", //聊天信息
      chatList: [
        // {
        //   id: 14,
        //   content: "哈哈哈",
        //   avaUrl:
        //     "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        // },
        // {
        //   id: 1,
        //   content: "嘿嘿嘿",
        //   avaUrl:
        //     "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        // },
      ], //聊天记录
      userList: [
        // {
        //   uid:1,
        //   username: "test1",
        //   content: "美美哒",
        //   time: "6-22",
        //   avaUrl:
        //     "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        // },
        // {
        //      uid:2,
        //   username: "test2",
        //   content: "活好每一天",
        //   time: "6-22",
        //   avaUrl:
        //     "https://pic4.zhimg.com/v2-a12b2d609fa2d5d16c10ea069419f3c3_xs.jpg"
        // },
      ],
      chooseId:0,
      titename:"",
      title: "",
      conv_id:"",
      duifangimg:"",
    };
  },
  props:['fuid','pcimg'],
  mounted() {
    this.uid = this.fuid;//可以自己写个假数据
    setTimeout(() => {
      this.getChatList();
    }, 300);
    //this.initWebsocket();
  },
  watch:{     //监听value的变化,进行相应的操做便可
    fuid: function(a,b){     //a是value的新值,b是旧值
      this.uid = this.fuid;
      this.chatList =[]
      this.getChatList();
    }
  },
  methods: {
    //发送信息
    sendmsg(event) {
      event.preventDefault()
      let msg = {"type":"send","send_id":this.uid,"receive_id":this.chooseId,"msg_type":"1","content":this.msg};
      if (this.$webSocket.ws && this.$webSocket.ws.readyState == 1) {
          this.$webSocket.ws.send(JSON.stringify(msg));
      }
      setTimeout(() => {
          this.getMsgList(this.conv_id);
      }, 500)
      this.msg = "";
    },
    //滚动条滚动到底部
    scrollBottm() {
      let el = this.$refs["MessageBox"];
      el.scrollTop = el.scrollHeight;
    },
    //选择联系人
    chooseUser(user){
        this.chooseId = user.uid;
        this.titename = user.nickname;
        this.getMsgList(user.conv_id);
    },
    //获取聊天列表
    getChatList(){
       axios.post("聊天列表接口",{uid:this.uid}).then(res=>{
           if(res.data.success == true){
               this.userList = res.data.data;
               if(this.userList.length>0){
                  this.chooseId = this.userList[0].uid;
                  this.titename = this.userList[0].nickname;
                  this.conv_id = this.userList[0].conv_id;
                  this.getMsgList(this.userList[0].conv_id);
                  this.duifangimg = this.userList[0].avatar;
               }else{
                  this.chooseId = '';
                  this.titename = '';
               }
               
           }
       })
    },
    //获取聊天记录
    getMsgList(chooseId){
      axios.post("聊天记录接口",{cid:chooseId,uid:this.uid,pagesize:'10'}).then(res=>{
           if(res.data.success == true){
                this.chatList = res.data.data;
                let msgNums = 0;
                for(let i=0;i<this.userList.length;i++){
                    if(this.userList[i].conv_id == chooseId){
                        msgNums = this.userList[i].unreadCount;
                        this.userList[i].unreadCount = 0;
                        this.$emit('getInitData',this.fuid,msgNums);
                    }
                }
                setTimeout(() => {
                  this.scrollBottm();
                }, 50);
           }
       })
    },
    //隐藏字符串
    customMethod(type,msg){
      if(type == '1'){
        if(msg.length<10){
          return msg;
        }else{
          this.title=msg.substring(0,10)+'...';
          return msg.substring(0,10)+'...';
        }
      }else {
        this.title='媒体消息';
        return '[媒体消息]';
      }
    },
    //重新计算未读消息数
    getUnreadNum(msgNums){
      //把未读消息数清空
      // for (let index = 0; index < this.$store.state.permission.customerList.length; index++) {
      //     if(this.fuid == this.$store.state.permission.customerList[index].id){
      //       if(this.$store.state.permission.customerList[index].msgNum != null && this.$store.state.permission.customerList[index].msgNum >= msgNums){
      //         this.$store.state.permission.customerList[index].msgNum = this.$store.state.permission.customerList[index].msgNum - msgNums;
      //       }else{
      //         this.$store.state.permission.customerList[index].msgNum = 0;
      //       }
      //     }
      // }
      // for (let index = 0; index < this.$store.state.permission.expertList.length; index++) {
      //     if(this.fuid == this.$store.state.permission.expertList[index].id){
      //       if(this.$store.state.permission.expertList[index].msgNum != null && this.$store.state.permission.expertList[index].msgNum >= msgNums){
      //         this.$store.state.permission.expertList[index].msgNum = this.$store.state.permission.expertList[index].msgNum - msgNums;
      //       }else{
      //         this.$store.state.permission.expertList[index].msgNum = 0;
      //       }
      //     }
      // }
     
    } 
    }
  }

</script>
<style lang="scss" scoped>
.Contact {
  height: 704px;
  width: 100%;
  background-color: #fff;
  border: 1px solid #ebebeb;
  box-shadow: 0 0 4px 0 rgba(26, 26, 26, 0.1);
  border-radius: 3px;
  display: flex;
  //联系人
  .ContactSide {
    width: 500px;
    height: 100%;
    border-right: 1px solid #ebebeb;
    .ContactSide-tip {
      height: 60px;
      line-height: 40px;
      font-weight: 600;
      padding: 0 30px;
      border-bottom: 1px solid #ebebeb;
    }
  }
  .ContactItem {
    padding: 12px 20px 12px 29px;
    cursor: pointer;
    display: flex;
    border-bottom: 1px solid #f7f8fa;
    .UserContent {
      flex: 1;
      .UserMsg {
        display: flex;
        align-items: center;
        justify-content: space-between;
        .UserName {
          font-size: 15px;
          columns: #444444;
          font-weight: 600;
        }
        .MsgTime {
          font-size: 12px;
          color: #999999;
          float: right;
        }
      }
    }
  }
  .ChooseItem{
    background: #f5f4f4;
 }
  .UserAvator {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    margin-right: 10px;
  }
  .ContactBox {
    width: 100%;
    &-header {
      background: #fafafa;
      font-size: 15px;
    //   margin: 0 14px;
    //   height: 20px;
      padding-bottom: 21px;
      padding-top: 22px;
      border-bottom: 1px solid #ebebeb;
      font-weight: 600;
      text-align: center;
    }
    //聊天框
    .MessageBox {
      height: 470px;
      overflow: scroll;
      .Message {
        display: flex;
        margin: 20px;
        .UserMsg {
          max-width: 388px;
          border-radius: 8px;
          padding: 6px 12px;
          font-size: 14px;
          position: relative;
          margin: 0 8px;
          text-align: left;
          white-space: pre-wrap;
          word-break: break-all;
        }
        .LeftMessage {
          background-color: #f6f6f6;
          color: #444;
          &::after {
            content: "";
            position: absolute;
            width: 8px;
            height: 8px;
            left: -4px;
            top: 14px;
            background: #f6f6f6;
            -webkit-transform: rotate(45deg);
            transform: rotate(45deg);
          }
        }
        .RightMessage {
          background-color: #0084ff;
          color: #fff;
          &::after {
            content: "";
            position: absolute;
            width: 8px;
            height: 8px;
            right: -4px;
            top: 14px;
            background: #0084ff;
            -webkit-transform: rotate(45deg);
            transform: rotate(45deg);
          }
        }
      }
    }
    //输入框
    .InputBox {
      padding: 0 10px;
      border-top: 1px solid #ebebeb;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      height: 168px;
      background: #fff;
      z-index: 10;
      .InputTextarea {
        margin-top: 20px;
        width: 100%;
        border: none;
        font-size: 14px;
        flex: 1;
        height:90px;
      }
      &-footer {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        height: 40px;
        .FooterDesc {
          font-size: 14px;
          color: #bfbfbf;
          padding-right: 10px;
        }
        .sendButton {
          color: #fff;
          background-color: #0084ff;
          border-radius: 6px;
          width: 72px;
          height: 32px;
          font-size: 13px;
          line-height: 32px;
          text-align: center;
        }
      }
    }
  }
}
 
</style>



版权声明:本文为qq_41992943原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。