vue实现web在线聊天功能

本文实例为大家分享了vue实现web在线聊天的具体代码,供大家参考,具体内容如下

最终实现的效果

实现过程

无限滚动窗体的实现之前已经介绍过,这里就不在赘述了,不清楚的可以通过文档前文的传送门进行查看。

实时在线聊天主要功能点

  • 滚动到两天窗体顶部,自动加载历史跟多信息,数据加载的时候,需要有一个loading动画;
  • 发送信息是滚动条自动滑动到窗体底部,并且自己发送的信息出现在聊天窗体中;
  • 收到别人发送信息时,需要判断滚动条处于窗体中的位置,在距离底部一定范围内收到信息需要自动滑动到窗体底部;
  • 收发的信息在聊天状态不能重复显示;
  • 收发的信息在聊天窗体中需要以逆序的方式展示,即离窗体底部越近的信息为最新消息;
  • 授信最好通过WebSocket与后端建立长连接,有新消息由后端主动向前端推送消息方式实现,这里主要介绍前端实现聊天窗体思路,WebSocket部分就不展开了,采用定时器轮询的方式简单实现。

话不多说,直接上代码

后端返回数据格式

我觉得所有的设计和功能实现都是基于数据的基础上去实现的,所以咋们先来看一下后端返回的数据格式:

{
 "code": 200, // 响应编码
 "msg": "OK", // 响应消息
 "total": 1,
 "sysTime": "2020-12-16 15:23:27", // 系统响应时间
 "data": [{
  "avatar": "",  // 用户头像
  "content": "{\"type\":\"txt\",\"msg\":\"你好!\"}", // 消息内容
  "isRead": 0, // 是否已读
  "isOneself": 0,  // 是否是自己发送的消息 0否,1是
  "msgId": 10, // 消息ID,用来去重
  "nickName": "碧海燕鱼", // 用户昵称
  "userCode": "202012162030202232" // 用户编码
 }]
}

这里需要说明的是,content字段返回的是一个json格式的字符串数据,content内容格式如下:

