融云集成一个聊天室页面(vue版本)

  首先,说一下使用情况。因为需求,需要做一个聊天室页面,因为不是专门的点对点聊天,是类似直播,但是是文字直播平台的那种。现在一般的课堂,可能会需要这种。分为2个端,一个是讲师端,一个是用户端。讲师端可能是单独的APP。用户端的页面可能是内嵌到专门的APP,或者是微信公众平台。我这次做的就是一个用户端。讲师端由原生来写,因为需要H5页面能兼容微信还有在手机端都能用。

  然后,说明一下,用了vue的UI框架是vux(下次可能就不会用这个了,其中Scroller竟然不维护了!),用的webpack打包。

  一切就绪,画页面,就是类似直播平台。用vux快速画好。

融云集成一个聊天室页面(vue版本)_第1张图片

先确认需求,1.需要老师能撤回,然后客户端同时将数据从数组删除。2.能播放语音。3.讲师区是下拉加载更多,观众区是上拉加载更多。4.因为ios的输入法有联想词一栏,所以为了能正常输入文字,最后决定使用一个弹框,弹框里面有textarea进行输入文字。5.讲师发的图片。需要客户端能点开,变成大图。(简易版本,暂时无双指放大和双击放大)。6.讲师可以禁言。而客户端需要相应的改样式,禁止输入。差不多就是这些需求。

开始准备:

1.去融云官网注册账号。其实对我们前端来说,只是需要一个appkey。

融云集成一个聊天室页面(vue版本)_第2张图片

2.拿到appkey,就相当于拿到大门钥匙。这个时候可以开始写js文件进行连接了。po上一张文件的内容。

融云集成一个聊天室页面(vue版本)_第3张图片

主要是在views里面的index.vue写的文件。因为页面只有一个,所以我的路由写在了main.js(别说我没写路由,哈哈哈哈)。

好了,接下来我们要先进行连接。那么我写在utils.js就是从官网上扣下来的js文件,顺便封装一下,为了能在index.vue中取值,这边添加了一个回调函数。

