zoukankan      html  css  js  c++  java
  • 使用环信WebIm实现一个客服功能

    介绍环信

    环信 Web IM SDK 为PC/移动 Web 应用,提供完善的即时通信功能开发能力,屏蔽其内部复杂细节,对外提供较为简洁的 API 接口,方便第三方应用快速集成即时通信功能。

    完成功能

    环信的SDK集成和配置基本按照官网给的文档就可以实现。本文仅完成了根据用户名互相发收文字信息的功能,更多功能可在此基础上参考官方文档、API。

    web IM的SDK集成和配置

    首先需要购买环信服务,申请appkey,使用账号和密码申请环信账号。

    // 以下部分是在前端部分的集成和配置

    step1:在官网下载WEIM SDK+体验demo(下载自己所需要的版本即可)

    step2:创建自己的项目,将下载的文件解压,并将sdk目录下的三个文件复制到自己的项目中:

    step3:创建html文件,用于展示聊天界面,在该html中使用使用引入外部js文件的方式,将step2中的三个文件引入进来。

    step4:创建webIMConfig.js文件,用于对webim的配置:

    // 配置
    WebIM.config = {
        xmppURL: 'http://im-api-v2.easemob.com/ws', // xmpp Server地址
        apiURL: 'http://a1.easemob.com', // rest Server地址
        appkey: '', // App key
    
        https: false, // 是否使用https
    
        isHttpDNS: true, // 3.0 SDK支持,防止DNS劫持从服务端获取XMPPUrl、restUrl 
    
        isMultiLoginSessions: false, // 是否开启多页面同步收消息,注意,需要先联系商务开通此功能
    
        isAutoLogin: true, // 自动出席,(如设置为false,则表示离线,无法收消息,需要在登录成功后手动调用conn.setPresence()才可以收消息)
    
        isDebug: false, // 打开调试,会自动打印log,在控制台的console中查看log
    
        autoReconnectNumMax: 2, // 断线重连最大次数
    
        autoReconnectInterval: 2, // 断线重连时间间隔
    
        heartBeatWait: 4500, // 使用webrtc(视频聊天)时发送心跳包的时间间隔,单位ms
    
        delivery: true, // 是否发送已读回执
    
        Host: "",
    }
    
    // 创建连接
    var conn = {};
    conn = WebIM.conn = new WebIM.default.connection({
        appKey: WebIM.config.appkey,
        isHttpDNS: WebIM.config.isHttpDNS,
        isMultiLoginSessions: WebIM.config.isMultiLoginSessions,
        host: WebIM.config.Host,
        https: WebIM.config.https,
        url: WebIM.config.xmppURL,
        apiUrl: WebIM.config.apiURL,
        isAutoLogin: WebIM.config.isAutoLogin,
        heartBeatWait: WebIM.config.heartBeatWait,
        autoReconnectNumMax: WebIM.config.autoReconnectNumMax,
        autoReconnectInterval: WebIM.config.autoReconnectInterval,
        isStropheLog: WebIM.config.isStropheLog,
        delivery: WebIM.config.delivery
    })

    以上内容都是直接从官网文档复制的,注意修改的地方有三个(代码中用红色标出的部分):

    1.appkey:要替换成自己申请的appkey;

    2.Host:配置成自己的服务器的主机号+端口号;

    3.new webIM.default.connection,官网文档中给出的是new webIM.connection,但是报connection未定义的错误,我将webIM打印出来发现这个connection在default下边,所以改成了这样。直接复制代码的话可能会有这类似的问题,可以注意一下。

    同样在step2中引入的webIMSDK3.0.6.js也有同样的错误,我也在webIM后边加上了.default。

    step5:添加回调监听,官网文档里复制的:

    conn.listen({
        onOpened: function(message) { //连接成功回调
            // 如果isAutoLogin设置为false,那么必须手动设置上线,否则无法收消息
            // 手动上线指的是调用conn.setPresence(); 如果conn初始化时已将isAutoLogin设置为true
            // 则无需调用conn.setPresence(); 
            console.log("连接成功")
        },
        onClosed: function(message) {}, //连接关闭回调
        onTextMessage: function(message) {
            console.log(message);
            receiverMessage(message)
        }, //收到文本消息
        onEmojiMessage: function(message) {}, //收到表情消息
        onPictureMessage: function(message) {}, //收到图片消息
        onCmdMessage: function(message) {}, //收到命令消息
        onAudioMessage: function(message) {}, //收到音频消息
        onLocationMessage: function(message) {}, //收到位置消息
        onFileMessage: function(message) {}, //收到文件消息
        onVideoMessage: function(message) {
            var node = document.getElementById('privateVideo');
            var option = {
                url: message.url,
                headers: {
                    'Accept': 'audio/mp4'
                },
                onFileDownloadComplete: function(response) {
                    var objectURL = WebIM.utils.parseDownloadResponse.call(conn, response);
                    node.src = objectURL;
                },
                onFileDownloadError: function() {
                    console.log('File down load error.')
                }
            };
            WebIM.utils.download.call(conn, option);
        }, //收到视频消息
        onPresence: function(message) {
            handlePresence(message);
        }, //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息
        onRoster: function(message) {}, //处理好友申请
        onInviteMessage: function(message) {}, //处理群组邀请
        onOnline: function() {}, //本机网络连接成功
        onOffline: function() {
            console.log("offline")
        }, //本机网络掉线
        onError: function(message) {
            console.log('onError: ', message);
        }, //失败回调
        onBlacklistUpdate: function(list) { //黑名单变动
            // 查询黑名单,将好友拉黑,将好友从黑名单移除都会回调这个函数,list则是黑名单现有的所有好友信息
            console.log(list);
        },
        onRecallMessage: function(message) {}, //收到撤回消息回调
        onReceivedMessage: function(message) {}, //收到消息送达服务器回执
        onDeliveredMessage: function(message) {}, //收到消息送达客户端回执
        onReadMessage: function(message) {}, //收到消息已读回执
        onCreateGroup: function(message) {}, //创建群组成功回执(需调用createGroupNew)
        onMutedMessage: function(message) {} //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员
    });

    实现客服功能

    以上已经将环信配置完毕了,接下来使用环信的API制作客服功能。

    这里我们申请了两个环信账号,一个作为客服,一个作为客户。

    先来看一下效果:

    客服端:

     

    客户端:

    前端样式非常简单,我直接将代码放在后边。注意在进行对话之前要先登录。由于这个项目会接入到商城中,因此不用显式登陆,会写一个接口传入用户名和密码,并自动完成登陆功能。

    以下讲述几个制作中的难点:

    难点1:接收和发送的信息展示在页面里。

    为了让消息交替错落的展示,每一条消息都是一个<div>块级元素,这样就能自动完成换行。当接收或者发送消息的时候,在页面中插入一个相应的<div>。

    难点2:当消息过多的时候,能够自动向上滚动,以显示最新消息。

    使用的scrollTop动画来完成。给scrollTop设置一个很大的值,这样页面就能总是能滑动显示出最新的消息。

    难点3:客服端的侧边显示来自不同用户的消息,并且显示消息未读小红点。

    当收到信息时,我们收到的消息中包含一个from字段,表示消息来自哪个用户,根据这个字段将消息插入到对应的对话界面中。当当前显示的对话界面不是该用户时,就要出现小红点提示有未读信息。

    添加一个全局变量talkTo,其中保存当前对话方的用户名,表示当前在与谁对话。通过点击消息列表中的用户来改变这个变量的值。我们用这个值来完成消息的发送等功能。同时也可以用这个值来判断出当前展示的对话界面,从而判断是否显示消息未读的小红点。

    难点4:显示与不同用户的交流界面

    使用<div>包裹用户名+用户对话界面,并给这个div添加带有用户信息的独特id。默认这个<div>是display:none。当通过侧边列表项选择对话方时,将对应的<div>设置为display:block

    在官方文档中提供了消息的发送和接收的示例函数,可以直接复制过来再稍加改动,添加一点显示方面的代码就可以了。

    代码

    客户端和客服端代码基本一致,只是客户端没有旁边的列表,也不用显示不同的对话界面,所以html和css上稍有差异,js中对消息的处理和显示也稍简单一点。

    他们都公用环信的配置、初始化和监听函数,尽量减少两者在监听中的差异,不同之处在各自的js中处理。

    项目代码如下:

    客服端:

    HTML

    <!DOCTYPE html>
    <!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
    <!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
    <!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
    <!--[if gt IE 8]><!-->
    <html>
    <!--<![endif]-->
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="index.css">
    </head>
    
    <body>
        <!-- 消息列表 -->
        <ul class="sidebar" id="roster" onclick="showDialogist()">
            <div>消息列表</div>
        </ul>
        <div class="message">
            <!-- 信息显示 -->
            <div class="display" id="display">
                <!-- 默认对话界面 -->
                <div>
                    <div class="dialogist">欢迎来到客户服务平台</div>
                    <div class="content">
                    </div>
                </div>
            </div>
            <!-- 信息编辑 -->
            <div class="edit">
                <textarea id="sendContent"></textarea>
                <button onclick="sendMessage()">发送信息</button>
            </div>
        </div>
    
        <script type='text/javascript' src='lib/jquery-3.2.1.js'></script>
        <script type='text/javascript' src='lib/webimSDK3.0.6.js'></script>
        <script type='text/javascript' src='lib/EMedia_x1v1.js'></script>
        <!-- <script type='text/javascript' src='lib/EMedia_sdk-dev.js'></script> -->
        <script type='text/javascript' src='WebIMConfig.js'></script>
        <script type='text/javascript' src='index.js'></script>
    </body>
    
    </html>    

    CSS

    body {
        margin: 0;
    }
    
    
    /* 侧边栏 */
    
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        width: 250px;
        height: 100%;
        background: #444;
        margin: 0;
        padding: 0;
    }
    
    .sidebar div {
        width: 100%;
        text-align: center;
        color: #FFF;
        font-size: 18px;
        font-weight: bold;
        margin-top: 16px;
        margin-bottom: 19px;
    }
    
    .sidebar li {
        padding: 18px;
        display: flex;
        align-items: center;
        position: relative;
        cursor: pointer;
    }
    
    .sidebar img {
        width: 45px;
        height: 45px;
        margin-right: 16px;
    }
    
    .sidebar .name {
        margin-right: 5px;
        color: #c8c9cc;
        font-size: 18px;
        width: 130px;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    
    .sidebar .redIcon {
        width: 12px;
        height: 12px;
        background-color: red;
        border-radius: 50%;
        position: absolute;
        top: 3px;
        left: 56px;
    }
    
    
    /* 侧边栏标签鼠标移入样式 */
    
    .sidebar li:hover {
        background-color: #666;
        color: #FFF;
    }
    
    
    /* 侧边栏标签选中样式 */
    
    .sidebar-selected,
    .sidebar .sidebar-selected:hover {
        background: #FFF;
    }
    
    .sidebar-selected .name {
        color: #333;
    }
    
    
    /* 信息部分 */
    
    .message {
        margin-left: 250px;
        width: calc(100% - 250px);
    }
    
    
    /* 信息显示部分 */
    
    .message .display {
        width: 100%;
    }
    
    .message .display .dialogist {
        border-bottom: 1px solid #333;
        padding: 16px;
        font-size: 20px;
    }
    
    .message .display .content {
        width: 100%;
        height: 385px;
        padding-bottom: 16px;
        overflow-y: scroll;
    }
    
    .message .display .head {
        width: 30px;
        height: 30px;
        border-radius: 50%;
        margin: 5px 16px;
    }
    
    .message .display .time {
        color: #bbb;
        font-size: 12px;
        margin: 5px 8px;
        display: inline-block;
    }
    
    .message .display .textBox {
        max-width: 250px;
        min-height: 20px;
        border-radius: 5px;
        padding: 8px 16px;
        box-shadow: 0 0 2px 1px #ddd;
        word-break: break-all;
    }
    
    .message .display .left {
        display: flex;
        align-items: flex-start;
        justify-content: flex-start;
        padding-left: 10px;
        margin-top: 16px;
    }
    
    .message .display .right {
        display: flex;
        align-items: flex-start;
        justify-content: flex-end;
        padding-right: 10px;
        margin-top: 16px;
    }
    
    .message .display .left .time {
        text-align: left;
    }
    
    .message .display .right .time {
        text-align: right;
    }
    
    .message .display .left .textBox {
        background-color: yellowgreen;
    }
    
    .message .display .right .textBox {
        background-color: #fff;
    }
    
    
    /* 信息编辑部分 */
    
    .message .edit {
        border-top: 1px solid #333;
        height: 230px;
        width: 100%;
    }
    
    .message .edit textarea {
        width: calc(100% - 20px);
        height: calc(100% - 70px);
        resize: none;
        padding: 10px;
        font-size: 16px;
        border: 0;
        outline: none;
    }
    
    .message .edit button {
        float: right;
        background-color: #333;
        border: 0;
        color: #fff;
        padding: 8px 16px;
        cursor: pointer;
        margin-right: 30px;
    }
    
    .message .edit button:hover {
        background-color: #666;
    }

    JS

    var username = "";//客服账号
    var password = "";//客服密码
    var talkTo = "";//当前对话方
    var curTalkList = new Set();//当前对话用户列表
    
    window.onload = function() {
        this.login();
    }
    
    // 登陆
    function login() {
        //登陆API
        var options = {
            apiUrl: WebIM.config.apiURL,
            user: username,
            pwd: password,
            appKey: WebIM.config.appkey
        };
        conn.open(options);
    }
    
    //   退出
    function logOut() {
        conn.close();
    }
    
    // 单聊发送文本信息
    //官方文档示例函数,稍加修改,与发送消息显示函数连接到一起
    function sendPrivateText(myTime, content) {
        var id = conn.getUniqueId(); // 生成本地消息id
        var msg = new WebIM.default.message('txt', id); // 创建文本消息
    
        msg.set({
            msg: content, // 消息内容
            to: talkTo, // 接收消息对象(用户id)
            roomType: false,
            ext: {
                "time": myTime
            }, //扩展消息
            success: function(id, serverMsgId) {
                console.log('send private text Success');
            }, // 对成功的相关定义,sdk会将消息id登记到日志进行备份处理
            fail: function(e) {
                    console.log("Send private text error");
                } // 对失败的相关定义,sdk会将消息id登记到日志进行备份处理
        });
        msg.body.chatType = 'singleChat';
        conn.send(msg.body);
    };
    
    // 显示已发送信息
    function sendMessage() {
        var myTime = new Date().toLocaleString();
        var content = document.getElementById("sendContent").value;
        document.getElementById("sendContent").value = "";
        $("#" + talkTo + "_i").children(".content").append('<div class="right">' +
            '<div class="time">' + myTime + '</div>' +
            '<div class="textBox">' + content + '</div>' +
            '<img class="head" src="./img/logo.png"></div>')
        $("#" + talkTo + "_i").children(".content").animate({ scrollTop: 9999 }, 200)
        sendPrivateText(myTime, content);
    }
    
    // 显示接收到的信息
    // 客服端:在列表中显示消息
    function receiverMessage(message) {
        var name = message.from,
            time = message.ext.time,
            content = message.data;
    
        if (curTalkList.has(name)) {
            $("#" + name + "_i").children(".content").append('<div class="left">' +
                '<img class="head" src="./img/logo.png">' +
                '<div class="textBox">' + content + '</div>' +
                '<div class="time">' + time + '</div></div>')
    
            if (talkTo == name) {
                $("#" + name).children(".redIcon").attr("style", "display:block;");
            }
        } else {
            // 添加消息列表中的信息
            curTalkList.add(name);
            $("#roster").append('<li id="' + name + '"><div class="redIcon"></div><img src="./img/logo.png"><div class="name">' + name + '</div></li>');
    
            // 添加消息显示框中的信息
            $("#display").append('<div style="display:none;" id="' + name + '_i"><div class="dialogist">' + name + '</div>' +
                '<div class="content">' +
                '<div class="left">' +
                '<img class="head" src="./img/logo.png">' +
                '<div class="textBox">' + content + '</div>' +
                '<div class="time">' + time + '</div></div>' +
                '</div></div>')
        }
    
        $("#" + name + "_i").children(".content").animate({ scrollTop: 9999 }, 200)
    }
    
    // 点击列表中的用户,显示对应的对话界面
    function showDialogist() {
        $("#roster").children("li").attr("class", "");
        $("#display").children("div").attr("style", "display:none;");
    
        var cur = event.target.closest("li").id;
        talkTo = cur;
        $("#" + cur).attr("class", "sidebar-selected");
        $("#" + cur).children(".redIcon").attr("style", "display:none;");
        $("#" + cur + "_i").attr("style", "display:block;");
    }

    客户端:

    HTML

    <!DOCTYPE html>
    <!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
    <!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
    <!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
    <!--[if gt IE 8]><!-->
    <html>
    <!--<![endif]-->
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title></title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="client.css">
    </head>
    
    <body>
        <div class="message">
            <!-- 信息显示 -->
            <div class="display">
                <div class="dialogist">商城-客服</div>
                <div class="content" id="display">
                </div>
            </div>
            <!-- 信息编辑 -->
            <div class="edit">
                <textarea id="sendContent"></textarea>
                <button onclick="sendMessage()">发送信息</button>
            </div>
        </div>
    
        <script type='text/javascript' src='lib/jquery-3.2.1.js'></script>
        <script type='text/javascript' src='lib/webimSDK3.0.6.js'></script>
        <script type='text/javascript' src='lib/EMedia_x1v1.js'></script>
        <!-- <script type='text/javascript' src='lib/EMedia_sdk-dev.js'></script> -->
        <script type='text/javascript' src='WebIMConfig.js'></script>
        <script type='text/javascript' src='client.js'></script>
    </body>
    
    </html>

    CSS

    body {
        margin: 0;
    }
    
    
    /* 信息部分 */
    
    .message {
        width: 100%;
    }
    
    
    /* 信息显示部分 */
    
    .message .display {
        width: 100%;
    }
    
    .message .display .dialogist {
        height: 27px;
        border-bottom: 1px solid #333;
        padding: 16px;
        font-size: 20px;
    }
    
    .message .display .content {
        width: 100%;
        height: 385px;
        padding-bottom: 16px;
        overflow-y: scroll;
    }
    
    .message .display .head {
        width: 30px;
        height: 30px;
        border-radius: 50%;
        margin: 5px 16px;
    }
    
    .message .display .time {
        color: #bbb;
        font-size: 12px;
        margin: 5px 8px;
        display: inline-block;
    }
    
    .message .display .textBox {
        max-width: 250px;
        min-height: 20px;
        border-radius: 5px;
        padding: 8px 16px;
        box-shadow: 0 0 2px 1px #ddd;
        word-break: break-all;
    }
    
    .message .display .left {
        display: flex;
        align-items: flex-start;
        justify-content: flex-start;
        padding-left: 10px;
        margin-top: 16px;
    }
    
    .message .display .right {
        display: flex;
        align-items: flex-start;
        justify-content: flex-end;
        padding-right: 10px;
        margin-top: 16px;
    }
    
    .message .display .left .time {
        text-align: left;
    }
    
    .message .display .right .time {
        text-align: right;
    }
    
    .message .display .left .textBox {
        background-color: yellowgreen;
    }
    
    .message .display .right .textBox {
        background-color: #fff;
    }
    
    
    /* 信息编辑部分 */
    
    .message .edit {
        border-top: 1px solid #333;
        height: 205px;
        width: 100%;
    }
    
    .message .edit textarea {
        width: calc(100% - 20px);
        height: calc(100% - 70px);
        resize: none;
        padding: 10px;
        font-size: 16px;
        border: 0;
        outline: none;
    }
    
    .message .edit button {
        float: right;
        background-color: #333;
        border: 0;
        color: #fff;
        padding: 8px 16px;
        cursor: pointer;
        margin-right: 30px;
    }
    
    .message .edit button:hover {
        background-color: #666;
    }

    JS

    // 登陆
    var username = "";//客户用户名
    var password = "";//客户密码
    var talkTo = "";//客服用户名
    window.onload = function() {
        this.login();
    }
    
    function login() {
        var options = {
            apiUrl: WebIM.config.apiURL,
            user: username,
            pwd: password,
            appKey: WebIM.config.appkey
        };
        conn.open(options);
    }
    
    //   退出
    function logOut() {
        conn.close();
    }
    
    // 单聊发送文本信息
    function sendPrivateText(myTime, content) {
        var id = conn.getUniqueId(); // 生成本地消息id
        var msg = new WebIM.default.message('txt', id); // 创建文本消息
    
        msg.set({
            msg: content, // 消息内容
            to: talkTo, // 接收消息对象(用户id)
            roomType: false,
            ext: {
                "time": myTime
            }, //扩展消息
            success: function(id, serverMsgId) {
                console.log('send private text Success');
            }, // 对成功的相关定义,sdk会将消息id登记到日志进行备份处理
            fail: function(e) {
                    console.log("Send private text error");
                } // 对失败的相关定义,sdk会将消息id登记到日志进行备份处理
        });
        msg.body.chatType = 'singleChat';
        conn.send(msg.body);
    };
    
    // 显示已发送信息
    function sendMessage() {
        var myTime = new Date().toLocaleString();
        var content = document.getElementById("sendContent").value;
        document.getElementById("sendContent").value = "";
        $("#display").append('<div class="right">' +
            '<div class="time">' + myTime + '</div>' +
            '<div class="textBox">' + content + '</div>' +
            '<img class="head" src="./img/logo.png"></div>')
        $("#display").animate({ scrollTop: 9999 }, 200)
        sendPrivateText(myTime, content);
    }
    
    // 显示接收到的信息
    function receiverMessage(message) {
        var time = message.ext.time,
            content = message.data;
        $("#display").append('<div class="left">' +
            '<img class="head" src="./img/logo.png">' +
            '<div class="textBox">' + content + '</div>' +
            '<div class="time">' + time + '</div></div>')
        $("#display").animate({ scrollTop: 9999 }, 200)
    }
  • 相关阅读:
    RegularExpressions(正则表达式)
    IntelliJ Idea工具使用
    JavaScript总结
    Ajax技术基础
    配置Nginx作为反向代理服务器
    SpringBoot下的Dubbo和Zookeeper整合
    springSecurity初识练气初期
    Spring Security拦截器加载流程分析练气中期
    Spring Security认证流程分析练气后期
    springBoot整合spring security+JWT实现单点登录与权限管理前后端分离筑基中期
  • 原文地址:https://www.cnblogs.com/cff2121/p/12582397.html
Copyright © 2011-2022 走看看