// 文本消息
{
  "type": "txt",
  "msg":"你好" //消息内容
}
// 图片消息
{
  "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":"中国 浙江省 杭州市 网商路 599号",    //地理位置
  "longitude":120.1908686708565,        // 经度
  "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 :style="item.isOneself == 1?'text-align:right;display: flex;flex-direction:row-reverse':''">
                {{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"
        style="margin:10px 0;"
        placeholder="主动一点,世界会更大!"
        :rows="4"
      />
     </div>
     <div class="footer-btn">
        <Button @click="cancel" type="text">取消</Button>
        <Button type="primary" @click="sendMsg">发送</Button>
      </div>
  </Modal>
</template>

注:自己发的信息和别人发的信息展示样式不一样,所以需要通过isOneself字段进行展示样式的区分。

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'), // 后端没有返回头像默认头像,注意:需要用require请求方式才能动态访问本地文件
      data:[],
      distincData:[], // 消息去重数组
      offsetMax:0, // 最大偏移位,记录当前获取的最大id,往后的定时轮询数据时每次只获取比这个id大的数据
      offsetMin:0,  // 最小偏移位,记录当前获取的最小id,往上滑动时每次只获取比这小id大的数据
      searchForm:{ // 每次定时获取数据或首次加载数据提交的form表单数据
        pageNumber: 1,
        pageSize: 20
      },
      form:{ // 发送数据提交数据表单
        content:"",
        msg:""
      },
      timerSwitch:0 // 定时器开关,默认关闭
    };
  },
  methods: {
    init(){

    },
    loadMsg(){ // 窗体打开默认加载一页数据,窗体什么周期中值运行一次
      let that = this;
      this.searchForm.offsetMax = this.offsetMax;
      listMsg(this.searchForm).then(res=>{
        if (res.code == 200) {
          res.data.forEach(e => {
            // 标记最大偏移位
            if(that.offsetMax < e.msgId){
                that.offsetMax = e.msgId;
            }
            e.content = JSON.parse(e.content);
            that.data.unshift(e)
            that.distincData.push(e.msgId);
            // 标记最大偏移位,后端返回数据是逆序,所以最后一条id最新
            that.offsetMin = e.msgId;
           });
          // 数据加载完成,滚动条滚动到窗体底部
          this.scrollToBottom();
        }
      });

    },
    show(){ // 打开窗体初始化数据
      // 初始化数据
      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;
      // 开启定时器
      this.timerSwitch = 1;
      this.reloadData();
    },
    sendMsg(){ // 发送消息
      if(!this.form.msg){
         this.$Message.warning("不能发送空白信息");
        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();
          // 发送信息只返回当前一条,此时可能对方已经发送信息,所以不修改偏移量
        }
      });
    },
    scrollToBottom(){ // 滚动到窗体底部
      this.$nextTick(()=>{
          let chatform = document.getElementById("chatform");
          chatform.scrollTop = chatform.scrollHeight;
      });
    },
    // 滚动到最上方,取历史数据,根据分页参数取。不用修改偏移标记位,但是需要判重
    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(){
    // 判断定时器开关是否开启,如果开启,则执行定时器
    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 => {
              // 修改最大偏移位,放到校验重复之前,防止当前发送信息已经放入消息列表,但是偏移值没该的情况
              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);
                // 收到新消息,判断高度,如果当前滚动条高度距底部小于100,则动滑到底部
                let chatform = document.getElementById("chatform");
                let gap = chatform.scrollHeight -chatform.scrollTop;
                if(gap >0 && gap < 400){
                  this.scrollToBottom();
                }
              }
            });
            that.reloadData();
          }
        });
      },1000*2);
    }

   },
   cancel(){ // 关闭窗体需要把提示任务开关一起关闭调
     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>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • vue + socket.io实现一个简易聊天室示例代码

    vue + vuex + elementUi + socket.io实现一个简易的在线聊天室,提高自己在对vue系列在项目中应用的深度.因为学会一个库或者框架容易,但要结合项目使用一个库或框架就不是那么容易了.功能虽然不多,但还是有收获.设计和实现思路较为拙劣,恳请各位道友指正. 可以达到的需求 能查看在线用户列表 能发送和接受消息 使用到的框架和库 socket.io做为实时通讯基础 vuex/vue:客户端Ui层使用 Element-ui:客户端Ui组件 类文件关系图 服务端: 客户端: 服

  • 基于vue和websocket的多人在线聊天室

    最近看到一些关于websocket的东西,就决定写一个在线聊天室尝试一下.最终决定配合vue来写,采用了官方的vue脚手架vue-cli和官方的router,在本例中呢,我是用了CHAT这个对象来存储app的数据的,但后来一想,虽然项目很小,但如果用官方的vuex会更好,方便以后扩展,比如可以加上私信功能,可以在对方不在线的时候缓存发送的消息,这些都是可以的.(现在比较尴尬的就是,我把聊天室写好放到公众号号redream里,但是很少有人会同时在线,所以你会经常发现你进去的时候只有你一个人,就导致

  • 基于Vue2实现的仿手机QQ单页面应用功能(接入聊天机器人 )

    概述 使用Vue2进行的仿手机QQ的webapp的制作,在ui上,参考了设计师kaokao的作品,作品由个人独立开发,源码中进行了详细的注释. 由于自己也是初学Vue2,所以注释写的不够精简,请见谅. 项目地址 https://github.com/jiangqizheng/vue-MiniQQ 项目已实现功能 对话功能--想着既然是QQ总要能进行对话交流,所以在项目中接入了图灵聊天机器人,可以与列表中的每个人物进行对话. 左滑删除--左滑删除相关消息. 搜索页面--点击右上角搜索按钮,能够进入

  • 基于node+vue实现简单的WebSocket聊天功能

    首先,我需要用到node的nodejs-websocket模块 使用yarn进行安装 yarn add nodejs-websocket --save 当然,你也可以用npm进行安装 npm i nodejs-websocket --save 安装完毕之后,我们开始写服务端的代码,首先,我用node在本地起了一个node服务器用来开启websocket服务 sock.js: let ws = require("nodejs-websocket"); console.log("

  • Vue +WebSocket + WaveSurferJS 实现H5聊天对话交互的实例

    引言 在与实现了语音合成.语义分析.机器翻译等算法的后端交互时,页面可以设计成更为人性化.亲切的方式.我们采用类似于聊天对话的实现,效果如下: 智能客服(输入文本,返回引擎处理后的文本结果) 语音合成(输入文本,返回文本以及合成的音频) 如上图所示,返回文本后,再返回合成出的音频. 音频按钮嵌在对话气泡中,可以点击播放. 语音识别(在页面录制语音发送,页面实时展示识别出的文本结果) 实现功能及技术要点 1.基于WebSocket实现对话流 页面与后端的交互是实时互动的,所以采用WebSocket

  • Vue.js仿微信聊天窗口展示组件功能

    源码:https://github.com/doterlin/vue-wxChat 演示地址:https://doterlin.github.io/vue-wxChat/ 运行 # install dependencies npm install # serve with hot reload at localhost:8080 npm run dev # build for production with minification npm run build 介绍 支持文本和图片的展示(后续将

  • Vue Cli 3项目使用融云IM实现聊天功能的方法

    介绍:前台使用vue开发的单页面,后台使用ant design pro单页面,实现手机端和后台聊天功能. 效果如图(PC+移动): 一.申请融云账号(token.appKey) 建议先看教程:sdk使用介绍 过一遍教程,接下来开始写 二.引入融云IM 如图: 位置:public/index.html,引入 <script src="https://cdn.ronghub.com/RongIMLib-2.3.5.min.js"></script> 三.可以正常使用

  • vue+web端仿微信网页版聊天室功能

    一.项目介绍 基于Vue2.5.6+Vuex+vue-cli+vue-router+vue-gemini-scrollbar+swiper+elementUI等技术混合架构开发的仿微信web端聊天室--vueWebChat,实现了发送消息.表情(动图),图片.视频预览,右键菜单.截屏.截图可直接粘贴至文本框进行发送. 二.技术框架 •MVVM框架:Vue2.5.6 •状态管理:Vuex •页面路由:Vue-router •iconfont图标:阿里巴巴字体图标库 •自定义滚动条:vue-gemi

  • vue实现web在线聊天功能

    本文实例为大家分享了vue实现web在线聊天的具体代码,供大家参考,具体内容如下 最终实现的效果 实现过程 无限滚动窗体的实现之前已经介绍过,这里就不在赘述了,不清楚的可以通过文档前文的传送门进行查看. 实时在线聊天主要功能点 滚动到两天窗体顶部,自动加载历史跟多信息,数据加载的时候,需要有一个loading动画: 发送信息是滚动条自动滑动到窗体底部,并且自己发送的信息出现在聊天窗体中: 收到别人发送信息时,需要判断滚动条处于窗体中的位置,在距离底部一定范围内收到信息需要自动滑动到窗体底部: 收

  • WPF+ASP.NET SignalR实现简易在线聊天功能的示例代码

    目录 涉及知识点 什么是ASP.NET SignalR 在线聊天整体架构 ASP.NET SignalR在线聊天服务端 1. 创建ASP.NET Web API项目 2. 创建消息通知中心Hub 3. 注册服务和路由 4. ASP.NET SignalR中心对象生存周期 SignalR客户端 1. 安装SignalR客户端依赖库 2. 客户端消息接收发送 运行示例 在实际业务中,当后台数据发生变化,客户端能够实时的收到通知,而不是由用户主动的进行页面刷新才能查看,这将是一个非常人性化的设计.有没

  • 基于Java Socket实现一个简易在线聊天功能(一)

    最近做了一个项目,其中有一个在线网页交流的需求,好久没写代码了,手都生疏了,于是先写demo练练手,分享到我们平台,以此做个记录,方便自己和大家使用. 先给大家说下实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持续发送消息: 2.结合线程,实现多客户端连接服务端发送消息: 3.实现服务端转发客户端消息至所有客户端,同时在客户端显示: 4.把awt组件生成的窗口界面改成前端jsp或者html展示的界面,java socket实现的客户端改为前端技术实现. 这里首

  • Java中使用websocket实现在线聊天功能

    很早以前为了快速达到效果,使用轮询实现了在线聊天功能,后来无意接触了socket,关于socket我的理解是进程间通信,首先要有服务器跟客户端,服务的启动监听某ip端口定位该进程,客户端开启socket分配ip端口连接服务端ip端口,于是两个进程间便可以通信了.下面简单画个图理解. but,今天还是准备分享websocket的使用,先上效果,再贴代码. 第一步启动socket服务. 然后连接客户端连接服务器,加入聊天室,分别使用googel(白玉京,沈浪),火狐(楚留香),ie(李寻欢)进行测试

  • vue+django实现一对一聊天功能的实例代码

    vue+django实现一对一聊天和消息推送的功能.主要是通过websocket,由于Django不支持websocket,所以我使用了django-channels.考虑到存储量的问题,我并没有把聊天信息存入数据库,服务端的作用相当于一个中转站.我只讲述实现功能的结构性代码,具体的实现还请大家看源代码. 前端聊天界面 前端消息提醒界面 后端代码 效果展示 实现过程 后端 首先,我们需要先定义websocket的两条连接路径.ws/chat/xxx/(xxx指代聊天组)这条路径是当聊天双方都进入

  • Vue+express+Socket实现聊天功能

    本文实例为大家分享了Vue+express+Socket实现聊天功能的具体代码,供大家参考,具体内容如下 实现聊天功能 具体功能 只是为了实现功能,不对界面进行美化 1.输入消息点击发送所有用户可以在下方收到消息 2.输入userid后点击连接,可以连接对应的聊天,另外一个界面输入刚刚那个页面的userid后再输入内容点击发送给指定的人,则刚才那个页面可以打印输出,而其他页面不会收到,实现私聊的功能 3.没有具体实现私聊的内容显示,但是接收发送消息都可以实现,要实现私聊的内容显示可以再添加一个私

  • Vue+Websocket简单实现聊天功能

    本文实例为大家分享了Vue+Websocket简单实现聊天功能的具体代码,供大家参考,具体内容如下 效果图: 聊天室 此篇文章是针对Websocket的简单了解和应用,利用Nodejs简单搭建一个服务器加以实现. 首先创建一个vue项目 然后再创建一个server文件夹,在终端上打开该文件夹,输入vue init(一直敲 "回车" 键),最后再建一个server.js文件,如下图 代码如下: server.js/ 在server文件终端下 npm install --s ws var

  • 基于PHP实现一个简单的在线聊天功能

    目录 前端页面 数据库 实现思路 1.showPage() 2.newChat() 3.getChatText() 4.getChatTemp() 5.pushChat() 总结 要实现功能,首先要做前端,经过对比其他网站的在线聊天功能,发现除了基本的聊天功能以外,还要注意以下几点. 一次只能和一个人聊天,但是可以随意切换其他人. 如果用户是从"发送消息" 入口进来的,那么当前马上就切换到对应的聊天窗口,而且如果之前有过聊天记录,应该把聊天记录也展示出来. 如果是从"我的消息

  • Java实现在线聊天功能

    本文实例为大家分享了Java实现在线聊天功能的具体代码,供大家参考,具体内容如下 效果 关键代码 创建Client.java import java.io.IOException; import java.net.Socket; import java.net.UnknownHostException; /**  * @author Lete  * @乐特的程序永无BUG  * @createDate 2020- 07-04 22:13  *  * 1. SendThread 发送消息线程  *

  • java基于UDP实现在线聊天功能

    本文实例为大家分享了java基于UDP实现在线聊天的具体代码,供大家参考,具体内容如下 效果图: 一.学习UDP的简单使用步骤 接收端: DatagramSocket socket = null;         try {             //1.开启自己的端口(客户端连接的端口)             socket = new DatagramSocket(9999);             //2.接收数据             byte[] buffer = new byt

随机推荐