export default {
  init(params, callbacks, modules) {
    var appKey = params.appKey;
    var token = params.token;
    // var navi = params.navi || "";

    modules = modules || {};
    var RongIMLib = modules.RongIMLib || window.RongIMLib;
    var RongIMClient = RongIMLib.RongIMClient;
    // var protobuf = modules.protobuf || null;

    var config = {};

    var dataProvider = null;
    var imClient = params.imClient;
    if (imClient) {
      dataProvider = new RongIMLib.VCDataProvider(imClient);
    }
    RongIMLib.RongIMClient.init(appKey, dataProvider, config);
    //语音播放初始化
    RongIMLib.RongIMVoice.init();

    var instance = RongIMClient.getInstance();

    // 连接状态监听器
    RongIMClient.setConnectionStatusListener({
      onChanged: function(status) {
        // console.log(status);
        switch (status) {
          case RongIMLib.ConnectionStatus["CONNECTED"]:
          case 0:
            console.log("连接成功");
            callbacks.getInstance && callbacks.getInstance(instance);
            break;

          case RongIMLib.ConnectionStatus["CONNECTING"]:
          case 1:
            console.log("连接中");
            break;

          case RongIMLib.ConnectionStatus["DISCONNECTED"]:
          case 2:
            console.log("当前用户主动断开链接");
            break;

          case RongIMLib.ConnectionStatus["NETWORK_UNAVAILABLE"]:
          case 3:
            console.log("网络不可用");
            break;

          case RongIMLib.ConnectionStatus["CONNECTION_CLOSED"]:
          case 4:
            console.log("未知原因,连接关闭");
            break;

          case RongIMLib.ConnectionStatus["KICKED_OFFLINE_BY_OTHER_CLIENT"]:
          case 6:
            alert("用户账户在其他设备登录,本机会被踢掉线");
            break;

          case RongIMLib.ConnectionStatus["DOMAIN_INCORRECT"]:
          case 12:
            console.log("当前运行域名错误,请检查安全域名配置");
            break;
        }
      }
    });

    //开始链接
    RongIMClient.connect(
      token,
      {
        onSuccess: function(userId) {
          callbacks.getCurrentUser &&
            callbacks.getCurrentUser({
              userId: userId
            });
          console.log("链接成功,用户id:" + userId);
        },
        onTokenIncorrect: function() {
          console.log("token无效");
        },
        onError: function(errorCode) {
          console.log(errorCode);
        }
      },
      params.userId
    );
    /*
        文档:http://www.rongcloud.cn/docs/web.html#3、设置消息监听器

        注意事项:
            1:为了看到接收效果,需要另外一个用户向本用户发消息
            2:判断会话唯一性 :conversationType + targetId
            3:显示消息在页面前,需要判断是否属于当前会话,避免消息错乱。
            4:消息体属性说明可参考:http://rongcloud.cn/docs/api/js/index.html
        */

    RongIMClient.setOnReceiveMessageListener({
      // 接收到的消息
      onReceived: function(message) {
        // 判断消息类型
        // console.log("新消息: " + message.targetId);
        // console.log(message);
        // 判断消息类型
        switch (message.messageType) {
          case RongIMClient.MessageType.TextMessage:
            // message.content.content => 消息内容
            break;
          case RongIMClient.MessageType.VoiceMessage:
            // message.content.content 格式为 AMR 格式的 base64 码
            break;
          case RongIMClient.MessageType.ImageMessage:
            // message.content.content => 图片缩略图 base64。
            // message.content.imageUri => 原图 URL。
            break;
          case RongIMClient.MessageType.DiscussionNotificationMessage:
            // message.content.extension => 讨论组中的人员。
            break;
          case RongIMClient.MessageType.RichContentMessage:
            // message.content.content => 文本消息内容。
            // message.content.imageUri => 图片 base64。
            // message.content.url => 原图 URL。
            break;
        }
        callbacks.receiveNewMessage && callbacks.receiveNewMessage(message);
      }
    });
  }
};

这样就算把发动机预热好了,这个时候,去我们的index.vue开车了。

在inde.vue里面首先初始化。写个startInit函数,顺手挂载到mounted上。因为初始化,融云是需要我们传token的,这个token是融云的token,那理所当然是需要后台给我们传。我们公司是这样写的,在进入页面的时候,先发2个请求,一个是用户个人信息请求,一个是聊天室详情请求。用户个人信息分为:这个人登录了,我把验证消息给后台,后台给我一个token,我去请求融云。第二种是这个人没有登录,那我这个时候的验证消息是空的,需要后台去判断,然后也给我传一个token,顺便需要传一些其他判断的依据,告诉我这个人没有登录,那我就要让他没办法发言。(扯远了_(:з」∠)_)。拿到token,我就开始初始化。

同时我们要加入聊天室:

