精华内容
下载资源
问答
  • 微信小程序即时通讯 开发这个项目付出了我很多心血,如果对你有帮助和启发的话,希望在GitHub上给个star!也是对我工作的支持和肯定! 也非常感谢对项目中文本超长溢出布局的问题的修正和提交! 介绍: wechat-im是...
  • 微信小程序即时通讯(融云sdk)

    千次阅读 2020-10-23 12:01:31
    小程序聊天中加载的资源更多的是网络资源(七牛云上的外链地址) app聊天加载的更多的是类似base64的资源 融云那边有提供比较完整的案例 但是是分开的一个是IM聊天室案例 一个是calllib音视频案例 这两个案例无法...

    we

    记录一下我在使用融云sdk中遇到的问题
    使用前要去融云那边注册账号申请appid 然后开通小程序服务,就可以下载小程序案例了
    融云的sdk中的
    小程序聊天中加载的资源更多的是网络资源(七牛云上的外链地址)
    app聊天加载的更多的是类似base64的资源

    融云那边有提供比较完整的案例
    但是是分开的一个是IM聊天室案例
    一个是calllib音视频案例 这两个案例无法同时使用
    因为sdk的版本不同会导致冲突(官方说是兼容性还没有做好)
    建议使用sdk2版本,这个版本两个都可以使用
    1、初始化(这个问题是我最后才解决掉的,一定要初始化好,不然就算能用也会有很多问题)
    2、实时获取消息加载到自己的聊天列表中
    3、解决首次登录的初始化问题还有第二次登录的初始化问题
    (这个问题比较有意思,因为我当时是分开处理化的,先初始化IM聊天室,在链接上之后再次获取那边的token去初始化音视频的sdk,反正就不对的,要一起初始化)
    4、在小程序开始运行并且链接上融云那边的服务会后设置全局监听(可以让不管处于哪一个界面都可以,收到消息通知,当然仅限于小程序内,就是小程序处于show状态的时候,切换到后台就收不到了,因为小程序不支持后台强制唤醒,这里的话可以去关联公众号,通过让用户在公众号上绑定用户信息,获取唯一标识,去实现离线推送,关于公众号授权的内容可以参考这个链接),具体关联上怎么推送的话,要去公众平台里申请和设置消息模板,具体咋推送的就要靠后端勒
    5、接下来就是判断被聊天的用户是否在线,是否需要被推送
    6、现在就是打音视频的界面需要判断是接听方还是呼叫方去通过不同的字段去展示不同的界面,当然聊天室也要判断是当前用户还是对方用户去显示到两侧
    7、我们也尝试过微信小程序消息推送(结果就是能退过来,但是次数限制是硬伤,订一次推送一次,不满足需求)
    8、在聊天室中发送定位信息时,要使用wx.getLocation和 wx.chooseLocation去选择地址具体的地址表述图片可以使用高德那边的api把经纬度传过去然后返回地址描述图,前端没办法单独处理,我是没有想到办法
    参考这个链接去高德api看 https://restapi.amap.com/v3/staticmap?markers=-1,http://www.5imoban.net/view/demoimg/jrzb_position_icon.png,0:${longitude},${latitude}&key=ee95e52bf08006f63fd29bcfbcf21df0&zoom=17&size=360*200&location=${longitude},${latitude}
    9、设置用户聊天的时间展示,当然写数据都融云那边的接口给我们的,给我们的是毫秒,不是给用户看的东西,所有要前台处理一下,但使用js对每一条数据都处理总感觉很慢,wxml行间也不允许处理过的内容,所以我使用wxs

    var filter = {
      getDateTime: function (value) {
        //不能使用 new Date()
          var now = getDate();
          var time = getDate(value);
          var year = time.getFullYear();
          var month = time.getMonth() + 1;
          var date = time.getDate();
          var hour = time.getHours();
          var minute = time.getMinutes();
          var second = time.getSeconds();
          month = month < 10 ? "0" + month : month;
          date = date < 10 ? "0" + date : date;
          hour = hour < 10 ? "0" + hour : hour;
          minute = minute < 10 ? "0" + minute : minute;
          second = second < 10 ? "0" + second : second;
          if(now.getFullYear() == year){
            if(now.getDate() == date){
              return hour + ":" + minute + ":" + second;
            }else{
              return  month + "-" + date + " " + hour + ":" + minute + ":" + second;
            }
          }else{
            return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second;
          }
      }
    }
    // 导出对外暴露的属性
    module.exports = {
      getDateTime: filter.getDateTime
    }
    

    具体使用

    <wxs module="filter" src="./message.wxs"></wxs>
    <view style='margin:16rpx 0'>{{filter.getDateTime(message.sentTime)}}</view>
    

    10、 确保每次用户进入到聊天界面时,界面要定位到最后一条信息哪里(因为聊天信息一般都比较多会有滚动条的存在,所以要让滚动条在最后一天数据哪里,这个时候你可能会觉得直接滚到底部就可以了,但是当时图片消息时内容高度较高就显得不合适了)
    这里使用小程序那边的scroll-view组件 这个组件有一个scroll-into-view属性可以让滚动条滚动到指定位置,具体操作就是通过获取到消息监听器监听来的数据的唯一标识去修改那个属性的值去实现的
    11、还有监听挂断时的重复触发回调的问题

    const gotoView = (context,first) => {
      context.setData({
        toView: context.data.messageList[context.data.messageList.length - 1].messageUId
      })
     
    }
    Page({
    	data:{
    		toView:null, // 用来定位最后一天的消息的标识
    		messageList:[] // 当前的聊天记录
    	},
    	收到消息后触发的方法:funtion(){
    		...这是处理messageList的内容
    		gotoView(this)
    	},
    	我发送某种消息:funtion(){
    		...这是处理messageList的内容
    		gotoView(this)
    	}
    })
    

    12、视频通话时切换前后摄像头

    this.livePusher = wx.createLivePusherContext()
     this.livePusher.switchCamera({})
    

    以上是我遇到的部分问题

    这是我关于初始化和设置的一些方法,为了有更好的拓展性,我写在小程序的app.js中方便全局使用

    具体的sdk版本可以参考我的版本去下载

    const RongIMLib = require('./libs/RongIMLib.wx-1.2.4.js');
    const RongRTC = require('./libs//RongRTC-wechat-minip-3.2.2.js');
    const RongCallLib = require('./libs//RongCallLib-wechat-minip-3.2.2.js');
    import {
      request
    } from './request/index'
    // const { co } = require('./helper');
    var commandWatcher = function (result) {
      console.log("----------------", result, result.messageType, result.messageType == "InviteMessage")
      if (result.messageType == "InviteMessage") {
        wx.navigateTo({
          url: '/pages/call/call?data=' + JSON.stringify(result)
        })
      }
    }
    // 
    function appletOnLine(type){
      request({
          url:'login/appletOnLine',
          data:{
            token:wx.getStorageSync('token'),
            applet_type:type
          }
      }).then(res => {
        console.log(res)
      })
    }
    const {
      appkey,
      msUrls,
      apps,
      serverUrl
    } = require('./config');
    var layout = function () {
      if (userListModle) {
        userListModle.setData({
          conversationList: []
        })
      }
    }
    var appType = (id,content) => {
      request({
        url: "wechat/selectExpertType",
        data: {
          id
        }
      }).then((res) => {
        console.log(res)
        if (res.data.data.account !== null && (res.data.data.account.applet_type === 0 || res.data.data.account.applet_type === null)) {
          request({
            url: "wechat/sendMessage",
            data: {
              id: id,
              name:wx.getStorageSync('name'),
              mobile: wx.getStorageSync('phone'),
              content
            }
          }).then((res) => {})
        }
      })
    
    }
    let RongIMClient = null;
    let userListModle = null;
    let ChatRoom = null;
    let reconnect = () => {
      var callback = {
        onSuccess: function (userId) {
          console.log('Reconnect successfully. ' + userId);
        },
        onTokenIncorrect: async function () {
          let result
          console.log('token无效');
          wx.showToast({
            title: '登录过期,请重新登录',
            icon: "none"
          })
          wx.clearStorageSync()
        },
        onError: function (errorCode) {
          console.log(errorcode);
        }
      };
      var config = {
        // 默认 false, true 启用自动重连,启用则为必选参数
        auto: true,
        // 网络嗅探地址 [http(s)://]cdn.ronghub.com/RongIMLib-2.2.6.min.js 可选
        url: 'cdn.ronghub.com/RongIMLib-2.2.6.min.js',
        // 重试频率 [100, 1000, 3000, 6000, 10000, 18000] 单位为毫秒,可选
        rate: [100, 1000, 3000, 6000, 10000]
      };
      RongIMClient.reconnect(callback, config);
    }
    var getList = (context) => {
      return new Promise((r, j) => {
        RongIMClient.getInstance().getConversationList({
          onSuccess: function (list) {
            request({
              url: 'index/communicationList',
              data: {
                token: wx.getStorageSync("token"),
                pageNo: context.data.pageNo,
                pageSize: context.data.pageSize,
                name: context.data.name
              }
            }).then(res => {
              if (res.data.status == 1) {
                let ryData;
                let my = [];
                res.data.page.list.forEach(ele => {
                  my.push(ele.user_id)
                })
                console.log(my)
                ryData = list.filter(e => {
                  if (e.targetId != wx.getStorageSync('userId')) {
                    return my.includes(parseInt(e.targetId))
                  }
                })
                ryData.forEach(e => {
                  e.zj = res.data.page.list.filter((element) => {
                    return element.user_id == e.targetId
                  })[0]
                })
                r(ryData)
              }
            })
          },
          onError: function (error) {
            j(error)
            getList(context)
          }
        }, null)
      })
    }
    var getHistory = (targetId, timestrap, count) => {
      console.log(targetId, timestrap, count)
      console.log(RongIMLib.ConversationType)
      var conversationType = RongIMLib.ConversationType.PRIVATE; //单聊, 其他会话选择相应的消息类型即可
      var targetId = targetId; // 想获取自己和谁的历史消息,targetId 赋值为对方的 userId。类型: string
      var timestrap = timestrap; // 默认传 null,若从头开始获取历史消息,请赋值为 0, timestrap = 0;
      var count = Number(count); // 每次获取的历史消息条数,范围 0-20 条,可以多次获取
      if (count == 0) {
        count = 8
      }
      return new Promise((r, j) => {
        RongIMLib.RongIMClient.getInstance().getHistoryMessages(conversationType, targetId, timestrap, count, {
          onSuccess: function (list, hasMsg) {
            // console.log(list, hasMsg)
            r({
              list,
              hasMsg
            })
            // list => Message 数组。
            // hasMsg => 是否还有历史消息可以获取。
          },
          onError: function (error) {
            console.log('GetHistoryMessages, errorcode:' + error);
            getHistory(targetId, timestrap, count)
            j(error)
          }
        });
      })
    
    }
    var ImageMessage = (targetId, base64Str, imageUri, extra) => {
      console.log(extra)
      return new Promise((r, j) => {
        extra = {
          ...extra,
          id1: wx.getStorageSync('userId'),
          id2: ChatRoom.data.targetId,
          xm1: ChatRoom.data.my.name,
          xm2: ChatRoom.data.your.name,
          tx1: ChatRoom.data.my.headPortrait,
          tx2: ChatRoom.data.your.headPortrait,
        }
        var msg = new RongIMLib.ImageMessage({
          content: base64Str,
          imageUri: imageUri,
          extra
        });
        var conversationType = RongIMLib.ConversationType.PRIVATE; // 单聊, 其他会话选择相应的会话类型即可
        targetId = String(targetId); // 目标 Id
        RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
          onSuccess: function (message) {
            // message 为发送的消息对象并且包含服务器返回的消息唯一 Id 和发送消息时间戳
            if (message.senderUserId == message.targetId) {
              message.direction = 'receiver'
            } else {
              message.direction = 'sender'
            }
            console.log(message)
            appType(targetId,'您有一条图片消息')
            r(message)
            console.log('Send successfully');
          },
          onError: function (errorCode, message) {
            j(message)
            console.log('发送失败:' + message + errorCode);
          }
        });
      })
    }
    var LocationMessage = (targetId, latitude, longitude, poi, content) => {
      let extra = {
        id1: wx.getStorageSync('userId'),
        id2: ChatRoom.data.targetId,
        xm1: ChatRoom.data.my.name,
        xm2: ChatRoom.data.your.name,
        tx1: ChatRoom.data.my.headPortrait,
        tx2: ChatRoom.data.your.headPortrait,
      }
    
      return new Promise((r, j) => {
        var msg = new RongIMLib.LocationMessage({
          latitude: latitude,
          longitude: longitude,
          poi: poi,
          content: content,
          extra
        });
    
        var conversationType = RongIMLib.ConversationType.PRIVATE; // 单聊, 其他会话选择相应的会话类型即可
        targetId = String(targetId); // 目标 Id
        RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
          onSuccess: function (message) {
            // message 为发送的消息对象并且包含服务器返回的消息唯一 Id 和发送消息时间戳
            console.log(message, 'Send successfully');
            if (message.senderUserId == message.targetId) {
              message.direction = 'receiver'
            } else {
              message.direction = 'sender'
            }
            appType(targetId,'您有一条地理定位消息')
            r(message)
          },
          onError: function (errorCode, message) {
            var info = '';
            j(message)
            switch (errorCode) {
              case RongIMLib.ErrorCode.TIMEOUT:
                info = '超时';
                break;
              case RongIMLib.ErrorCode.UNKNOWN:
                info = '未知错误';
                break;
              case RongIMLib.ErrorCode.REJECTED_BY_BLACKLIST:
                info = '在黑名单中,无法向对方发送消息';
                break;
              case RongIMLib.ErrorCode.NOT_IN_DISCUSSION:
                info = '不在讨论组中';
                break;
              case RongIMLib.ErrorCode.NOT_IN_GROUP:
                info = '不在群组中';
                break;
              case RongIMLib.ErrorCode.NOT_IN_CHATROOM:
                info = '不在聊天室中';
                break;
            }
            console.log('发送失败:' + info + errorCode);
          }
        });
      })
    
    }
    var RegisterMessage = (targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra, ) => {
      return new Promise((r, j) => {
    
        extra = {
          ...extra,
          id1: wx.getStorageSync('userId'),
          id2: ChatRoom.data.targetId,
          xm1: ChatRoom.data.my.name,
          xm2: ChatRoom.data.your.name,
          tx1: ChatRoom.data.my.headPortrait,
          tx2: ChatRoom.data.your.headPortrait,
        }
        let content = mediaUrl;
        var messageName = 'VoiceMessage'; // 消息名称
        var objectName = 'RC:VcMsg'; // 消息内置名称,请按照此格式命名
        var isCounted = true; // 消息计数
        var isPersited = true; // 消息保存
        var mesasgeTag = new RongIMLib.MessageTag(isCounted, isPersited); // 消息是否保存是否计数,true true 计数且保存,false false 不计数不保存
        var prototypes = ['content', 'duration', 'extra']; // 消息类中的属性名
        RongIMClient.registerMessageType(messageName, objectName, mesasgeTag, prototypes);
        var conversationType = RongIMLib.ConversationType.PRIVATE; //单聊, 其他会话选择相应的会话类型即可
        targetId = String(targetId); // 想获取自己和谁的历史消息,targetId 赋值为对方的 Id
        var msg = new RongIMClient.RegisterMessage.VoiceMessage({
          content,
          extra,
          duration
        });
        RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
          onSuccess: function (message) {
            console.log(message)
            if (message.senderUserId == message.targetId) {
              message.direction = 'receiver'
            } else {
              message.direction = 'sender'
            }
            appType(targetId,'您收到一条语音消息')
            r(message)
            console.log('发送自定义消息成功');
          },
          onError: function (errorCode) {
            j(errorCode)
            console.log('发送自定义消息失败');
          }
        });
      })
    
    }
    var RegisterMessage1 = (targetId, name, sightUrl, fileUrl, fileType, fileSize, duration, extra, ) => {
      return new Promise((r, j) => {
    
        extra = {
          ...extra,
          id1: wx.getStorageSync('userId'),
          id2: ChatRoom.data.targetId,
          xm1: ChatRoom.data.my.name,
          xm2: ChatRoom.data.your.name,
          tx1: ChatRoom.data.my.headPortrait,
          tx2: ChatRoom.data.your.headPortrait,
        }
        // let content = {
        //   name,
        //   mediaUrl,
        //   fileUrl,
        //   fileType,
        //   fileSize,
        //   duration
        // }
        let content = {
          content: sightUrl,
          sightUrl,
          duration
        };
        var messageName = 'SightMessage'; // 消息名称
        var objectName = 'RC:SightMsg'; // 消息内置名称,请按照此格式命名
        var isCounted = true; // 消息计数
        var isPersited = true; // 消息保存
        var mesasgeTag = new RongIMLib.MessageTag(isCounted, isPersited); // 消息是否保存是否计数,true true 计数且保存,false false 不计数不保存
        var prototypes = ['message', 'duration', 'extra']; // 消息类中的属性名
        RongIMClient.registerMessageType(messageName, objectName, mesasgeTag, prototypes);
        var conversationType = RongIMLib.ConversationType.PRIVATE; //单聊, 其他会话选择相应的会话类型即可
        targetId = String(targetId); // 想获取自己和谁的历史消息,targetId 赋值为对方的 Id
        var msg = new RongIMClient.RegisterMessage.SightMessage({
          message: {
            content
          },
          extra,
          duration
        });
        RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
          onSuccess: function (message) {
            console.log(message)
            if (message.senderUserId == message.targetId) {
              message.direction = 'receiver'
            } else {
              message.direction = 'sender'
            }
            appType(targetId,'您有一条视频消息')
            r(message)
            console.log('发送自定义消息成功');
          },
          onError: function (errorCode) {
            j(errorCode)
            console.log('发送自定义消息失败');
          }
        });
      })
    
    }
    var TextMessage = (targetId, content, extra) => {
      return new Promise((r, j) => {
        extra = {
          ...extra,
          id1: wx.getStorageSync('userId'),
          id2: ChatRoom.data.targetId,
          xm1: ChatRoom.data.my.name,
          xm2: ChatRoom.data.your.name,
          tx1: ChatRoom.data.my.headPortrait,
          tx2: ChatRoom.data.your.headPortrait,
        }
        var msg = new RongIMLib.TextMessage({
          content,
          extra
        });
        // request({
        //   url: 'rongyun/pushWx',
        //   data: {
        //     token:wx.getStorageSync("token"),
        //     thing2:content
    
        //   }
        // })
        var conversationType = RongIMLib.ConversationType.PRIVATE; // 单聊, 其他会话选择相应的会话类型即可
        targetId = String(targetId); // 目标 Id
        RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
          onSuccess: function (message) {
            // message 为发送的消息对象并且包含服务器返回的消息唯一 Id 和发送消息时间戳
            console.log(message)
            appType(targetId,content)
            if (message.senderUserId == message.targetId) {
              message.direction = 'receiver'
            } else {
              message.direction = 'sender'
            }
            r(message)
            console.log('Send successfully');
          },
          onError: function (errorCode, message) {
            j(errorCode)
            console.log('发送失败: ' + message + errorCode);
          }
        });
      })
    }
    var clearUnreadCount = (targetId) => {
    
      var conversationType = RongIMLib.ConversationType.PRIVATE;
      var targetId = String(targetId);
      RongIMClient.getInstance().clearUnreadCount(conversationType, targetId, {
        onSuccess: function (message) {
          console.log(message)
          // 清除未读消息成功
          // if (getCurrentPages()[0].route != "pages/conversation/chat" ) {
          getList(userListModle)
          // }
        },
        onError: function (error) {
          console.log(error)
    
          // error => 清除未读消息数错误码
        }
      });
    
    
    }
    var ReadReceiptMessage = (targetId, messageUId, lastMessageSendTime, type = 1) => {
      console.log(targetId, messageUId, lastMessageSendTime, type)
      var conversationType = RongIMLib.ConversationType.PRIVATE; // 单聊, 其他会话选择相应的会话类型即可
      var msg = new RongIMLib.ReadReceiptMessage({
        messageUId,
        lastMessageSendTime,
        type
      })
      targetId = String(targetId)
      RongIMClient.getInstance().sendMessage(conversationType, targetId, msg, {
        onSuccess: function (message) {
          // message 为发送的消息对象并且包含服务器返回的消息唯一 Id 和发送消息时间戳
          console.log(message, 'Send successfully');
        },
        onError: function (errorCode, message) {
          var info = '';
          switch (errorCode) {
            case RongIMLib.ErrorCode.TIMEOUT:
              info = '超时';
              break;
            case RongIMLib.ErrorCode.UNKNOWN:
              info = '未知错误';
              break;
            case RongIMLib.ErrorCode.REJECTED_BY_BLACKLIST:
              info = '在黑名单中,无法向对方发送消息';
              break;
            case RongIMLib.ErrorCode.NOT_IN_DISCUSSION:
              info = '不在讨论组中';
              break;
            case RongIMLib.ErrorCode.NOT_IN_GROUP:
              info = '不在群组中';
              break;
            case RongIMLib.ErrorCode.NOT_IN_CHATROOM:
              info = '不在聊天室中';
              break;
          }
          console.log('发送失败:' + info + errorCode);
        }
      });
    }
    // 初始化融云
    function connectIM(context, token) {
      RongIMClient = RongIMLib.RongIMClient;
      RongIMLib.RongIMClient.init(appkey);
      // let RongIMClient = RongIMLib.RongIMClient;
      // RongIMLib.RongIMClient.init(appkey);
    
      // console.log(RongIMClient.Conversation.get)
      // console.log(RongIMClient.getInstance().getFileToken)
    
      /* 连接状态监听器 */
      RongIMClient.setConnectionStatusListener({
        onChanged: function (status) {
          /* status 标识当前连接状态 */
          switch (status) {
            case RongIMLib.ConnectionStatus.CONNECTED:
              console.log('链接成功');
              wx.showToast({
                title: '链接成功',
                icon: "none"
              })
              break;
            case RongIMLib.ConnectionStatus.CONNECTING:
              console.log('正在链接');
              break;
            case RongIMLib.ConnectionStatus.DISCONNECTED:
              console.log('断开连接');
              wx.showToast({
                title: '断开连接',
                icon: "none"
              })
              break;
            case RongIMLib.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT:
              console.log('其他设备登录');
              wx.showToast({
                title: '其他设备登录',
                icon: "none"
              })
              break;
            case RongIMLib.ConnectionStatus.DOMAIN_INCORRECT:
              console.log('域名不正确');
              wx.showToast({
                title: '域名不正确',
                icon: "none"
              })
              break;
            case RongIMLib.ConnectionStatus.NETWORK_UNAVAILABLE:
              console.log('网络不可用');
    
              reconnect()
              wx.showToast({
                title: '网络不可用',
                icon: "none"
              })
              break;
          }
        }
      });
    
      /* 消息监听器 */
      RongIMClient.setOnReceiveMessageListener({
        onReceived: function (message) {
          console.log(message)
          if (userListModle) {
            userListModle.getList(message)
          }
          if (ChatRoom && ChatRoom.data.targetId == message.targetId) {
            ChatRoom.setNewMessage(message)
          }
        }
      });
    
      var config = {
        timeout: 20000,
        RongIMLib: RongIMLib,
        RongRTC: RongRTC,
        url: msUrls[0].url
      };
      var rongCallLib = RongCallLib.init(config);
      context.globalData.Service.rongCallLib = rongCallLib
      console.log(111, commandWatcher)
      rongCallLib.commandWatch(commandWatcher);
      console.log(token)
      RongIMClient.connect(token, {
        onSuccess: function (userId) {
          console.log('连接成功, 用户 ID 为', userId);
          // 连接已成功, 此时可通过 getConversationList 获取会话列表并展示
        },
        onTokenIncorrect: function () {
          console.log('连接失败, 失败原因: token 无效');
        },
        onError: function (errorCode) {
          var info = '';
          switch (errorCode) {
            case RongIMLib.ErrorCode.TIMEOUT:
              info = '超时';
              break;
            case RongIMLib.ConnectionState.UNACCEPTABLE_PAROTOCOL_VERSION:
              info = '不可接受的协议版本';
              break;
            case RongIMLib.ConnectionState.IDENTIFIER_REJECTED:
              info = 'appkey不正确';
              break;
            case RongIMLib.ConnectionState.SERVER_UNAVAILABLE:
              info = '服务器不可用';
              break;
          }
          console.log(info);
        }
      });
      context.globalData.Service = {
        ...context.globalData.Service,
        Status: {},
        Conversation: {},
        User: {},
        CONNECTION_STATUS: {},
        RongIMClient: RongIMClient.getInstance(),
        RongIMLib: RongIMLib,
        getList,
        layout,
        getHistory,
        ConversationType: RongIMLib.ConversationType,
        MessageType: RongIMClient.MessageType,
      }
    
    }
    
    App({
    
      setList(context) {
    
        userListModle = context
      },
      setChat(context) {
        setChat(context)
      },
     
      setChatRoom(context) {
        ChatRoom = context
      },
      onHide() {
        if(wx.getStorageSync('status') == 2){
          appletOnLine(0)
        }
      },
      onShow(){
        if(wx.getStorageSync('status') == 2){
          appletOnLine(1)
        }
      },
      setConversationList(conversationList) {
        if (chatList) {
        
          chatList.communicationList(conversationList)
        }
      },
      connectIM(token) {
        console.log(token, '开始初始化')
        connectIM(this, token)
      },
      onLaunch: function () {
        // 展示本地存储能力
        let token = wx.getStorageSync('RYtoken')
        if (token) {
          this.connectIM(token)
        }
        if (wx.getStorageSync('userId') && wx.getStorageSync('token')) {}
        const updateManager = wx.getUpdateManager()
        updateManager.onCheckForUpdate(function (res) {
          // 请求完新版本信息的回调
          console.log(res.hasUpdate)
        })
        updateManager.onUpdateReady(function () {
          wx.showModal({
            title: '更新提示',
            content: '新版本已经准备好,是否重启应用?',
            success(res) {
              if (res.confirm) {
                // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
                updateManager.applyUpdate()
              }
            }
          })
        })
        updateManager.onUpdateFailed(function () {
          // 新版本下载失败
        })
        wx.login({
          success: res => {
            // 发送 res.code 到后台换取 openId, sessionKey, unionId
          }
        })
        // 获取用户信息
        wx.getSetting({
          success: res => {
            if (res.authSetting['scope.userInfo']) {
              // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
              wx.getUserInfo({
                success: res => {
                  // 可以将 res 发送给后台解码出 unionId
                  this.globalData.userInfo = res.userInfo
    
                  // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
                  // 所以此处加入 callback 以防止这种情况
                  if (this.userInfoReadyCallback) {
                    this.userInfoReadyCallback(res)
                  }
                }
              })
            }
          }
        })
      },
      TextMessage(targetId, content, extra = {}) {
        return TextMessage(targetId, content, extra)
      },
      ImageMessage(targetId, base64Str, imageUri, extra = {}) {
        return ImageMessage(targetId, base64Str, imageUri, extra)
      },
      RegisterMessage(targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra = {}) {
        return RegisterMessage(targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra)
      },
      RegisterMessage1(targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra = {}) {
        return RegisterMessage1(targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra)
      },
      ReadReceiptMessage({
        targetId,
        messageUId,
        lastMessageSendTime,
        type
      }) {
        ReadReceiptMessage(targetId, messageUId, lastMessageSendTime, type)
      },
      clearUnreadCount(targetId) {
        clearUnreadCount(targetId)
      },
      LocationMessage(targetId, latitude, longitude, poi, content) {
        return LocationMessage(targetId, latitude, longitude, poi, content)
      },
      reconnect() {
        reconnect()
      },
      onReceiveMessage(message) {
        console.log(message)
        wx.navigateTo({
          url: '../../call/call?data=' + JSON.stringify(message)
        })
        return false
      },
      globalData: {
        Service: {
          Status: {},
          Conversation: {},
          User: {},
          CONNECTION_STATUS: {},
          getList,
          layout,
          getHistory,
          rongCallLib: null,
          ConversationType: RongIMLib.ConversationType,
        },
        userInfo: null,
        statusBarHeight: wx.getSystemInfoSync()['statusBarHeight'],
       
      }
    })
    

    这个是我聊天界面的js(在官方案例的基础上修改了,目前还没有整理好)

    import {
      request
    } from "../../request/index"
    let clear = true
    let timer = null;
    let dangDate = null;
    let LTID = null
    let timeInt = 4*60*60
    const scale = wx.getSystemInfoSync().windowWidth / 750
    //  通话配置
    const callUtils = require('../../utils/util');
    const Config = require('../../config.js');
    const common = require('../common');
    let newW = false
    const {
      co
    } = require('../../helper');
    const {
      apps,
      serverUrl,
      msUrls
    } = Config;
    const reg = /^\w{1,15}$/;
    let doLogin = false;
    
    //  默认配置
    const utils = require('../utils/utils.js');
    const {
      adapterHeight
    } = utils.getAdapterheight();
    
    
    const {
      globalData,
      setChatRoom,
      TextMessage,
      ImageMessage,
      LocationMessage,
      RegisterMessage,
      RegisterMessage1,
      clearUnreadCount
    } = getApp();
    const {
      Service: {
        Status,
        Message,
        File,
        Conversation,
        RongIMClient,
        RongIMLib,
        getHistory
      }
    } = globalData;
    // 
    const RongEmoji = require('../lib/RongIMEmoji-2.2.6.js');
    RongEmoji.init();
    
    const softKeyboardHeight = 210;
    
    const getToView = (context) => {
      let {
        messageList
      } = context.data;
      let index = messageList.length - 1;
      let message = messageList[index] || {};
      return message.uId || '';
    };
    const setNewMessage = (context, newMessage) => {
      console.log(newMessage, 1111111111111, getCurrentPages())
      if (newMessage.senderUserId == newMessage.targetId) {
        newMessage.direction = 'receiver'
      } else {
        newMessage.direction = 'sender'
      }
      context.data.messageList.push(newMessage)
      context.setData({
        messageList: context.data.messageList
      })
      if (clear) {
        console.log('qimhcji')
        clearUnreadCount(newMessage.targetId)
      }
      gotoView(context)
    }
    const imgBase64 = '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';
    const setKeyboardPos = (context, keyboardHeight, adapterHeight) => {
      keyboardHeight = keyboardHeight || 0;
      let data;
      let isScroll = (keyboardHeight > 0);
      if (isScroll) {
        data = {
          bottom: adapterHeight + keyboardHeight,
          isShowEmojiSent: false,
          toView: getToView(context)
        };
      } else {
        data = {
          bottom: adapterHeight + keyboardHeight,
          isShowEmojiSent: false
        };
      }
      context.setData(data);
    };
    
    const showSoftKeyboard = (context, display) => {
      context.setData({
        display: display,
        bottom: softKeyboardHeight,
        isShowKeyboard: false,
        toView: getToView(context)
      });
    };
    const hideSoftKeyboard = (context) => {
      context.setData({
        display: {
          emoji: 'none',
          more: 'none'
        }
      });
    };
    
    const hideKeyboard = (context) => {
      let keyboardHeight = 0;
      let {
        adapterHeight
      } = context.data;
      setKeyboardPos(context, keyboardHeight, adapterHeight);
      hideSoftKeyboard(context);
    };
    
    const formatEmojis = () => {
      let list = RongEmoji.list;
      return utils.sliceArray(list, {
        size: 24
      });
    };
    
    const getMessageList = (context, params) => {
      let {
        position
      } = params;
      let event = params.type == 4 ? Message.getChatRoomMessageList : Message.getList;
      return event(params).then((result) => {
        let messages = result.messageList;
        let hasMore = result.hasMore;
    
        let {
          messageList,
          playingVoice,
          playingMusicComponent
        } = context.data;
        messageList = messages.concat(messageList);
        let toView = '';
        if (params.position == 0) {
          let index = messageList.length - 1;
          let message = messageList[index] || {};
          toView = message.uId || '';
        }
        let isFirst = (position == 0);
        if (!hasMore && !isFirst) {
          // 灰条提示
          toView = 'message-notify-without';
          context.setData({
            hasMore: hasMore
          });
        }
    
        if (isFirst) {
          context.setData({
            messageList: messageList,
            isAllowScroll: true,
            toView: toView
          });
        } else {
          context.setData({
            messageList: messageList,
            isAllowScroll: true
          });
        }
    
      });
    };
    
    const updatePlayStatus = (context, {
      newMusicComponent,
      isPlaying
    }, callback) => {
      let {
        data: {
          messageList,
          playingMusicComponent
        }
      } = context;
      callback = callback || utils.noop;
      messageList.map((message) => {
        callback(message);
        return message;
      });
      if (playingMusicComponent) {
        playingMusicComponent.setData({
          isPlaying
        });
      }
      if (newMusicComponent) {
        context.setData({
          playingMusicComponent: newMusicComponent,
          messageList
        });
      } else {
        context.setData({
          messageList
        });
      }
    
    };
    
    const stopPlayMusic = (context) => {
      let newMusicComponent = null,
        isPlaying = false;
      updatePlayStatus(context, {
        newMusicComponent,
        isPlaying
      }, (message) => {
        utils.extend(message, {
          isPlaying
        });
      });
    };
    
    const getImageUrls = (context) => {
      let {
        messageList
      } = context.data;
      return messageList.filter(message => {
        return message.name == 'ImageMessage';
      }).map(message => {
        return message.content.imageUri;
      });
    };
    const byAccount = (context, type) => {
      request({
        url: 'my/byAccount',
        data: {
          accountId: type == 'my' ? wx.getStorageSync('userId') : context.data.userId
        }
      }).then(e => {
        if (e.data.status == 1) {
          context.setData({
            [type]: e.data.data
          })
        }
      })
    }
    const mygetHistory = (context, first) => {
      console.log(context.data.targetId, context.data.timestrap, context.data.count)
      getHistory(context.data.targetId, context.data.timestrap, context.data.count).then(({
        list,
        hasMsg
      }) => {
        list = [...list, ...context.data.messageList]
        console.log(list, hasMsg)
        list.forEach(element => {
          if (element.senderUserId == element.targetId) {
            element.direction = 'receiver'
          } else {
            element.direction = 'sender'
          }
        })
        context.setData({
          messageList: list,
          hasMsg: hasMsg,
          timestrap: list[0].sentTime
        })
        if (first) {
          gotoView(context,true)
        }
      })
    }
    const onLoad = (context, query) => {
      clear = true
      newW = true
      setChatRoom(context)
      let {
        title,
        type,
        targetId
      } = query;
      wx.setNavigationBarTitle({
        title
      });
      context.setData({
        adapterHeight: adapterHeight,
        type,
        targetId,
        userId: targetId
      });
      mygetHistory(context, true);
      byAccount(context, 'my');
      byAccount(context, 'your');
      let keyboardHeight = 0;
    
      setKeyboardPos(context, keyboardHeight, adapterHeight);
      // app.setChat(context)
      let position = 0;
      let count = 15;
      // getMessageList(context, { type, targetId, position, count });
    
      // Message.watch((message) => {
      //   if (message.isOffLineMessage) {
      //     return;
      //   }
      //   if (message.type == type && message.targetId === targetId) {
      //     let { messageList } = context.data;
      //     // console.log(message,1111111111111111111111111111)
      //     messageList.push(message);
      //     context.setData({
      //       messageList,
      //       toView: message.uId
      //     });
      //     Conversation.clearUnreadCount({
      //       type, targetId
      //     });
      //   }
      // });
    };
    
    const onUnload = (context) => {
      clear = false
      newW = false
      let {
        playingVoice,
        playingMusicComponent
      } = context.data;
      if (playingVoice) {
        playingMusicComponent.stop();
      }
      if (playingMusicComponent) {
        playingMusicComponent.stop();
      }
      // Message.unwatch();
    };
    
    const showVoice = (context) => {
      let {
        adapterHeight
      } = context.data;
      context.setData({
        isShowKeyboard: false
      });
      hideKeyboard(context);
    };
    
    const showKeyboard = (context) => {
      context.setData({
        isShowKeyboard: true
      });
      hideKeyboard(context);
    };
      const gotoView = (context,first) => {
    
        context.setData({
          toView: context.data.messageList[context.data.messageList.length - 1].messageUId
        })
        if (timer === null && !first && newW) {
          console.log("Start")
          timer = setInterval(() => {
            dangDate = parseInt(dangDate) + 1
            // console.log(dangDate)
            if (dangDate > timeInt) {
              context.showpjPopup()
              dangDate = 0
              wx.setStorageSync(LTID, 0)
              clearInterval(timer)
              timer = null
            }
          }, 1000);
        }
      }
    const recorderManager = wx.getRecorderManager()
    
    const startRecording = (context) => {
    
      wx.getSetting({
        success(res) {
          console.log(res.authSetting)
          // res.authSetting = {
          //   "scope.userInfo": true,
          //   "scope.userLocation": true
          // }
          wx.authorize({
            scope: 'scope.record',
            success() {
              // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
              wx.startRecord()
            }
          })
        }
      })
      // setTimeout(function () {
      //   wx.stopRecord() // 结束录音
      // }, 1000)
      context.setData({
        isRecording: true
      });
      let record = () => {
        recorderManager.start({
          format: 'aac'
        });
      };
      wx.getSetting({
        success(res) {
          if (!res.authSetting['scope.record']) {
            wx.authorize({
              scope: 'scope.record',
              success: record
            })
          } else {
            record();
          }
        }
      })
    };
    
    const stopRecording = (context) => {
      context.setData({
        isRecording: false
      });
      let {
        type,
        targetId,
        messageList
      } = context.data
      recorderManager.onStop((res) => {
        console.log('recorder stop', res)
        var {
          tempFilePath,
    
          fileSize
        } = res;
        console.log(tempFilePath, duration);
        var duration = res.duration
        duration = Math.ceil(duration / 1000)
        var fileType = RongIMLib.FileType.IMAGE;
        RongIMClient.getFileToken(fileType, {
          onSuccess: function (data) {
            console.log('上传 token 为', data.token);
            return wxUpload({
              path: tempFilePath,
              name: 'voice.aac'
            }, data.token).then(result => {
              console.log(result)
              let content = {
                // remoteUrl: file.downloadUrl,
                // duration: Math.ceil(duration / 1000)
              };
              let qiniuHash, qiniuName;
              const {
                data
              } = result;
              const {
                hash,
                name
              } = JSON.parse(data);
              qiniuHash = hash, qiniuName = name;
              console.log(RongIMClient.getFileUrl(fileType, qiniuHash, qiniuName, res))
              return RongIMClient.getFileUrl(fileType, qiniuHash, qiniuName, {
                onSuccess: (e) => {
                  console.log(e.downloadUrl)
                  var mediaUrl = e.downloadUrl
                  var fileUrl = e.downloadUrl
                  let extra = {}
                  RegisterMessage(targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra).then((e) => {
                    messageList.push(e)
                    context.setData({
                      messageList: messageList
                    })
                    gotoView(context)
                  })
                  // ImageMessage(targetId,tempFilePath,e.downloadUrl, extra ).then(e => {
                  //   messageList.push(e)
                  //   context.setData({
                  //     messageList: messageList
                  //   })
                  //   gotoView(context)
                  // }).catch(e => {
                  //   wx.showToast({
                  //     title: '发送失败~',
                  //     icon: "none"
                  //   })
                  // })
                },
                onError: (e) => {
                  console.log(e)
                }
              });
    
              // Message.sendImage({
              //   type,
              //   targetId,
              //   imageUri,
              //   extra,
              //   content: imgBase64
              // }).then(message => {});
            });
    
          },
          onError: function (error) {
            console.log('get file token error', error);
          }
        })
        //   File.upload({
        //     path: tempFilePath,
        //     name: 'voice.aac'
        //   }, 2).then(file => {
        //     console.log(file)
        //     let content = {
        //       remoteUrl: file.downloadUrl,
        //       duration: Math.ceil(duration / 1000)
        //     };
        //     let {
        //       type,
        //       targetId,
        //       messageList
        //     } = context.data;
        //     Message.sendVoice({
        //       type,
        //       targetId,
        //       content
        //     }).then(message => {
        //       messageList.push(message);
        //       context.setData({
        //         messageList,
        //         toView: message.uId
        //       });
        //     });
        //   });
      })
      recorderManager.stop();
    };
    const uploadBos = (url, fileInfo, header) => {
      return new Promise((resolve, reject) => {
        const fileData = wx.getFileSystemManager().readFileSync(fileInfo.path);
        wx.request({
          url: url,
          header: header,
          method: 'POST',
          data: fileData,
          success: function (res) {
            console.log(res);
            let data = {
              downloadUrl: url, //上传成功的 url 即为下载 url
              isBosRes: true // 判断是否是百度返回
            }
            resolve(data);
          },
          fail: reject
        })
      })
    
    }
    const wxUpload = (fileInfo, token) => {
      return new Promise((resolve, reject) => {
        const uploadTask = wx.uploadFile({
          url: Config.qiniuHost,
          filePath: fileInfo.path,
          name: 'file',
          formData: {
            token: token
          },
          success: resolve,
          fail: function (err) {
            console.log(err)
            // console.log('upload qiniu failed', err);
            // uploadBos(bosUrl, fileInfo, bosHeaders).then(function(res) {
            //   resolve(res);
            // },function(err) {
            //   reject(err)
            // });
          }
        })
        wx.showLoading({
          title: '上传中',
        })
        uploadTask.onProgressUpdate(res => {
          if (res.progress == 100) {
            wx.hideLoading({
              complete: (res) => { },
            })
          }
          console.log('上传进度', res.progress)
          console.log('已经上传的数据长度', res.totalBytesSent)
          console.log('预期需要上传的数据总长度', res.totalBytesExpectedToSend)
        })
      });
    };
    
    const showEmojis = (context) => {
      showSoftKeyboard(context, {
        emoji: 'block',
        more: 'none'
      });
    };
    
    const showMore = (context) => {
      showSoftKeyboard(context, {
        emoji: 'none',
        more: 'block'
      });
    };
    
    const selectEmoji = (context, event) => {
      var content = context.data.content;
      var {
        emoji
      } = event.target.dataset;
      content = content + emoji;
      context.setData({
        content: content,
        isShowEmojiSent: true
      });
    };
    
    const sendText = (context) => {
      let {
        content,
        type,
        targetId,
        messageList
      } = context.data;
      // console.log(content)
      // console.log(content, type, targetId, messageList)
      
      context.setData({
        content: '',
        isShowEmojiSent: false
      });
    
      TextMessage(targetId, content).then(e => {
        messageList.push(e)
        context.setData({
          messageList: messageList
        })
        gotoView(context)
      }).catch(e => {
        wx.showToast({
          title: '发送失败~',
          icon: "none"
        })
      })
      // content = {
      //   content,
      //   extra
      // }
      // console.log(content)
      // content = JSON.stringify(content)
      // if (content.length == 0) {
      //   return;
      // }
      // Message.sendText({
      //   type,
      //   targetId,
      //   content,
      // }).then(message => {
      //   message.content.extra = extra
      //   message.extra = extra
      //   messageList.push(message);
      //   context.setData({
      //     messageList,
      //     toView: message.uId
      //   });
      // });
    };
    
    const getMoreMessages = (context) => {
      let {
        type,
        targetId,
        hasMore,
        messageList
      } = context.data;
      messageList = messageList || [];
      let firstMessage = messageList[0] || {};
      let position = firstMessage.sentTime || 0;
      let count = 5;
      if (context.data.hasMsg) {
        // context.data.timestrap ++ ;
        context.setData({
          isAllowScroll: false,
        });
        mygetHistory(context);
        // , { type, targetId, position, count }
      } else {
        context.setData({
          isAllowScroll: false,
        });
        wx.showToast({
          title: '没有更多消息了',
          icon: "none",
        })
      }
    };
    
    const sendImage = (context) => {
      wx.chooseImage({
        count: 1,
        sizeType: ['original'],
        sourceType: ['album', 'camera'],
        success: (res) => {
          let {
            tempFilePaths,
            tempFiles
          } = res;
          let tempFilePath = tempFilePaths[0];
          console.log('tempFilePath', tempFilePath)
          // ImageMessage
          wx.getImageInfo({
            src: tempFilePath,
            success: (res) => {
              let extra = utils.compress(res);
              let {
                type,
                targetId,
                messageList
              } = context.data;
    
              let name = 'RC:ImgMsg';
              let content = {
                imageUri: tempFilePath,
                extra
              };
              console.log(res, content)
              var fileType = RongIMLib.FileType.IMAGE;
              RongIMClient.getFileToken(fileType, {
                onSuccess: function (data) {
                  console.log('上传 token 为', data.token);
                  return wxUpload({
                    path: tempFilePath,
                    name: 'image.png'
                  }, data.token).then(result => {
                    console.log(result)
                    let qiniuHash, qiniuName;
                    const {
                      data
                    } = result;
                    const {
                      hash,
                      name
                    } = JSON.parse(data);
                    qiniuHash = hash, qiniuName = name;
                    console.log(RongIMClient.getFileUrl(fileType, qiniuHash, qiniuName, res))
                    return RongIMClient.getFileUrl(fileType, qiniuHash, qiniuName, {
                      onSuccess: (e) => {
                        ImageMessage(targetId, tempFilePath, e.downloadUrl, extra).then(e => {
                          messageList.push(e)
                          context.setData({
                            messageList: messageList
                          })
                          gotoView(context)
                        }).catch(e => {
                          wx.showToast({
                            title: '发送失败~',
                            icon: "none"
                          })
                        })
                      },
                      onError: (e) => {
                        console.log(e)
                      }
                    });
                  });
                },
                onError: function (error) {
                  console.log('get file token error', error);
                }
              })
            }
          })
        }
      })
    };
    
    const sendFile = (context) => {
      let {
        type,
        targetId,
        messageList
      } = context.data;
    
      wx.chooseMessageFile({
        count: 1,
        type: 'file',
        success: function (res) {
          let {
            tempFiles
          } = res;
          let {
            name,
            size,
            type: fileType
          } = tempFiles[0];
          File.upload(tempFiles[0], 4).then(result => {
            const {
              downloadUrl: fileUrl
            } = result;
            console.log(fileUrl)
            let content = {
              name,
              size,
              type: fileType,
              fileUrl
            };
            let message = Message.create({
              type: 4,
              targetId,
              name: 'RC:FileMsg',
              content
            });
            messageList.push(message);
            context.setData({
              messageList,
              toView: message.uId
            });
            Message.sendFile({
              type,
              targetId,
              name,
              size,
              fileUrl
            })
          });
        }
      })
    }
    const sendAudio = (context) => {
      let {
        type,
        targetId,
        messageList
      } = context.data;
      context.login()
    }
    const sendVideo = (context) => {
      wx.chooseVideo({
        count: 1,
        sourceType: ['album', 'camera'],
        success: (res) => {
          console.log('tempFilePath', res)
          let extra = utils.compress(res);
          let {
            type,
            targetId,
            messageList
          } = context.data;
          let content = {
            imageUri: tempFilePath,
          };
          var {
            tempFilePath,
            fileSize,
            duration
          } = res;
          console.log(tempFilePath, duration);
          var fileType = RongIMLib.FileType.IMAGE;
          RongIMClient.getFileToken(fileType, {
            onSuccess: function (data) {
              console.log('上传 token 为', data.token);
              return wxUpload({
                path: tempFilePath,
                name: 'voice.mp'
              }, data.token).then(result => {
                console.log(result)
                let content = {
                  // remoteUrl: file.downloadUrl,
                  // duration: Math.ceil(duration / 1000)
                };
                let qiniuHash, qiniuName;
                const {
                  data
                } = result;
                const {
                  hash,
                  name
                } = JSON.parse(data);
                qiniuHash = hash, qiniuName = name;
                console.log(RongIMClient.getFileUrl(fileType, qiniuHash, qiniuName, res))
                return RongIMClient.getFileUrl(fileType, qiniuHash, qiniuName, {
                  onSuccess: (e) => {
                    console.log(e.downloadUrl)
                    var mediaUrl = e.downloadUrl
                    var fileUrl = e.downloadUrl
                    let extra = {}
                    RegisterMessage1(targetId, name, mediaUrl, fileUrl, fileType, fileSize, duration, extra).then((e) => {
                      messageList.push(e)
                      context.setData({
                        messageList: messageList
                      })
                      gotoView(context)
                    })
    
                  },
                  onError: (e) => {
                    console.log(e)
                  }
                });
              });
            },
            onError: function (error) {
              console.log('get file token error', error);
            }
          })
        }
      })
    }
    const sendMusic = (context) => {
      let {
        content,
        type,
        targetId,
        messageList
      } = context.data;
      Message.sendMusic({
        type,
        targetId
      }).then(message => {
        messageList.push(message);
        context.setData({
          messageList,
          toView: message.uId
        });
      });
    };
    
    const playVoice = (context, event) => {
      let voiceComponent = event.detail;
      let {
        playingVoice
      } = context.data;
      if (playingVoice) {
        let playingId = playingVoice.__wxExparserNodeId__;
        let voiceId = voiceComponent.__wxExparserNodeId__;
        // 两次播放为同个音频,状态保持不变 
        if (playingId == voiceId) {
          return;
        }
        let {
          innerAudioContext
        } = playingVoice.data;
        playingVoice.setData({
          isPlaying: false
        });
        innerAudioContext.stop();
      }
      context.setData({
        playingVoice: voiceComponent
      });
    };
    
    const playMusic = (context, event) => {
      let newMusicComponent = event.detail;
      let {
        playingMusicComponent,
        messageList
      } = context.data;
      let {
        properties: {
          message: {
            messageUId: newPlayId
          }
        }
      } = newMusicComponent
      let playingId = '';
    
      // 连续点击播放不同音乐
      if (playingMusicComponent) {
        let {
          properties: {
            message
          }
        } = playingMusicComponent;
        playingId = message.messageUId;
        //先停止上一个,再播放
        let isDiffMusic = (playingId != newPlayId);
        if (isDiffMusic) {
          let {
            innerAudioContext
          } = playingMusicComponent.data;
          playingMusicComponent.setData({
            isPlaying: false
          });
          innerAudioContext.stop();
        }
      }
      let isPlaying = false;
      updatePlayStatus(context, {
        newMusicComponent,
        isPlaying
      }, (message) => {
        let {
          messageUId
        } = message;
        // 默认为未播放状态
        isPlaying = false;
        if (messageUId == newPlayId) {
          isPlaying = true;
        }
        utils.extend(message, {
          isPlaying
        });
      });
    };
    
    const previewImage = (context, event) => {
      let currentImageUrl = event.detail;
      let urls = getImageUrls(context);
      if (utils.isEmpty(urls)) {
        urls.push(currentImageUrl);
      }
      wx.previewImage({
        current: currentImageUrl,
        urls: urls
      })
    };
    
    const stopMusic = (context, event) => {
      let musicComponent = event.detail;
      let {
        properties: {
          message: {
            messageUId
          }
        }
      } = musicComponent;
    
      let {
        messageList,
        playingMusicComponent
      } = context.data;
      if (playingMusicComponent) {
        let {
          data: {
            innerAudioContext
          }
        } = playingMusicComponent;
        innerAudioContext.stop();
      }
      musicComponent.setData({
        isPlaying: false
      });
      stopPlayMusic(context);
    };
    // call用到的方法
    const expertRecord = (context) => {
      if (context.data.count == 0) {
        request({
          url: 'index/expertRecord',
          data: {
            token: wx.getStorageSync("token"),
            id: context.data.userId
            // 
          }
        }).then((e) => {
          if (e.data.status === 1) {
            context.setData({
              count: 1
            })
          }
        })
      }
    
    }
    Page({
      data: {
        count: 8,
        timestrap: 0,
        hasMsg: true,
        my: {},
        your: {},
        pjvalue: 0,
        // call中用到的data
        userId: '',
        isHiddenTip: true,
        pjshow: false,
        apps,
        appkeys: apps.map(item => item.appkey),
        appkeyIndex: 0,
        msUrls: msUrls.map(item => item.title),
        msUrl: msUrls[0],
        // 默认data
        content: '',
        messageList: [],
        bottom: 0,
        adapterHeight: 0,
        display: {
          emoji: 'none',
          more: 'none'
        },
        emojis: formatEmojis(),
        isShowEmojiSent: false,
        isRecording: false,
        isShowKeyboard: false,
        hasMore: true,
        toView: '',
        playingVoice: null,
        playingMusicComponent: null,
        isAllowScroll: true,
        scrollTop: 0
      },
      evaluateExpert() {
        request({
          url: "index/evaluateExpert",
          data: {
            token: wx.getStorageSync('token'),
            id: this.data.userId,
            level: this.data.pjvalue
          }
        }).then(res => {
          if (res.data.status == 1) {
            wx.showToast({
              title: res.data.msg,
            })
            this.onpjClose()
          }
        })
      },
      onpjChange(event) {
        this.setData({
          pjvalue: event.detail,
        });
      },
      showpjPopup() {
        if (wx.getStorageSync('status') == 1) {
          this.setData({ pjshow: true });
        }
      },
    
      onpjClose() {
        this.setData({ pjshow: false, pjvalue: 0 });
      },
      expertRecord: function () {
        expertRecord(this)
      },
      hideKeyboard: function () {
        hideKeyboard(this);
      },
      selectEmoji: function (event) {
        selectEmoji(this, event);
      },
      sendText: function () {
        // if (timer === null) {
        //   timer = setInterval(() => {
        //     dangDate = parseInt(dangDate) + 1
        //     console.log(dangDate)
        //     if (dangDate > timeInt) {
        //       this.showpjPopup()
        //       dangDate = 0
        //       clearInterval(timer)
        //       timer = null
        //     }
        //   }, 1000);
        // }
        sendText(this);
      },
      getMoreMessages: function (event) {
        getMoreMessages(this);
      },
      sendImage: function () {
    
        // if (timer === null) {
        //   timer = setInterval(() => {
        //     dangDate = parseInt(dangDate) + 1
        //     if (dangDate > timeInt) {
        //       this.showpjPopup()
        //       dangDate = 0
        //       clearInterval(timer)
        //       timer = null
        //     }
        //   }, 1000);
        // }
    
        sendImage(this);
      },
      sendFile: function () {
        // if (timer === null) {
        //   timer = setInterval(() => {
        //     dangDate = parseInt(dangDate) + 1
        //     if (dangDate > timeInt) {
        //       this.showpjPopup()
        //       dangDate = 0
        //       clearInterval(timer)
        //       timer = null
        //     }
        //   }, 1000);
        // }
        sendFile(this);
      },
      sendAudio: function () {
        // if (timer === null) {
        //   timer = setInterval(() => {
        //     dangDate = parseInt(dangDate) + 1
        //     if (dangDate > timeInt) {
        //       this.showpjPopup()
        //       dangDate = 0
        //       clearInterval(timer)
        //       timer = null
        //     }
        //   }, 1000);
        // }
        sendAudio(this)
      },
      sendVideo: function () {
    
        // if (timer === null) {
        //   timer = setInterval(() => {
        //     dangDate = parseInt(dangDate) + 1
        //     if (dangDate > timeInt) {
        //       this.showpjPopup()
        //       dangDate = 0
        //       clearInterval(timer)
        //       timer = null
        //     }
        //   }, 1000);
    
        // }
        sendVideo(this)
      },
      sendMusic: function () {
    
        // if (timer === null) {
        //   timer = setInterval(() => {
        //     dangDate = parseInt(dangDate) + 1
        //     if (dangDate > timeInt) {
        //       this.showpjPopup()
        //       dangDate = 0
        //       clearInterval(timer)
        //       timer = null
        //     }
        //   }, 1000);
    
        // }
        sendMusic(this);
      },
      showVoice: function () {
    
        showVoice(this);
      },
      showKeyboard: function () {
        showKeyboard(this);
      },
      startRecording: function () {
        startRecording(this);
      },
      stopRecording: function () {
        stopRecording(this);
      },
      showEmojis: function () {
        showEmojis(this);
      },
      showMore: function () {
        showMore(this);
      },
      gotoView(){
        gotoView(this)
      },
      // 以下是事件
      onLoad: function (query) {
        onLoad(this, query)
        LTID = wx.getStorageSync('LT' + wx.getStorageSync('userId') + query.targetId)
        console.log()
        if (dangDate === null) {
          if (LTID) {
            dangDate = LTID ? LTID : 0
          } else {
            wx.setStorageSync('LT' + wx.getStorageSync('userId') + query.targetId, 0)
            dangDate = 0
          }
        }
        console.log('LT' + wx.getStorageSync('userId') + query.targetId,LTID,dangDate)
        query.count && this.setData({
          count: query.count
        })
      },
      onUnload: function () {
        onUnload(this);
        console.log(LTID, LTID !== null)
        if (LTID !== null) {
          console.log('LT' + wx.getStorageSync('userId') + this.data.targetId, dangDate)
          wx.setStorageSync('LT' + wx.getStorageSync('userId') + this.data.targetId, dangDate)
          LTID = null
          dangDate = null
        }
        clearTimeout(timer)
        timer = null
      },
      onInput: function (event) {
        this.setData({
          content: event.detail.value
        });
      },
      onFocus: function (event) {
        let {
          height
        } = event.detail;
        let adapterHeight = 0;
        setKeyboardPos(this, height, adapterHeight);
        hideSoftKeyboard(this);
      },
      onPlayVoice: function (event) {
        playVoice(this, event);
      },
      setNewMessage(newMessage) {
        setNewMessage(this, newMessage)
      },
      onPlayMusic: function (event) {
        playMusic(this, event);
      },
      onMusicStop: function (event) {
        stopMusic(this, event);
      },
      onPreviewImage: function (event) {
        previewImage(this, event);
      },
      onHide: function () {
        hideKeyboard(this);
        stopPlayMusic(this);
      },
      // call的方法
      async login() {
        if (doLogin) {
          return;
        }
        doLogin = true;
        let context = this;
        let {
          data: {
            userId,
            msUrl,
            appkeyIndex
          }
        } = context;
        console.log(userId, msUrl, appkeyIndex)
        let isUserEmpty = callUtils.isEmpty(userId);
        if (isUserEmpty) {
          context.setData({
            isHiddenTip: !isUserEmpty
          });
          doLogin = false;
          return;
        }
    
        const {
          appkey,
          secret
        } = apps[appkeyIndex];
    
        wx.showLoading({
          title: '登录中',
          mask: true
        })
        let result;
        try {
          result = await co(wx.request)({
            url: serverUrl,
            method: 'POST',
            data: {
              appkey,
              secret,
              userId,
              name: userId,
              portrait: `portrait-${userId}`,
            },
          });
        } catch (error) {
          doLogin = false;
          wx.hideLoading();
          common.showToast(`获取 Token 失败 ${error.statusCode}`);
          return;
        }
    
        wx.hideLoading();
    
        const {
          token,
          code,
          msg
        } = result.data;
        if (code !== 200) {
          console.error(code, msg);
          return;
        }
    
        // app.setServiceConf(appkey, msUrl.url);
    
        wx.navigateTo({
          url: `../main/main?token=${encodeURIComponent(token)}&userId=${userId}&appkey=${appkey}`,
        });
    
        doLogin = false;
      },
      call1() {
        if (timer === null) {
          console.log("Start")
          timer = setInterval(() => {
            dangDate = parseInt(dangDate) + 1
            // console.log(dangDate)
            if (dangDate > timeInt) {
              this.showpjPopup()
              dangDate = 0
              clearInterval(timer)
              timer = null
            }
          }, 1000);
        }
        wx.navigateTo({
          url: '../call/call?type=2&targetId=' + this.data.targetId,
        })
      },
      call2() {
        if (timer === null) {
          console.log("Start")
          timer = setInterval(() => {
            dangDate = parseInt(dangDate) + 1
            // console.log(dangDate)
            if (dangDate > timeInt) {
              this.showpjPopup()
              dangDate = 0
              clearInterval(timer)
              timer = null
            }
          }, 1000);
        }
        wx.navigateTo({
          url: '../call/call?type=0&targetId=' + this.data.targetId,
        })
      },
    
      selectLocation() {
        let that = this
        let {
          messageList
        } = that.data;
        wx.getLocation({
          type: 'gcj02',
          altitude: 'true',
          success(res) {
            const latitude = res.latitude
            const longitude = res.longitude
            const speed = res.speed
            const accuracy = res.accuracy
            let context = `https://restapi.amap.com/v3/staticmap?markers=-1,http://www.5imoban.net/view/demoimg/jrzb_position_icon.png,0:${longitude},${latitude}&key=ee95e52bf08006f63fd29bcfbcf21df0&zoom=17&size=360*200&location=${longitude},${latitude}`
    
            wx.chooseLocation({
              latitude,
              longitude,
              complete: (res) => {
                console.log(res)
                if (res.latitude) {
                  LocationMessage(that.data.targetId, res.latitude, res.longitude, res.address, context).then(ele => {
                    console.log(ele)
                    messageList.push(ele)
                    that.setData({
                      messageList: messageList
                    })
                    gotoView(that)
                  })
                }
              }
            })
          }
        })
    
      },
      onShow(){
        // let context = this
        // mygetHistory(context, true);
      }
    
    })
    

    这个是我打电话界面的js(也是还没整理)优化的余地还是很多的,奈何没有时间)

    // pages/call/call.js
    import {
      request
    } from "../../request/index"
    let count = true;
    let timer;
    // const { TextMessage } = getApp()
    const common = require('../common');
    const utils = require('../../utils/util.js');
    let TextMessage = null;
    let rongCallLib = null;
    let RongIMLib = null;
    
    // const {Service:{rongCallLib,RongIMLib}} = getApp().globalData
    // console.log(getApp().globalData)
    // console.log(Call,ConversationType,MessageType,Connection)
    // const  rongCallLib
    let hungupCount = false
    function backNav() {
      console.log(hungupCount)
      if (hungupCount) {
        console.log('执行')
        wx.navigateBack({
          complete: (res) => { },
        })
        hungupCount = false
      }
    
    }
    const conversationTypes = [
      { type: 1, label: '单聊' },
      { type: 3, label: '群聊' }
    ];
    const MediaTypes = [
      { type: 2, label: '视频通话' },
      { type: 1, label: '音频通话' },
    ];
    const Status = {
      // 空闲
      FREE: 0,
      // 等待接听或挂断,暂未加入房间,包含呼入、呼出场景
      WAITING: 1,
      // 通话中,即已经加入房间,群聊状态下有一人曾接通过即进入到通话中状态
      TALKING: 2,
    }
    let that;
    Page({
    
      /**
       * 页面的初始数据
       */
      desc: '',
      back: true,
      data: {
        crtUserId: '',
    
        // 通话状态
        status: Status.FREE,
        // 根据 status 变化修改,computed
        free: true,
        waiting: false,
        talking: false,
        // 群呼中途邀请标记
        inviting: false,
        // 被邀请人
        inviteUserIds: '',
        // 是否为呼入
        callin: false,
        // 当前通话的 targetId,单聊为对方 userId,群聊为 groupId
        crtTargetId: '',
        // 当前的通话类型
        crtConversationType: 0,
        // 当前通话中的人员,不包含自己,单聊时长度为 1,群聊时长度 >= 1,当长度为 0 时需挂断当前通话
        // 某些条件下,对方可能无法发送 hungupMessage 信息,如直接关闭应用、杀进程
        crtMembers: [],
        statusCall: true,
        // 输入选项
        conversationTypeOptions: conversationTypes.map(item => item.label),
        mediaTypeOptions: MediaTypes.map(item => item.label),
        message: null,
        // 输入表单
        userId: '',
        groupId: '',
        conversationTypeIndex: 0,
        conversationType: conversationTypes[0].type,
        mediaTypeIndex: 0,
        mediaType: '',
        player: null,
        // 推送视频流
        pusher: null,
        targetId: '',
        // 拉取视频流:{ [userId]: mediaStream }
        streams: {
        },
        type: '',
        back: true,
        showUser: {},
        senderUserId: '',
        show: true,
        hungupfalse: false
      },
    
      /**
       * 生命周期函数--监听页面加载
       */
      // 接听
      inputUserId(e) {
        const userId = e.detail.value;
        this.setData({ userId });
      },
      onLoad: function (options) {
        TextMessage = getApp().TextMessage
        rongCallLib = getApp().globalData.Service.rongCallLib
        RongIMLib = getApp().globalData.Service.RongIMLib
        console.log(rongCallLib)
        count = true
        that = this
        hungupCount = true
        // Connection.watch((status) => {
        //   console.log('status:', status);
        // });
        this.audioCtx = wx.createInnerAudioContext()
        this.audioCtx.src = '/image/1.mp3'
        this.audioCtx.loop = true
        this.audioCtx.play()
        console.log(options.data && JSON.parse(options.data))
    
        if (options.data) {
          // timer = setTimeout(() => {
          //   if(that.data.back){
          //     wx.showToast({
          //       title: '超时,未接通',
          //       icon:"none",
          //       success(){
    
          //         setTimeout(() => {
          //           wx.navigateBack({
          //             complete: (res) => {},
          //           })
          //         },200)
          //       }
          //     })
          //   }
          // },12000)
          this.setData({
            message: JSON.parse(options.data),
            senderUserId: JSON.parse(options.data).senderUserId
          })
          console.log(this.data.senderUserId, 11111111)
          this.setData({
            mediaType: this.data.message.content.mediaType
          })
          request({
            url: 'my/byAccount',
            data: {
              accountId: this.data.message.senderUserId,
            }
          }).then(e => {
            if (e.data.status == 1) {
              that.setData({
                showUser: e.data.data
              })
            }
          })
        }
        if (options.targetId) {
          console.log('我拨打')
          this.setData({
            mediaType: options.type == 0 ? 1 : 2
          })
          request({
            url: 'my/byAccount',
            data: {
              accountId: options.targetId
            }
          }).then(e => {
            if (e.data.status == 1) {
              that.setData({
                showUser: e.data.data
              })
            }
          })
          that.setData({
            userId: options.targetId,
            type: options.type
          }, () => {
            that.call()
          })
        }
    
        rongCallLib.videoWatch(this.onVideoChange1.bind(this));
        // this.onReceiveMessage(JSON.parse(options.data))
        rongCallLib.commandWatch(this.onVideoChange.bind(this));
    
      },
      onReceiveMessage(message) {
        // const { MessageType, ConversationType } = app.getService();
        console.log(message)
        const { messageType, conversationType, targetId, senderUserId, content } = message;
      },
      onVideoChange1(video) {
        const { type, userId, data } = video;
        if (type === 'added') {
          // 添加音视频节点
          console.log(userId == that.data.userId, userId, that.data.userId)
          this.setData({
            hungupfalse: true
          })
          if (userId == wx.getStorageSync('userId')) {
            that.setData({
              pusher: video
            })
          } else {
    
            that.setData({
              back: false,
              show: false,
              player: video
            })
          }
    
        } else if (type === 'removed') {
          // 删除对应音视频节点
          this.audioCtx.pause()
          wx.showToast({
            title: '通话结束',
            icon: "none"
          })
          this.setData({
            pusher: null,
            player: null
          })
          setTimeout(() => {
            backNav()
          }, 400);
        } else if (type === 'leave') {
          // 删除对应音视频节点
          this.audioCtx.pause()
          this.setData({
            pusher: null,
            player: null
          })
    
          if (that.data.back) {
            this.audioCtx.pause()
            wx.showToast({
              title: '通话结束',
              icon: "none"
            })
            setTimeout(() => {
              backNav()
    
            }, 400);
          }
    
        }
      },
      getSummaryText(status) {
        var text;
        switch (status) {
          // case 1:
          //   text = '己方已取消';
          //   break;
          // case 2:
          //   text = '己方已拒绝';
          //   break;
          // case 3:
          //   text = '己方挂断';
          //   break;
          case 4:
            text = `己方忙碌中, 不处理`;
            break;
          case 5:
            text = '己方未接听';
            break;
          // default:
          //   text = '未知原因';
        }
        return text;
      },
      onVideoChange(video) {
        const { type, userId, data } = video;
        const { streams } = this.data;
        console.log('commandWatch =>', video, video.content);
        if (video.messageType == "SummaryMessage") {
          var status = video.content.status;
          var promptText = this.getSummaryText(status);
          console.log(promptText)
          if (promptText) {
            wx.showToast({
              title: promptText,
              icon: "none"
            })
          }
    
          // if (status === 5) { // 自己未接听, 回到初始状态
          //   context.callStep = CallStep.READY_TO_CALL;
          // }
        }
        if (video.messageType == "HungupMessage" && video.content && video.content.reason) {
          let data = {
            1: '己方取消已发出的通话请求',
            2: '己方拒绝收到的通话请求',
            3: '己方挂断',
            4: '己方忙碌',
            5: '己方未接听',
            6: '当前引擎不支持',
            7: '己方网络出错',
            8: '其他端已经接听',
            11: '对方取消已发出的通话请求',
            12: '对方拒绝收到的通话请求',
            13: '通话过程对方挂断',
            14: '对方忙碌',
            15: '对方未接听',
            16: '对方引擎不支持',
            17: '对方网络错误',
            18: 'im ipc服务已断开',
          }
          console.log(data[video.content.reason])
          this.setData({
            desc: data[video.content.reason]
          })
          // this.hungup()
          wx.showToast({
            title: data[video.content.reason],
            icon: "none",
            success() {
              setTimeout(() => {
                backNav()
    
              }, 300)
            },
            complete(res) {
            }
          })
        }
        // if(this.data.hungupfalse && video.content && video.content.reason == 13 ){
        //   console.log('未接通被挂断')
        //   wx.showToast({
        //     title: data[video.content.reason],
        //     icon:"none",
        //     success(){
        //       setTimeout(() => {
        //         wx.navigateBack({
        //           complete: (res) => {},
        //         })
        //       },200)
        //     }
        //   })
        // }
        if (that.data.message && video.content && video.content.messageName == "HungupMessage") {
          console.log()
    
          this.audioCtx.pause()
          // if(this.data.player){
          //   wx.showToast({
          //     title: '对方已取消',
          //     icon:"none",
          //     success(){
          //       setTimeout(() => {
          //         wx.navigateBack({
          //           complete: (res) => {},
          //         })
          //       },200)
          //     }
          //   })
          // }
    
        }
        if (video.content && video.content.messageName == "AcceptMessage") {
          wx.hideLoading({
            complete: (res) => { },
          })
          this.audioCtx.pause()
          wx.showToast({
            title: '已接听',
          })
        }
        console.log(video.content && video.content.messageName == "InviteMessage")
        if (video.content && video.content.messageName == "InviteMessage") {
          console.log(video)
          that.setData({
            callin: true,
          })
          console.log(that.data)
        }
        function getSeconds(s) {
          var sTime = parseInt(s);// 秒
          var mTime = 0;// 分
          var hTime = 0;// 时
          if (sTime > 60) {//如果秒数大于60,将秒数转换成整数
            //获取分钟,除以60取整数,得到整数分钟
            mTime = parseInt(sTime / 60);
            //获取秒数,秒数取佘,得到整数秒数
            sTime = parseInt(sTime % 60);
            //如果分钟大于60,将分钟转换成小时
            if (mTime > 60) {
              //获取小时,获取分钟除以60,得到整数小时
              hTime = parseInt(mTime / 60);
              //获取小时后取佘的分,获取分钟除以60取佘的分
              mTime = parseInt(mTime % 60);
            }
          }
          var result = '';
          if (sTime >= 0 && sTime < 10) {
            result = "0" + parseInt(sTime) + "";
          } else {
            result = "" + parseInt(sTime) + "";
          }
          if (mTime >= 0 && mTime < 10) {
            result = "0" + parseInt(mTime) + ":" + result;
          } else {
            result = "" + parseInt(mTime) + ":" + result;
          }
          if (hTime >= 0 && hTime < 10) {
            result = "0" + parseInt(hTime) + ":" + result;
          } else {
            result = "" + parseInt(hTime) + ":" + result;
          }
          return result;
        }
        if (video.messageType == "SummaryMessage") {
          that.setData({
            player: null,
            pusher: null
          })
          // wx.showToast({
          //   icon:"none",
          //   title: '语音超时未接通',
          // })
          // wx.navigateBack({
          //   complete: (res) => {
          //     console.log("挂断")
          //   },
          // })
          backNav()
          this.audioCtx.pause()
          console.log(video.senderUserId, '-----------------')
          if (video.senderUserId == wx.getStorageSync('userId') && count) {
            count = false
            let targetId = video.targetId
            let content = getSeconds(video.content.duration / 1000)
            console.log('取消时呼叫方', video.content)
            if (video.content.duration > 0) {
              content = '通话结束:' + content
            } else {
              content = '[语音未接通]'
            }
            console.log(content)
            TextMessage(targetId, content).then(e => {
              console.log(e)
              let pages = getCurrentPages()[getCurrentPages().length - 1]
              pages.data.messageList.push(e)
              pages.setData({
                messageList: pages.data.messageList
              }, () => {
                pages.gotoView()
              })
              // wx.navigateBack({
              //   complete: (res) => {},
              // })
            }).catch(e => {
              console.log(e)
              wx.showToast({
                title: '发送失败~',
                icon: "none"
              })
            })
          }
    
        }
    
      },
      setStatus(status) {
        const free = status === Status.FREE;
        const data = {
          status,
          free,
          talking: status === Status.TALKING,
          waiting: status === Status.WAITING,
        };
        if (free) {
          data.crtTargetId = '';
          data.crtConversationType = 0;
          data.crtMembers = [];
          data.pusher = null;
          data.streams = {};
        }
        this.setData(data);
      },
      async call() {
        var CallType = RongIMLib.VoIPMediaType;
        console.log('222222222222', CallType, this.data.type == 0 ? CallType.MEDIA_AUDIO : CallType.MEDIA_VIDEO)
        this.setData({
          mediaType: this.data.type == 0 ? CallType.MEDIA_AUDIO : CallType.MEDIA_VIDEO
        })
        var params = {
          conversationType: RongIMLib.ConversationType.PRIVATE,
          targetId: this.data.userId,
          inviteUserIds: [this.data.userId],
          mediaType: this.data.type == 0 ? CallType.MEDIA_AUDIO : CallType.MEDIA_VIDEO
          // RongIMLib.VoIPMediaType.MEDIA_VEDIO
        };
        console.log(this.data.type, params)
        wx.showLoading({
          title: '呼叫中...',
        })
        rongCallLib.call(params, (error) => {
          console.log(error)
          wx.hideLoading({
            complete: (res) => { },
          })
    
          if (error) {
            if (error.code == 4) {
              that.setData({
                back: false
              })
              this.hungup(1)
            } else {
              that.setData({
                back: true
              })
              this.hungup()
            }
            // wx.showToast({
            //   title: error.info,
            //   icon:"none"
            // })
            wx.hideLoading({
              complete: (res) => { },
            })
            // wx.showToast({
            //   title: '呼叫失败',
            //   icon:"none"
            // })
            this.audioCtx.pause()
            console.error('发起通话失败', error);
          }
        });
      },
      accept() {
        //  setTimeout(()=>{
        //   console.log(that,that.data)
        //  })
        console.log(this.data.mediaType)
        var params = {
          conversationType: that.data.message.conversationType,
          targetId: that.data.message.targetId,
          mediaType: this.data.mediaType
        };
        console.log(params)
        this.audioCtx.pause()
        rongCallLib.accept(params, function (error) {
          that.setData({
            senderUserId: that.data.message.senderUserId,
            message: null
          })
          if (error) {
            console.error('接听通话失败', error);
            wx.showToast({
              title: '接听通话失败',
              icon: "none",
              success: () => {
                setTimeout(() => {
                  backNav()
    
    
                }, 300)
              }
            })
          }
        });
      },
      async reject() {
        var params = {
          conversationType: RongIMLib.ConversationType.PRIVATE,
          targetId: this.data.senderUserId
        };
        rongCallLib.reject(params, function (error) {
          if (error) {
            console.error('拒绝通话失败', error);
          }
        });
      },
      async hungup(a) {
        wx.hideLoading()
        console.log(11111, '挂断', this.data.type, this.data.userId, this.data.senderUserId)
        var params = {
          conversationType: RongIMLib.ConversationType.PRIVATE,
          targetId: this.data.type ? this.data.userId : this.data.senderUserId
        };
        console.log(212, params)
    
        if (a.currentTarget) {
          this.setData({
            statusCall: false
          })
        }
        rongCallLib.hungup(params, function (error) {
          console.log(error)
          if (a != 1) {
            console.log(error)
    
            setTimeout(() => {
              backNav()
    
            }, 400);
          } else {
            // that.call()
          }
    
          if (error) {
            console.error('挂断通话失败', error);
            // wx.showToast({
            //   title: error.msg,
            //   icon:"none"
            // })
            setTimeout(() => {
              backNav()
    
            }, 400);
          }
        });
      },
      onUnload() {
        // clearTimeout(timer)
        this.audioCtx.pause()
        if (this.data.statusCall) {
          this.hungup(1)
          this.data.message ? console.log("拒接") : console.log("挂断")
          // this.data.message ?  '' :  this.data.pusher ? this.hungup(1) : ''
          that.setData({
            player: null,
            pusher: null
          })
        }
      }
    })  
    

    w

    展开全文
  • Udesk客服系统支持微信小程序、公众号渠道绑定客服,以保证客户多渠道与客服进行沟通。同时,小程序渠道的优点是支持客户关闭小程序后(客户最后一条消息48小时内),客服依旧可以发送消息。在微信小程序及公众号接...

    作者:张振琦

    Udesk客服系统支持微信小程序、公众号渠道绑定客服,以保证客户多渠道与客服进行沟通。同时,小程序渠道的优点是支持客户关闭小程序后(客户最后一条消息48小时内),客服依旧可以发送消息。在微信小程序及公众号接入后,也可以通过后台设置客服的工作时间、留言功能等。

    首先我们必须要注册一个微信小程序,这个大家自己注册就好了。自己开发练习的话,注册一个个人的就可以了。个人的现在不支持认证,企业或者其他组织的可以进行认证。

    小程序注册好后,我们使用管理员账号登陆Udesk在线客服系统进行接入。【管理中心】-【渠道管理】-【小程序】 点击 “接入微信小程序” 按钮

    在这里插入图片描述
    在这里插入图片描述
    Udesk现在支持 公众号第三方平台授权和公众号开发者授权 两种接入方式。公众号第三方平台授权仅支持通过认证的小程序,开发者模式支持未认证及通过认证的小程序。个人注册的微信小程序只能使用公众号开发者授权方式。

    我们选择公众号开发者授权,点击确定。

    在这里插入图片描述
    需要填写小程序的名称、ID以及密钥信息。在微信小程序页面的【开发】-【开发设置】处可以查看。如果生成过密钥,不知道了的话,只能进行重置。

    在这里插入图片描述
    填写信息后点击确定。然后在小程序列表中,找到刚创建的记录,点击后侧的管理进入小程序详情页,查看账号信息。红框的url和token一会要填入到微信小程序的消息推送配置中。
    在这里插入图片描述
    在这里插入图片描述
    下一步要在微信小程序中开启消息推送。在微信小程序页面的开发-开发设置页面的下方。

    在这里插入图片描述
    点击启动按钮,进入信息推送配置页面。

    在这里插入图片描述

    URL(服务器地址)和Token(令牌)填写在Udesk客服系统中小程序账号信息内的内容,在上面的图片中,用红框已经标出。消息加密方式选择安全模式,数据格式选择XML。EncodingAESKey,点击后面的随机生成,并将生成的值,设置回Udesk客服系统中。

    在这里插入图片描述
    URL(服务器地址)和Token(令牌)照着Udesk客服系统生成的写,EncodingAESKey两边保持一致。都保存后,配置就完成了。

    下一篇,我们继续介绍Udesk微信小程序即时通讯开发入门 代码篇的内容。

    展开全文
  • 最近公司开发的项目管理软件需要加入即时通讯功能,后端基于springboot+netty+ws,本人被迫从java开发工程师...前端采用uniapp提供的wsAPI,微信小程序或web端实现思路也是一样的 实现ws登录,心跳,断线重连的功...

    最近公司开发的项目管理软件需要加入即时通讯功能,后端基于springboot+netty+ws,本人被迫从java开发工程师转型成全栈,如果代码有什么问题欢迎指出

    后端功能开发参考了github的开源项目 上链接https://github.com/lmxdawn/him-netty

    前端采用uniapp提供的wsAPI,微信小程序或web端实现思路也是一样的
    实现ws登录,心跳,断线重连的功能

    vuex的功能不用多说 不懂的可以先学习一下

    在项目根目录下创建文件:  /common/websocketStore.js

    注释很全就不多作说明了

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    
    
    export default new Vuex.Store({
    	state: {
    		socketTask: null, // ws链接
    		webSocketPingTimer: null, // 心跳定时器
    		webSocketPingTime: 10000, // 心跳的间隔,当前为 10秒,
    		webSocketReconnectCount: 0, // 重连次数
    		webSocketIsReconnect: true, // 是否重连
    		webSocketIsOpen: true,
    		uid: null, //ws登录userId
    		sid: null, //ws登录token
    		msg: null //接收到的信息
    	},
    	getters: {
    		// 获取接收的信息
    		socketMsgs: state => {
    			return state.msg
    		}
    	},
    	mutations: {
    		//发送http请求登录后设置用户id 用于ws登录
    		setUid(state, uid) {
    			state.uid = uid
    		},
    		//发送http请求登录后设置用户token 用于ws登录
    		setSid(state, sid) {
    			state.sid = sid
    		},
    
    		//初始化ws 用户登录后调用
    		webSocketInit(state) {
    			let that = this
    			// 创建一个this.socketTask对象【发送、接收、关闭socket都由这个对象操作】
    			state.socketTask = uni.connectSocket({
    				url: "ws://196.192.168.169:9001/ws",
    				success(data) {
    					console.log("websocket连接成功");
    				},
    			});
    
    			// ws连接开启后登录验证
    			state.socketTask.onOpen((res) => {
    				console.log("WebSocket连接正常打开中...!");
    				that.commit('webSocketLogin')
    				//开始心跳
    				that.commit('webSocketPing')
    				// 注:只有连接正常打开中 ,才能正常收到消息
    				state.socketTask.onMessage((res) => {
    					console.log("收到服务器内容:" + res.data);
    					state.msg = JSON.parse(res.data)
    				});
    
    			});
    
    			// 链接开启后登录验证
    			state.socketTask.onError((errMsg) => {
    				console.log(errMsg)
    				console.log("ws连接异常")
    				that.commit('webSocketClose')
    			});
    
    			// 链接开启后登录验证
    			state.socketTask.onClose((errMsg) => {
    				console.log(errMsg)
    				console.log("ws连接关闭")
    				that.commit('webSocketClose')
    			});
    
    		},
    
    		webSocketLogin() {
    			let that = this
    			
    			console.log("ws登录");
    			const payload = {
    				uid: that.state.uid,
    				sid: that.state.sid,
    				type: 1
    			};
    			that.commit('webSocketSend', payload);
    			that.state.webSocketIsOpen = true
    		},
    
    		// 断开连接时
    		webSocketClose(state) {
    			let that = this
    			// 修改状态为未连接
    			state.webSocketIsOpen = false;
    			state.webSocket = null;
    			// 判断是否重连
    			if (
    				state.webSocketIsReconnect &&
    				state.webSocketReconnectCount === 0
    			) {
    				// 第一次直接尝试重连
    				that.commit('webSocketReconnect');
    			}
    		},
    
    		// 定时心跳
    		webSocketPing() {
    			let that = this
    			that.state.webSocketPingTimer = setTimeout(() => {
    				if (!that.state.webSocketIsOpen) {
    					return false;
    				}
    				console.log("心跳");
    				const payload = {
    					type: 0
    				};
    				that.commit('webSocketSend', payload);
    				clearTimeout(that.state.webSocketPingTimer);
    				// 重新执行
    				that.commit('webSocketPing');
    			}, that.state.webSocketPingTime);
    		},
    
    		// WebSocket 重连
    		webSocketReconnect(state) {
    			let that = this
    			if (state.webSocketIsOpen) {
    				return false;
    			}
    			console.log("第"+state.webSocketReconnectCount+"次重连")
    			state.webSocketReconnectCount += 1;
    			// 判断是否到了最大重连次数 
    			if (state.webSocketReconnectCount >= 10) {
    				this.webSocketWarningText = "重连次数超限";
    			    return false;
    			}
    			// 初始化
    			console.log("开始重连")
    			that.commit('webSocketInit');
    
    			// 每过 5 秒尝试一次,检查是否连接成功,直到超过最大重连次数
    			let timer = setTimeout(() => {
    				that.commit('webSocketReconnect');
    				clearTimeout(timer);
    			}, 5000);
    		},
    
    		// 发送ws消息
    		webSocketSend(state, payload) {
    			let that = this
    			that.state.socketTask.send({
    				data: JSON.stringify(payload),
    				fail: function(res){
    					console.log("发送失败")
    					that.state.sendMsgStatus = true
    				},
    				success: function(res){
    					console.log("发送成功")
    					that.state.sendMsgStatus = false
    				}
    			})
    		}
    	},
    
    
    	actions: {
    		webSocketInit({
    			commit
    		}, url) {
    			commit('webSocketInit', url)
    		},
    		webSocketSend({
    			commit
    		}, p) {
    			commit('webSocketSend', p)
    		}
    	}
    })
    

    在项目根路径main.js下全局引入js

    import websocket from '@/common/websocketStore.js'
    
    Vue.prototype.$websocket = websocket;

    成功登录的回调方法中设置 uid,sid

    let that = this
    that.$websocket.commit('setUid',res.data.id)
    that.$websocket.commit('setSid',res.data.apiKey)
    that.$websocket.dispatch('webSocketInit');//初始化ws

    在需要接收消息的页面接受并处理消息

    computed: {
        //监听接收到的消息
    	socketMsgs() {
    		return this.$websocket.getters.socketMsgs
        }
    },
    watch: {
    	'socketMsgs': {
            //处理接收到的消息
    		handler: function() {
    		    let that = this
    			console.log("接收到msg")
    			let sMsg = that.socketMsgs
    			console.log(sMsg)
              }
    	}
    },

    app.vue添加代码 实现微信后台心跳失败 重连

    //应用生命周期 onShow	当 uni-app 启动,或从后台进入前台显示
    onShow: function() {
    	let that = this
    	if(that.$websocket.getters.sendMsgStatus){
    		that.$websocket.dispatch('webSocketInit');
    	}
    }

     

    展开全文
  • 开发微信小程序,需要下载《微信开发者工具》,在微信官方文档小程序、工具、下载里面可以找到。安装好后,新建一个小程序项目,填写自己的AppId,会生成一个默认的项目。 默认项目中的范例代码,已经实现了,获取...

    作者:张振琦

    开发微信小程序,需要下载《微信开发者工具》,在微信官方文档小程序、工具、下载里面可以找到。安装好后,新建一个小程序项目,填写自己的AppId,会生成一个默认的项目。

    在这里插入图片描述
    默认项目中的范例代码,已经实现了,获取用户信息。我们这边接着写,将范例代码得到的用户名和用户头像直接传给Udesk。代码非常简单,添加一个按钮即可。

    <view class='todos'>
        <button open-type="contact" size="mini" session-from= "udesk|{{userInfo.nickName}}|{{userInfo.avatarUrl}}">进入客服会话 </button>
    </view>
    

    注意:因为小程序自身限制,暂时无法通过API获取用户信息,所有Udesk采用小程序支持的 session-from 参数实现该功能 session-from 小程序最多支持1000字符串,为了正常使用,该字段的长度尽量<=1000字节 支持客户的系统信息、客户自定义字段、业务标题、业务自定义字段。

    如果不传入其他信息,代码就完成了,非常的简单。开发者工具暂时不支持打开客服会话,要使用真机进行运行。效果如下:

    在这里插入图片描述
    点击“进入客服会话”

    在这里插入图片描述
    Udesk客户系统中效果,可以看到客户来源,客户的昵称和客户的微信头像都会传入进来。

    在这里插入图片描述
    如果需要传入其他的客户信息,可以在button的session-from里继续添加customer信息。在indes.js里的Page的data对象里添加custome_info字段

      customer_info: JSON.stringify({
          "email": "udesk@test.com",
          "description": "微信小程序用户"
      })
    

    我们传入邮箱、描述以及客户等级,我们再添加一个按钮,然后在session-from里追加客户信息

    <view class='todos'>
        <button open-type="contact" size="mini" session-from= "udesk|{{userInfo.nickName}}|{{userInfo.avatarUrl}}|customer^{{customer_info}}">进入客服对话并传入客户信息</button>
    </view>
    

    运行后效果

    在这里插入图片描述
    点击“进入客服对话并传入客户信息”

    在这里插入图片描述
    我们看到,之前对话记录也会保留,同时还支持对上一次对话的评价。查看一下Udesk在线客服系统内的客服窗口

    在这里插入图片描述
    可以看到,邮箱和描述信息都已经传入Udesk系统的客户信息里。

    如果需要传入其他的客户信息或者是业务记录信息,可以查询Udesk官网文档:https://www.udesk.cn/doc/thirdparty/weixin_mini/

    另外Udesk客服系统还提供了更便捷的客服接入方式–H5,Udesk系统客户可以根据不同场景,将h5链接嵌入不同的按钮中。同样也支持在小程序的webview中打开会话页面,我们后续再介绍这种方式。

    展开全文
  • 微信小程序即时通讯源码示例,基于websocket通讯,代码提供测试账号和web端测试工具。
  • 《Udesk微信小程序即时通讯开发入门(一、配置篇)》 《Udesk微信小程序即时通讯开发入门(二、代码篇)》 与原生接入方式不同,使用web-view组件嵌入Udesk即时通讯网页插件只需要微信小程序的管理页面【开发】-...
  • 一个微信小程序同时只能有一个 WebSocket 连接,如果当前已存在一个 WebSocket 连接,会自动关闭该连接,并重新创建一个 WebSocket 连接。基础库版本 1.7.0 及以后,支持存在多个 WebSokcet 连接,每次成功调用 wx....
  • 这几天值班忙的不要不要,人工智能这块看的都是零零散散,今天就来写写小程序的实时通讯吧。小程序端://这个是连接lianjie:function(){var socketOpen = false//注册信息var data = { appid: "77494ad321405fb340e...
  • 适用范围:所有版本微信小程序库 日期 :2019/9/2 前端: <scroll-view style="height:{{height}}vh;width: 100vw;display: flex; position: relative;flex-direction: column;top:{{top?top:0}}px;" ...
  • 微信小程序环信即时聊天
  • 微信小程序-即时通讯踩坑

    千次阅读 2018-09-28 11:56:21
    websocket在测试环境没有问题 IOS生产环境报错 接口404 经测试,安卓没有问题。   https通讯端口是443,不是80   配置环境更改: wss:127.0.0.1:443  
  • 主要为大家详细介绍了微信小程序websocket实现即时聊天功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • globalData: { localSocket: {}, callback: function () {}, exceptionClose: true }, // 初始化socket initSocket() { let that = this; that.globalData.exceptionClose=true;... var cookie = '';...
  • 主要介绍了微信小程序实现即时通信聊天功能的实例代码,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
  • 微信小程序接入腾讯云IM即时通讯(一)

    万次阅读 热门讨论 2018-09-04 17:22:25
    微信小程序接入腾讯云IM即时通讯(一) 第一节先上一波效果图,主要就是列表跟聊天窗口两个页面 1.会话列表页效果图 这里的会话列表页主比较简单:功能主要是未读消息那一块,有坑,后面会讲 2.聊天...
  • 从今天开始,为大家开一门连载课程 ——《微信小程序开发入门》。 2011 年,腾讯公司新的即时通讯社交软件微信正式上线,凭借其新颖的设计风格和良好的使用体验,微信很快就获得了大量的用户。据腾讯公司官方信息,...
  • E聊客户端核心SDK 通讯部分已适配了微信小程序平台,下面分享一下适配过程中的思路与方法。 2.分析: 微信小程序接入要求: 微信小程序主要支持https 与 wss 两种通讯方式,前者用于api 单次请求,后者用于长连接。...
  • 开发的时候这个老表帮了我不少忙,帮忙测试找问题,还别说一开始有很多问题。在这里感谢这位老表。 目前只支持文字聊天。消息列表页面仿微信,chat页面仿QQ,我真是个带天才哈哈哈。 # 背景 在本文开始前,请确保...
  • // 发送文本消息,Web 端与小程序端相同 // 1. 创建消息实例,接口返回的实例可以上屏 let message = tim.createTextMessage({ to: 'user0', conversationType: TIM.TYPES.CONV_C2C, payload: { text: ...
  • 微信小程序开发交流qq群 173683895 承接微信小程序开发。扫码加微信。 接入流程: 初次接触网易云通信IM服务,您可以通过以下产品介绍文档了解我们的产品功能、相关概念、业务限制: 产品简介 主要功能 帐号...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,639
精华内容 2,255
关键字:

微信小程序即时通讯

微信小程序 订阅