前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >uni-app+php+workman实现简单聊天功能之聊天模块封装

uni-app+php+workman实现简单聊天功能之聊天模块封装

作者头像
切图仔
发布2022-09-08 16:32:01
4.5K1
发布2022-09-08 16:32:01
举报
文章被收录于专栏:生如夏花绚烂

前面介绍了Laravel中Websocket基本使用(Workerman) 接下来利用uni-app+laravel+workman实现一个简单的聊天功能。 聊天功能主要涉及到以下场景

场景一

双方都处于聊天界面

这个时候我们要

  1. 将聊天数据渲染到页面
  2. 将产生的聊天数据放到本地存储用于历史记录等

2.1存储当前聊天数据(直接存储 key=chatdetail_当前用户id_聊天对象id

2.2存储当前聊天列表 (key=chatlist_当前用户id) 将当前聊天会话在消息列表置顶,更新最后一条消息,更新时间

如下图

场景二

当前用户不处于聊天界面如: 用户在其他页面或者当前用户正在与其他用户聊天,此时接受到消息 这个时候我们要

  1. 将消息渲染到聊天列表,展示最后一条消息,消息数量,时间等
  2. 将聊天数据放到本地存储

2.1存储聊天数据(直接存储 key=chatdetail_当前用户id_发送消息的用户id

2.2存储当前聊天列表 (key=chatlist_当前用户id) 将接受到的消息在消息列表置顶,更新最后一条消息,更新时间,更新tabbat角标展示

发送消息

  1. 将聊天数据存储到本地存储

1.1存储聊天数据(直接存储 key=chatdetail_当前用户id_聊天对象id

1.2存储当前聊天列表 (key=chatlist_当前用户id) 将当前会话置顶在消息列表置顶,更新最后一条消息,更新时间 1.3请求ajax发送消息

1.4渲染到页面

读取消息

  1. 写入本地存储 chatlist_当前用户id:获取将当前会话的未读书清零,相应的减少总未读书(总未读书展示在tabbar)渲染tabbar

可以看到有很多类似的操作,我们可以将其封装成一个聊天对象。 首先新键配置文件 config.js

代码语言:javascript
复制
export default{
    //api请求前缀
    webUrl:'http://social.zihanzy.com/api/v1/',
    //websocket
    websocket:'wss://social.zihanzy.com/wss',
    //展示未读信息的tabbar
    TabbarIndex:2
}

新键chat.js(聊天相关操作)

代码语言:javascript
复制
 import Config from './config.js';
 import User from './user.js';
 export default{
     //socket地址
      url:Config.websocket,
      //连接状态
      IsOpen:false,
      //SocketTask
      SocketTask:false,
      //是否上线(会员id绑定客户端id 验证用户身份 通过则绑定)
      IsOnline:false,
      //当前聊天对象(进入聊天页面获取)
      CurrentToUser:{
          userid:0,//判断userid是否为0,当前用户处于什么场景下
          username:"",
          userpic:""
      },
      //连接
      Open(){
          if(this.IsOpen)return;//防止重复连接
          //连接
          this.SocketTask = uni.connectSocket({
              url:this.url,
              complete:(e)=>{}
          });
          if(!this.SocketTask)return;
          //监听开启
          this.SocketTask.onOpen(()=>{
              //连接成功将连接状态设置为已连接
              this.IsOpen = true;
          });
          //监听消息
          this.Message();
          //监听关闭
          this.SocketTask.onClose(()=>{
              this.IsOpen = false;
            this.SocketTask = false;
          });
          //监听错误
          this.SocketTask.onError((e)=>{
              this.IsOpen=false;
            this.SocketTask = false;
          })
      },
        ...

1.我们初始化相关配置,引入相关文件,编写了websocket连接逻辑 当连接成功后我们会将状态设置为已连接。

代码语言:javascript
复制
...
 //用户绑定
async UserBind(client_id){
let res = await this.$.post('chat/bind',{
    type:'bind',
    client_id:client_id,
},{
    header: {
        'Accept':'application/json',
        'Authorization':'Bearer '+uni.getStorageSync('access_token')
    },
})
console.log(res)
//错判断
 //成功处理
 return this.resultUserBind(res.data)
},
...

2.用户绑定将用户的token和消息类型发送给服务端,服务端会对用户进行验证在执行用户是否上线操作

代码语言:javascript
复制
 //监听信息
Message(){
    //接受服务器消息
    this.SocketTask.onMessage((e)=>{
    //字符串转换json
    let res = JSON.parse(e.data);
    /*
    {
        type:"bind",
        status:true//绑定成功
    }
    */
    //绑定返回结束
if(res.type=='bind')return this.UserBind(res);//服务器返回client-id下一步执行用户绑定
    if(res.type!=='text') return;//接受的信息不是信息就return
    //全局通知接口
    uni.$emit('UserChat',res);
    //存储到chatdetail(与某用户的聊天记录)
    this.__UpdateChatdetail(res);//(默认接受信息)
    //更新chatlist(当前会话置顶,修改chatlist中当前会话的data和time显示
    this.__UpdateChatlist(res);
    //总未读数+1 修改tabbar信息数
    //当前聊天对象与from_id不同 未读数加1
    //只要当前用户与某一用户没有处于聊天界面时执行未读书+1
    if(this.CurrentToUser.userid!==res.from_id){
        this.__UpdateNoReadNum({type:"add"})
    }

 })
     console.log('监听信息')
},

3.当发送给服务器消息后我们就要接受服务器给我们的响应

  1. 将服务器给我们的响应进行编码
  2. 判断服务器的响应类型,如果是bind(绑定),我们会调用用户绑定函数(UserBind)函数
  3. 如果接受的响应类型是文本

3.1 全局通知接口,使每个页面都能接受到信息

3.2 将聊天记录存储到本地存储,调用(__UpdateChatdetail)默认是接受信息(发送消息也会调用该函数)

3.3更新消息列表,将当前会话置顶,修改chatlist中当前会话的data和time显示,调用(__UpdateChatlist)

3.4使总未读消息数+1,调用(__UpdateNoReadNum)

接下来分别介绍resultUserBind__UpdateChatdetail__UpdateChatlist__UpdateNoReadNum

Message > resultUserBind

代码语言:javascript
复制
...
//用户绑定结果
resultUserBind(res){
    if(res.status && res.type=='bind'){
        //该为上线状态
        this.IsOnline = true;
        //获取总未读数,并且渲染到tabbar的badge
        this.initTabbarBadge();
        //获取未读信息
        // this.getChatMessages();
        return;
    }
    //绑定失败,断开连接
    uni.showToast({
        title:res.msg,
    icon:"none"
    })
    this.SocketTask.close();
},\
...

该函数获取服务器的用户绑定结果

  1. 如果用户状态正常则使用户上线
  2. 初始化tabbar的未读总信息角标initTabbarBadge
  3. 获取未读的信息getChatMessages
  4. 绑定失败断开连接,并展示相关提示

Message > resultUserBind > initTabbarBadge

代码语言:javascript
复制
//初始化tabber信息
    initTabbarBadge(){
        //获取总未读数
        console.log('初始化tabber未读数');
        let noreadnum = uni.getStorageSync('noreadnum'+User.userinfo.id);
        if(noreadnum && noreadnum>0){
            //设置tabber角标
            return uni.setTabBarBadge({
                index:Config.TabbarIndex,
            text:noreadnum>99?'99+':noreadnum.toString()
            });
        }
        return uni.removeTabBarBadge({
            index:Config.TabbarIndex
        })
    },

该函数用于初始化tabbar信息 获取总未读数 当未读数>0时渲染tabbar 当未读书=0时 清除tabbar的角标

Message > resultUserBind > getChatMessages

代码语言:javascript
复制
...
getChatMessages(){
    console.log('test未读')
    $.post('chat/get',{},{
        header: {
            'Accept':'application/json',
            'Authorization':'Bearer '+uni.getStorageSync('access_token')
        }
    }).then(datas=>{
        let data = datas.data.data
    if(data.length>0){
        for(let i = 0;i<data.length;i++){
             let msg = data[i]
             uni.$emit('UserChat',msg);
             //存储到chatdetail(与某用户的聊天记录)
             this.__UpdateChatdetail(msg);//(默认接受信息)
             //更新chatlist(当前会话置顶,修改chatlist中当前会话的msg和time显示
             this.__UpdateChatlist(msg);
             //总未读数+1 修改tabbar信息数
             //当前聊天对象与from_id不同 未读数加1
             //    //只要当前用户与某一用户没有处于聊天界面时执行未读书+1
             if(this.CurrentToUser.userid!==msg.from_id){
                this.__UpdateNoReadNum({type:"add"})
             }
        }
    }
    })
},
        ...

该函数用于获取未读信息,当用户离线时接受到的消息暂存在服务端缓存,当用户重新上线时触发该函数获取到未读消息并广播事件UserChat使页面获取到未读消息

Message > __UpdateChatdetail

代码语言:javascript
复制
//存储到chatdetail(与某用户的聊天记录)
__UpdateChatdetail(res,issend = false){
    /*发送和接受都要调用函数存储,
    1.判断是不是发送 发送 issend=true 接受 issend=false (默认接受信息)
    如果是发送userid = 当前要发送用户id
    如果是接受userid = 发送消息的用户id
    */
    let userid = issend ? this.CurrentToUser.userid : res.from_id;
    //获取旧数据
    let list = uni.getStorageSync('chatdetail_'+User.userinfo.id+'_'+userid);//与某用户的聊天记录
    list = list ? JSON.parse(list):[];
    //追加
    list.push(this.__format(res,{type:"chatdetail",isme:issend,olddata:list}));
    //存储
    uni.setStorage({
        key:'chatdetail_'+User.userinfo.id+'_'+userid,
    data:JSON.stringify(list)
    })

},

该函数存储当前用户与某位用户的聊天记录,这里分两种情况接受消息和发送消息都要将消息存储,在Message函数体里面我们默认是接受消息

  1. 获取原来的消息历史记录
  2. 对消息历史记录进行追加,调用了函数__format进行数据格式化
  3. 将追加后的数据进行本地存储

Message > __UpdateChatdetail > __format 该函数用于数据的格式化,存储聊天记录和消息列表都会用到此函数

代码语言:javascript
复制
  __format(data,options={}){
    /**
     * options={
         type:"chatdetail",//转换类型
         olddata:olddata,//旧数据(chatdetail中必填)
         isme:true //(true本人,false聊天对象,chatdetail中必填)
     }
     */
     switch(options.type){        
         case "chatdetail"://聊天详情记录
         let list = options.olddata; //旧数据
         let chattime = new Date().getTime();//获取当前时间
         let length = list.length;
         return {
            isme:options.isme,
            userpic:options.isme ? User.userinfo.userpic : data.from_userpic,
            type:data.type,
            data:data.data,
            time:chattime,
            gstime:chattime,
            //Time.gettime.getChatTime(chattime,(length)>0)?list[length-1].time:0)

        };
        break;

isme主要用于消息渲染的时候将哪个用户展示在右边如果(isme=true)那么这条消息应该展示在右边

Message >** UpdateChatlist**

代码语言:javascript
复制
 //更新chatlist (将当前会话置顶,修改chatlist中当前会话的data和time显示)
__UpdateChatlist(res){
    //获取旧数据
    let chatlist = uni.getStorageSync('chatlist'+User.userinfo.id);
    chatlist = chatlist ? JSON.parse(chatlist):[];
    //判断是否已经存在该会话 ,存在:将当前会话置顶,不存在;追加至头部
    let index = chatlist.findIndex((item)=>{
        //发送和接受消息的用户id
        return item.userid == res.to_id || item.userid == res.from_id;

    })
    //不存在
    if(index == -1){
     let obj = this.__format(res,{type:"chatlist"}); 
     //忽略本人发送
     if(res.from_id!==User.userinfo.id){
         obj.noreadnum=1;
     }
     chatlist.unshift(obj); 
    }else{
        //存在 将当前会话置顶,修改chatlist中当前会话的data和time显示
        chatlist[index].data = res.data;
        chatlist[index].type = res.type;
        chatlist[index].time = res.time;
        //当前聊天对象不是该id ,未读书加1(排除本人发送消息)
        if(res.from_id !== User.userinfo.id && this.CurrentToUser.userid!==chatlist[index].userid){
            chatlist[index].noreadnum++;//不处在聊天对象中
        }
        //置顶当前会话
        chatlist = this.__toFirst(chatlist,index);
    }
    //存储到本地
    uni.setStorage({
        key:'chatlist'+User.userinfo.id,
    data:JSON.stringify(chatlist)
    })
},

该函数用于存储消息列表,并进行消息列表内容的更新、时间更新、最新消息置顶等 包含以下步骤

  1. 获取之前的消息列表
  2. 对之前的消息列表进行判断,如果存在则置顶,调用__toFirst函数
  3. 不存在则调用__format函数进行数据格式化并将数据存储到消息列表数组头部
  4. 将列表存储到本地存储

Message > __UpdateChatlist > __format

代码语言:javascript
复制
switch(options,type){
     ...
     case "chatlist"://新增会话
     let obj = {
         userid:data.from_id,
         userpic:data.from_userpic,
         username:data.from_username,
         time:data.time,//最新消息时间戳
         data:data.data,
         noreadnum:0 //未读数
     };
     //本人发送的消息
     if(data.from_id == User.userinfo.id){
         obj.userid = this.CurrentToUser.userid;
         obj.userpic = this.CurrentToUser.userpic;
         obj.username = this.CurrentToUser.username;
     }
     return obj;
     break;
     ...

Message > __UpdateChatlist > __toFirst

代码语言:javascript
复制
//数组置顶
__toFirst(arr,index){
    if(index!=0){
        arr.unshift(arr.splice(index,1)[0]);
    }
    return arr;
}

Message > __UpdateNoReadNum

代码语言:javascript
复制
__UpdateNoReadNum(options = {}){
    //获取总未读数
    let noreadnum = uni.getStorageSync('noreadnum'+User.userinfo.id);
    noreadnum = noreadnum || 0;
    //接受信息增加
    if(options.type == 'add'){
        noreadnum++;
        //响铃振动提示
        this.__Nofify();
    }else{
        noreadnum-=options.num;//读取信息减少 
    }
    noreadnum = noreadnum > 0 ? noreadnum : 0;
    //修改tabbar信息数
    this.__UpdateTabbarBadege(noreadnum);
    //存储
    uni.setStorage({
        key:'noreadnum'+User.userinfo.id,
    data:noreadnum
    })
},

该函数用于计算总的消息未读数,有消息时进行响铃提示(__Nofify),渲染tabbar等(__UpdateTabbarBadege),比对最新的数据进行存储

Message > __UpdateNoReadNum > __Nofify

代码语言:javascript
复制
//消息提示
__Nofify(){
uni.vibrateLong()  
},

Message > __UpdateNoReadNum > __UpdateTabbarBadege

代码语言:javascript
复制
__UpdateTabbarBadege(num){
    if(num && num > 0){
        return uni.setTabBarBadge({
            index:Config.TabbarIndex,
        text:num > 99 ? '99+':num.toString()
        });
    }
    return uni.removeTabBarBadge({
        index:Config.TabbarIndex
    })
}

该函数将未读数渲染到tabbar

Message函数封装完了,接下来封装发送消息函数

Send

代码语言:javascript
复制
//发送消息
send(data){
    //发送的格式
    let senddata = this.__format(data,{type:"send"});
    //存储到chatdetail
    this.__UpdateChatdetail(senddata,true);
    //存储到chatlist(将当前会话置顶,修改时间内容)
    this.__UpdateChatlist(senddata);
    //发送服务器(交由页面做)
    return senddata;
},

该函数处理发送消息相关逻辑

  1. 数据格式化,调用__format函数
  2. 将消息存储到本地存储调用 __UpdateChatdetail函数,可参照前面的
  3. 将消息存储到chatlist调用__UpdateChatlist,参照前面
  4. 返回聊天数据,在页面进行ajax请求

Send > __format

代码语言:javascript
复制
...
case "send":
    return {
        to_id:this.CurrentToUser.userid,
        from_id:User.userinfo.id,
        from_username:User.userinfo.username,
        from_userpic:User.userinfo.userpic,
        type:data.type,
        time:new Date().getTime()
    }
    break;
 }
},

接收消息函数 Read

代码语言:javascript
复制
//读取当前会话
Read(item){
    if(!item.noreadnum)return;
    let chatlist = uni.getStorageSync('chatlist'+User.userinfo.id);
    chatlist = chatlist ? JSON.parse(chatlist):[];
    //拿到当前会话
    let index = chatlist.findIndex((value)=>{
        return value.userid == item.userid;
    });
    let oldnoreadnum = chatlist[index].noreadnum
    //会话存在 未读消息=0
    if(index!==-1){
        chatlist[index].noreadnum = 0;
        //存储
        uni.setStorage({
            key:"chatlist"+User.userinfo.id,
        data:JSON.stringify(chatlist)
        })
        //更新tabber的角标
        this.__UpdateNoReadNum({type:"read",num:item.noreadnum});
    }
},

该函数用于读取消息,主要包含以下

  1. 获取旧数据
  2. 如果该会话存在则使为读消息数清零,更新消息列表
  3. 重新渲染tabbar

到此chat对象封装完成,移步【聊天实现】

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-08-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档