startInit(){
        let token = getCookie('tk1')
        var params = {
          appKey: this.appKey,
          token: token,
        };
        const that = this;
        var userId = "";
        var callbacks = {
          getInstance: function (instance) {
            //注册 PersonMessage
            var propertys = ["name", "age", "gender"]; // 消息类中的属性名。
            that.registerMessage("PersonMessage", propertys);
            //注册 ProductMessage
            var propertys = ["price", "title", "desc", "images"]; // 消息类中的属性名。
            that.registerMessage("ProductMessage", propertys);
          },
          getCurrentUser(userInfo) {
            const userId = getCookie('uId')
            document.titie = "链接成功;userid=" + userInfo.userId;
            //加入聊天室
            that.joinChatRoom();
          },
          // 收到融云的最新消息
          receiveNewMessage(message) {
            // 禁言
            if (message.objectName == 'ChatRoom:state') {
              if (message.content.message.content.roomState == "BANNED_TO_POST") {
                $('.weui-input').attr('placeholder', '全员禁言中,不能发言')
              } else {
                let rightControl = getCookie('rightControl')
                if (rightControl == '1020') {
                  $('.weui-input').attr('placeholder', '登录之后才能向讲师提问')
                } else {
                  $('.weui-input').attr('placeholder', '输入您的问题')
                }
              }
            }
            // 自定义消息,判断是否为主持人
            let tag
            if (message.content.extra) {
              tag = JSON.parse(message.content.extra).tag
            }
            let messageBig1 = {};
            // 判断消息类型
            if (!tag) { //没有值是观众
              if (message.objectName == 'RC:TxtMsg') { // 文字信息
                messageBig1.sendTimeStamp = message.sentTime;
                messageBig1.sendUserPortrait = JSON.parse(message.content.extra).portrait;
                messageBig1.sendUserName = JSON.parse(message.content.extra).userName;
                messageBig1.content = message.content.content;
                messageBig1.messageId = JSON.parse(message.content.extra).messageId;
                that.listBox.unshift(messageBig1)
              }
              that.totalCount = parseInt(getCookie('totalCount')) + 1;
              if (that.totalCount >= 999) {
                that.totalCount = "999+"
              }
              let expireDays = 1000 * 60 * 60;
              // 存好讨论区的历史信息数
              setCookie('totalCount', that.totalCount, expireDays)
              // 发言之后会滚到上方(vux方法)
              that.$nextTick(() => {
                let initTop;
                if (
                  document.getElementById("conversationListH").scrollHeight > 300
                ) {
                  initTop = -(document.getElementById("conversationListH").scrollHeight - 300);
                } else {
                  initTop = 0;
                }
                that.$refs.scrollerBottom.reset({
                  bottom: initTop
                });
              });
              if (!that.chatDiscussion) {
                that.hasMsg = true
              }
            }
            else { //有值是主持人
              let messageBig = {
                messageType: {
                  code: null
                }
              };
              messageBig.duration = message.content.duration;
              messageBig.sendTimeStamp = message.sentTime;
              messageBig.sendUserPortrait = JSON.parse(message.content.extra).portrait;
              messageBig.sendUserName = JSON.parse(message.content.extra).userName;
              messageBig.messageId = JSON.parse(message.content.extra).messageId;
              messageBig.content = message.content.content;
              if (message.objectName == 'RC:TxtMsg') {
                messageBig.messageType.code = 1010;
                that.listBox1.push(messageBig)
              } else if (message.objectName == 'RC:VcMsg') {
                messageBig.messageType.code = 1020;
                messageBig.hasRead = false;
                that.listBox1.push(messageBig)
              } else if (message.objectName == 'RC:ImgMsg') {
                messageBig.messageType.code = 1030;
                messageBig.attachmentUrl = message.content.imageUri;
                that.listBox1.push(messageBig)
              }
              that.withdraw();
            }
            // 消息撤回
            if (message.objectName == 'message:recall') {
              that.hasMsg = false
              let messageIds = message.content.message.content.messageIds;
              messageIds.map((item) => {
                that.listBox1 = that.listBox1.filter(e => e.messageId !== item)
                that.withdraw();
              })
            }
          },
        };
        utils.init(params, callbacks)
      },

这个里面要注意一点,就是获取token会是异步,所以,我最后决定,将这个函数,丢在获取个人信息函数里面,这样就不会出现异步了。(大家可以试着写中间件,或者是用其他处理异步方法。)。因为vue是双向绑定,所以只需要我改变数组的值,就会自动出现撤回以及新增数据丢到页面。对了,这个里面有个很重要的方法,我还没说。从案列上扒下来的,老实说,我并不知道为什么要写这个函数。这个函数在初始化里面调用了。

registerMessage(type, propertys) {
        var messageName = type; // 消息名称。
        var objectName = "s:" + type; // 消息内置名称,请按照此格式命名 *:* 。
        var mesasgeTag = new RongIMLib.MessageTag(true, true); //true true 保存且计数,false false 不保存不计数。
        RongIMClient.registerMessageType(
          messageName,
          objectName,
          mesasgeTag,
          propertys
        );
      },

