最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 微信小程序即时通讯开发记录(结合通讯云IM)

    正文概述 掘金(青庸)   2021-03-14   355

    前言

    最近接到一个需求是在原项目的基础上去开发一个即时通讯的功能,主要是一对一的聊天,供客户和客户之间进行即时通讯,功能主要包括聊天列表、发送文字、发送表情、发送图片、发送视频、发送语音、发送自定义信息等功能。因为项目需求,所以选择了腾讯云IM来开发。

    开发流程

    首先是要注册腾讯云并创建应用,拿到APPID和秘钥。

    文档位置:cloud.tencent.com/document/pr…

    1.安装依赖

    // IM 小程序 SDK
    npm install tim-wx-sdk --save
    // 发送图片、文件等消息需要腾讯云 即时通信 IM 上传插件
    npm install tim-upload-plugin --save
    

    2.在项目脚本里引入模块,并初始化。

    安装完依赖以后在项目目录的utils目录下创建tencentIM目录,用于存放关于即时通讯IM相关的js文件。

    封装event.js

    因为项目中需要一些时间监听,所以需要在utils里面添加一个event类,然后在app.js上面绑定到wx全局对象上。

    // app.js
    import Event from './utils/tencentIM/event'
    wx.event = new Event();
    
    // event.js
    class Event {
      version = "1.0.0";
      constructor() {
        this._events = {};
      }
      on(eventName, listener) {
        if (!eventName || !listener) return;
        // 判断回调的 listener 是否为函数
        if (isValidListener(listener)) {
          throw new TypeError("listener must be a function");
        }
        let events = this._events;
        let listeners = (events[eventName] = events[eventName] || []);
        let listenerIsWrapped = typeof listener === "object";
        // 不重复添加事件,判断是否有一样的
        if (indexOf(listeners, listener) === -1) {
          listeners.push(
            listenerIsWrapped
              ? listeners
              : {
                  listener,
                  once: false,
                }
          );
        }
        return this;
      }
      once(eventName, listener) {
        // 直接调用 on 方法,once 参数传入 true,待执行之后进行 once 处理
        this.on(eventName, {
          listener,
          once: true,
        });
        return this;
      }
      emit(eventName, args) {
        // 直接通过内部对象获取对应自定义事件的回调函数
        let listeners = this._events[eventName];
        if (!listeners) return;
        // 需要考虑多个 listener 的情况
        listeners.forEach((listener) => {
          if (listener) {
            listener.listener.apply(this, args || []);
            if (listener.once) {
              this.off(eventName, listener.listener);
            }
          }
        });
        return this;
      }
      off(eventName, listener) {
        let listeners = this._events[eventName];
        if (!listener) return;
        listeners.some((item, index) => {
          if (item && item.listener === listener) {
            listeners.splice(index, 1);
            return true;
          }
          return false;
        });
      }
      allOff(eventName) {
        if (eventName && this._events[eventName]) {
          this._events[eventName] = [];
        } else {
          this._events = {};
        }
      }
    }
    
    // 判断是否是合法的 listener
    function isValidListener(listener) {
      if (typeof listener === "function") {
        return true;
      } else if (listener && typeof listener === "object") {
        return isValidListener(listener.listener);
      } else {
        return false;
      }
    }
    
    // 判断新增自定义事件是否存在
    function indexOf(array, item) {
      var result = -1;
      item = typeof item === "object" ? item.listener : item;
      for (var i = 0, len = array.length; i < len; i++) {
        if (array[i].listener === item) {
          result = i;
          break;
        }
      }
      return result;
    }
    
    

    服务端开发生成userSig接口或者下载腾讯云提供生成userSig的文件

    相关文档:cloud.tencent.com/document/pr…

    腾讯云提供的客户端生成userSig文件:github.com/tencentyun/…

    初始化tim并设置监听

    import TIM from 'tim-wx-sdk';
    import TIMUploadPlugin from 'tim-upload-plugin';
    let options = {
     SDKAppID: 0 // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID
    };
    // 创建 SDK 实例,`TIM.create()`方法对于同一个 `SDKAppID` 只会返回同一份实例
    let tim = TIM.create(options); // SDK 实例通常用 tim 表示
    // 设置 SDK 日志输出级别,详细分级请参见 <a href="https://imsdk-1252463788.file.myqcloud.com/IM_DOC/Web/SDK.html#setLogLevel">setLogLevel 接口的说明</a>
    tim.setLogLevel(0); // 普通级别,日志量较多,接入时建议使用
    // tim.setLogLevel(1); // release 级别,SDK 输出关键信息,生产环境时建议使用
    // 注册腾讯云即时通信 IM 上传插件
    tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
    // 监听事件,例如:
    tim.on(TIM.EVENT.SDK_READY, function(event) {
    // 收到离线消息和会话列表同步完毕通知,接入侧可以调用 sendMessage 等需要鉴权的接口
    // event.name - TIM.EVENT.SDK_READY
    });
    tim.on(TIM.EVENT.MESSAGE_RECEIVED, function(event) {
    // 收到推送的单聊、群聊、群提示、群系统通知的新消息,可通过遍历 event.data 获取消息列表数据并渲染到页面
    // event.name - TIM.EVENT.MESSAGE_RECEIVED
    // event.data - 存储 Message 对象的数组 - [Message]
    });
    tim.on(TIM.EVENT.MESSAGE_REVOKED, function(event) {
    // 收到消息被撤回的通知
    // event.name - TIM.EVENT.MESSAGE_REVOKED
    // event.data - 存储 Message 对象的数组 - [Message] - 每个 Message 对象的 isRevoked 属性值为 true
    });
    tim.on(TIM.EVENT.MESSAGE_READ_BY_PEER, function(event) {
    // SDK 收到对端已读消息的通知,即已读回执。使用前需要将 SDK 版本升级至 v2.7.0 或以上。仅支持单聊会话。
    // event.name - TIM.EVENT.MESSAGE_READ_BY_PEER
    // event.data - event.data - 存储 Message 对象的数组 - [Message] - 每个 Message 对象的 isPeerRead 属性值为 true
    });
    tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function(event) {
    // 收到会话列表更新通知,可通过遍历 event.data 获取会话列表数据并渲染到页面
    // event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
    // event.data - 存储 Conversation 对象的数组 - [Conversation]
    });
    tim.on(TIM.EVENT.GROUP_LIST_UPDATED, function(event) {
    // 收到群组列表更新通知,可通过遍历 event.data 获取群组列表数据并渲染到页面
    // event.name - TIM.EVENT.GROUP_LIST_UPDATED
    // event.data - 存储 Group 对象的数组 - [Group]
    });
    tim.on(TIM.EVENT.PROFILE_UPDATED, function(event) {
    // 收到自己或好友的资料变更通知
    // event.name - TIM.EVENT.PROFILE_UPDATED
    // event.data - 存储 Profile 对象的数组 - [Profile]
    });
    tim.on(TIM.EVENT.BLACKLIST_UPDATED, function(event) {
    // 收到黑名单列表更新通知
    // event.name - TIM.EVENT.BLACKLIST_UPDATED
    // event.data - 存储 userID 的数组 - [userID]
    });
    tim.on(TIM.EVENT.ERROR, function(event) {
    // 收到 SDK 发生错误通知,可以获取错误码和错误信息
    // event.name - TIM.EVENT.ERROR
    // event.data.code - 错误码
    // event.data.message - 错误信息
    });
    tim.on(TIM.EVENT.SDK_NOT_READY, function(event) {
    // 收到 SDK 进入 not ready 状态通知,此时 SDK 无法正常工作
    // event.name - TIM.EVENT.SDK_NOT_READY
    });
    tim.on(TIM.EVENT.KICKED_OUT, function(event) {
    // 收到被踢下线通知
    // event.name - TIM.EVENT.KICKED_OUT
    // event.data.type - 被踢下线的原因,例如:
    //    - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多实例登录被踢
    //    - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多终端登录被踢
    //    - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 签名过期被踢 (v2.4.0起支持)。 
    });
    tim.on(TIM.EVENT.NET_STATE_CHANGE, function(event) { 
    //  网络状态发生改变(v2.5.0 起支持)。 
    // event.name - TIM.EVENT.NET_STATE_CHANGE 
    // event.data.state 当前网络状态,枚举值及说明如下: 
    //     \- TIM.TYPES.NET_STATE_CONNECTED - 已接入网络 
    //     \- TIM.TYPES.NET_STATE_CONNECTING - 连接中。很可能遇到网络抖动,SDK 在重试。接入侧可根据此状态提示“当前网络不稳定”或“连接中” 
    //    \- TIM.TYPES.NET_STATE_DISCONNECTED - 未接入网络。接入侧可根据此状态提示“当前网络不可用”。SDK 仍会继续重试,若用户网络恢复,SDK 会自动同步消息  
    });
    // 开始登录 
    tim.login({userID: 'your userID', userSig: 'your userSig'});
    

    聊天开发记录点

    (1) 表情开发

    进入www.oicqzone.com/tool/emoji/ 网站,把最后一列的表情下载下来 当然也可以通过下面这种方式把表情一下子copy下来,然后存到一个js里面 命令:Array.from(document.querySelectorAll('body > table > tbody > tr > td:nth-child(8)')).map(item=>item && item.innerText||'') 微信小程序即时通讯开发记录(结合通讯云IM)

    然后导入项目中就可以使用,点击表情的时候将表情直接放到输入框文本后面

      selectEmoji(e){
        const {text} = e.currentTarget.dataset
        this.setData({
          colSendMsg:this.data.colSendMsg + text,
        })
      }
    

    (2)发送语音功能

    发送语音主要是调用 wx.createInnerAudioContext()方法来实现的 主要细节在于录音授权、长按录音,录音时间短于一定时间不予以发送并提示,可以通过上滑的方式取消发送。

    录音授权
      // 点击录音按钮
      record() {
        let that = this
        // canRecord变量是用于保存是否授权录音功能的状态
        if (that.canRecord) {
          // 开始录音
          that.beforeStartRecord()
          return
        }
        wx.authorize({
          scope: 'scope.record',
          success() {
            console.log("录音授权成功");
            that.canRecord = true
            // 用户已经同意小程序使用录音功能
          },
          fail() {
            console.log("第一次录音授权失败");
            wx.showModal({
              title: '提示',
              content: '您未授权录音,功能将无法使用',
              showCancel: true,
              confirmText: "授权",
              confirmColor: "#52a2d8",
              success: function (res) {
                if (res.confirm) {
                  //确认则打开设置页面(重点)
                  wx.openSetting({
                    success: (res) => {
                      console.log(res.authSetting);
                      if (!res.authSetting['scope.record']) {
                        //未设置录音授权
                        wx.showModal({
                          title: '提示',
                          content: '您未授权录音,功能将无法使用',
                          showCancel: false,
                          success: function (res) {},
                        })
                      } else {
                        //第二次才成功授权
                        that.canRecord = true
                        console.log("设置录音授权成功");
                      }
                    },
                    fail: function () {
                      console.log("授权设置录音失败");
                    }
                  })
                } else if (res.cancel) {
                  console.log("cancel");
                }
              },
              fail: function () {
                console.log("openfail");
              }
            })
          }
        })
      },
    

    后面录音相关的流程很多博文上都有,就不再赘述,只提供大家一个思考:

    微信小程序即时通讯开发记录(结合通讯云IM)

    (3)页面滚动到底部

    聊天页面很重要一点是要让用户看到最新的信息,所以要显示页面的最底部内容,我根据以下几种情况显示最底部内容:

    1. 第一次进入页面
    2. 发送任意消息
    3. 监听到最新的消息(这里其实推荐值设置提醒)

    具体部分代码如下:

    // wxml
    <scroll-view class="chat-area" refresher-enabled="{{true}}" refresher-triggered="{{triggered}}"
      bindrefresherrefresh="onRefresh" scroll-into-view="{{ viewIndex }}" scroll-y="true" enable-flex="{{true}}"
      bindtap="onHideSendMore" bindtap="hideBottom" style="padding-bottom:{{InputBottom}}px">
      <!-- <view wx:if="{{showLoading}}" class="cu-load loading"></view> -->
      <view class="cu-chat">
        <view wx:for="{{ arrMsg }}" wx:key="ID">
          // 具体内容
        </view>
      </view>
    </scroll-view>
    
    // js - pageScrollToBottom
      // isBottom 是否滑动到底部
      // lastIndex 滑动到指定位置
      // arrMsg 消息列表
      pageScrollToBottom(isBottom,lastIndex = 0) {
        let index = null
        if (isBottom) {
          index = 'msg-' + (this.data.arrMsg.length -1);
        } else if(lastIndex) {
          index = 'msg-' + lastIndex;
        }else{
          index = 'msg-' + 0;
        }
        this.setData({
          viewIndex: index
        })
      },
    

    (4)底部弹起高度

    我们在开发的时候常常需要底部弹起,还有键盘弹起的操作,这里也需要优化一下用户的体验。

    1. 弹起的时候让聊天记录也向上推起
    2. 保持其他部门弹起的高度和键盘弹起高度一样(这样就不会有过多的切换跳动效果)

    我使用style="padding-bottom:{{InputBottom}}px"的方式,inputBottom默认我设置了200,然后在键盘弹起的时候记录键盘弹起的高度,然后保存键盘弹起的高度,下次使用这个保存的高度

    // wxml
    <input catchblur="InputBlur"></input>
    // js
    InputFocus(e) {
        let height = e.detail.height
        if(height > 0) this.InputBottom = height
        this.setData({
          InputBottom: height
        })
    }
    

    微信小程序即时通讯开发记录(结合通讯云IM)

    尾声

    本文只是对我开发这个项目的时候一些重要的地方的一个记录,供大家参考和以后个人的回顾,如果有不足的地方,还请大家多多指点。


    起源地下载网 » 微信小程序即时通讯开发记录(结合通讯云IM)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元