上面差不多就是发言和收到消息了。

最后说一下语音。因为融云语音是用base64位,正常的办法没有办法解读。这个时候要用融云自己的文件。但是融云目前官网上的语音版本,在安卓微信端不能播放语音,但是新版本的还没有更新到官网,我先把这个文件复制上来。

var RongIMLib;
(function(RongIMLib) {
    var RongIMVoice = (function() {
        function RongIMVoice() {}
        RongIMVoice.init = function() {
            if (this.notSupportH5) {
                var div = document.createElement("div");
                div.setAttribute("id", "flashContent");
                document.body.appendChild(div);
                var script = document.createElement("script");
                script.src = (RongIMLib.RongIMClient && RongIMLib.RongIMClient._memoryStore && RongIMLib.RongIMClient._memoryStore.depend && RongIMLib.RongIMClient._memoryStore.depend.voiceSwfobjct) || "//cdn.ronghub.com/swfobject-2.0.0.min.js";
                var header = document.getElementsByTagName("head")[0];
                header.appendChild(script);
                var browser = navigator.appName;
                var b_version = navigator.appVersion;
                var version = b_version.split(";");
                var trim_Version = version[1].replace(/[ ]/g, "");
                script.onload = function() {
                    var swfVersionStr = "11.4.0";
                    var flashvars = {};
                    var params = {};
                    params.quality = "high";
                    params.bgcolor = "#ffffff";
                    params.allowscriptaccess = "always";
                    params.allowScriptAccess = "always";
                    params.allowfullscreen = "true";
                    var attributes = {};
                    attributes.id = "player";
                    attributes.name = "player";
                    attributes.align = "middle";
                    swfobject.embedSWF((RongIMLib.RongIMClient && RongIMLib.RongIMClient._memoryStore && RongIMLib.RongIMClient._memoryStore.depend && RongIMLib.RongIMClient._memoryStore.depend.voicePlaySwf) || "//cdn.ronghub.com/player-2.0.2.swf", "flashContent", "1", "1", swfVersionStr, null, flashvars, params, attributes)
                    var f_version = swfobject.getFlashPlayerVersion();
                    if (f_version['major'] <= 0) {
                        console.error("You haven't installed the flash Player yet.");
                    }
                }
                if (!(browser == "Microsoft Internet Explorer" && trim_Version == "MSIE9.0" || trim_Version == "MSIE10.0")) {
                    script.onreadystatechange = script.onload;
                }
            }
            this.isInit = true
        };
        RongIMVoice.play = function(data, duration) {
            this.checkInit("play");
            var me = this;
            if (me.notSupportH5) {
                if (me.thisMovie().doAction) {
                    me.thisMovie().doAction("init", data);
                } else {
                    setTimeout(function() { me.play(data, duration); }, 500);
                }
            } else {
                var key = data.substr(-10);
                if (this.element[key]) {
                    this.element[key].play();
                }
                me.onCompleted(duration)
            }
        };
        RongIMVoice.stop = function(base64Data) {
            this.checkInit("stop");
            var me = this;
            if (me.notSupportH5) {
                me.thisMovie().doAction("stop")
            } else {
                if (base64Data) {
                    var key = base64Data.substr(-10);
                    if (me.element[key]) {
                        me.element[key].pause();
                        me.element[key].currentTime = 0
                    }
                } else {
                    for (var key_1 in me.element) {
                        me.element[key_1].pause();
                        me.element[key_1].currentTime = 0
                    }
                }
            }
        };
        RongIMVoice.preLoaded = function(base64Data, callback) {
            var str = base64Data.substr(-10),
                me = this;
            if (me.element[str]) {
                callback && callback();
                return
            }
            // if (/android/i.test(navigator.userAgent) && /MicroMessenger/i.test(navigator.userAgent)) {
            if (false) {
                var audio = new Audio();
                audio.src = "data:audio/amr;base64," + base64Data;
                me.element[str] = audio;
                callback && callback()
            } else {
                if (!me.notSupportH5) {
                    if (str in me.element) {
                        return
                    }
                    var audio = new Audio();
                    audio.src = "";
                    var nopromise = {
                        catch: new Function()
                    };
                    (audio.play() || nopromise).catch(function() {}); //解决浏览器报错 The play() request was interrupted by a new load request

                    var blob = me.base64ToBlob(base64Data, "audio/amr");
                    var reader = new FileReader();
                    reader.onload = function(e) {
                        var data = new Uint8Array(e.target.result);
                        var samples = AMR.decode(data);
                        var pcm = PCMData.encode({
                            sampleRate: 8000,
                            channelCount: 1,
                            bytesPerSample: 2,
                            data: samples
                        });
                        audio.src = "data:audio/wav;base64," + btoa(pcm);
                        me.element[str] = audio;
                        callback && callback()
                    };
                    reader.readAsArrayBuffer(blob)
                } else {
                    callback && callback()
                }
            }
        };
        RongIMVoice.onprogress = function() {};
        RongIMVoice.checkInit = function(position) {
            if (!this.isInit) {
                throw new Error("RongIMVoice is not init,position:" + position)
            }
        };
        RongIMVoice.thisMovie = function() {
            return eval("window['player']")
        };
        RongIMVoice.onCompleted = function(duration) {
            var me = this;
            var count = 0;
            var timer = setInterval(function() {
                    count++;
                    me.onprogress();
                    if (count >= duration) {
                        clearInterval(timer)
                    }
                },
                1000);
            if (me.notSupportH5) {
                me.thisMovie().doAction("play")
            }
        };
        RongIMVoice.base64ToBlob = function(base64Data, type) {
            var mimeType;
            if (type) {
                mimeType = {
                    type: type
                }
            }
            base64Data = base64Data.replace(/^(.*)[,]/, "");
            var sliceSize = 1024;
            var byteCharacters = atob(base64Data);
            var bytesLength = byteCharacters.length;
            var slicesCount = Math.ceil(bytesLength / sliceSize);
            var byteArrays = new Array(slicesCount);
            for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
                var begin = sliceIndex * sliceSize;
                var end = Math.min(begin + sliceSize, bytesLength);
                var bytes = new Array(end - begin);
                for (var offset = begin,
                        i = 0; offset < end; ++i, ++offset) {
                    bytes[i] = byteCharacters[offset].charCodeAt(0)
                }
                byteArrays[sliceIndex] = new Uint8Array(bytes)
            }
            return new Blob(byteArrays, mimeType)
        };
        RongIMVoice.notSupportH5 = /Trident/.test(navigator.userAgent);
        RongIMVoice.element = {};
        RongIMVoice.isInit = false;
        return RongIMVoice
    }());
    RongIMLib.RongIMVoice = RongIMVoice;
    if ("function" === typeof require && "object" === typeof module && module && module.id && "object" === typeof exports && exports) {
        module.exports = RongIMVoice
    } else {
        if ("function" === typeof define && define.amd) {
            define("RongIMVoice", [],
                function() {
                    return RongIMVoice
                })
        }
    }
})(RongIMLib || (RongIMLib = {}));

在index.vue中调用play方法。友情提醒,我已经在util.js中初始化语音了,你们注意一下,不然会报错没有初始化哦。

  play(voice) {
        RongIMLib.RongIMVoice.stop();
        if (voice) {
          var duration = voice.length / 1024;    // 音频持续大概时间(秒)
          RongIMLib.RongIMVoice.preLoaded(voice, function () {
            RongIMLib.RongIMVoice.play(voice, duration);
          });
        } else {
          console.error('请传入 amr 格式的 base64 音频文件');
        }
      },

好像要记录的就这些了。有不清楚的,可以告诉我,知无不言哦~(仅限聊天室,我实力也有限QAQ)

你可能感兴趣的:(javascript,vue)