推送V3 - Vue + Layim + Websocket 实践笔记

本文记录了作者如何将Vue与Layim集成,实现客服列表跳转、控制台交互、Tab切换管理和右键菜单功能,期间遇到的挑战与解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue + Layim 实践笔记

坦白讲,我接触的前端很少,不喜欢在自己不擅长的地方搞来搞去,Vue中集成了Layim的人工客服Im系统,
出发点是挑战一下自己,经历了将近2个月的时间,项目一波三折,实属不易。

两者的区别

Vue的编程模式MVVM,没有Dom的概念,全局都是针对模型数据进行的操作,有固定的生命周期和路由。

Layim是layUi中的一个组件,是个类似QQ的Im通信工具模板。

实践

1.vue 结构

.
├── build
├── config
│   ├── test1.env.js
│   └── test.env.js
├── index.html
├── package.json
├── README.md
├── robots.txt
├── src
│   ├── App.vue
│   ├── components
│   ├── main.js
│   ├── pages
│   └── router
├── static
│   ├── layim
└── test
    ├── e2e
    └── unit

step 01.引入js文件

用到的Js或者是css文件 在index.html根目录直接引入就可以

<script src="/static/layim/src/layui.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.9.1/lib/theme-chalk/index.css">

step 02.initjs 配置

import complaint from './_complaint';
import channel from './_channel';
let pages = [
    //...
    complaint,
    channel
];

step 03.新建路由

路由js中,路由的Name是 customerLists、customerChatLists,folderName名字拼上pages的key值。

export default {
    folderName: 'customer',
    pages: {
        Lists: {
            title: '客服管理',
            path: '/customer/Lists'
        },
        ChatLists: {
            title: '聊天记录',
            path: '/customer/ChatLists'
        },
        Console: {
            title: '控制台',
            path: '/customer/Console'
        }
    }
}

Vue 生命周期

beforeCreate -> reated -> beforeMount -> mounted -> beforeUpdate -> updated -> activated -> deactivated -> beforeDestroy -> destroyed

Vue的生命周期就是一个执行过程,在指定的生命内执行相对应的函数

实践

1.新页面跳转在客服列表中选取一个按钮跳转到自己的控制台,代码如下:
{
  title: '控制台',
  width: 200,
  type: 'handle',
  btn: {
      manager: {
          text: '控制台',
          handle: this.managerHandle
      }

  },
  width: 140
},

跳转方法:

managerHandle(row){
    this.$router.push({name: 'customerConsole', params: { manager_id: row.manager_id,virtual_uid:row.virtual_uid }})
}
2.控制台页面 Console.vue

在index.html里引入layim.js成功后,其实Js已经引入成功,需要两步初始化Layim的界面。

  • create 方法里接收刚才传递的参数,根据不用的客服ID初始化不用的数据。
created(){
    let params = this.$route.params
    this.$data.virtual_uid = params.virtual_uid;
    this.$data.manager_id = params.manager_id;
},
  • 挂载方法里写layim的需要的业务代码
//getMessage接收消息
//sendMessage发送消息

mounted(){
    layui.use('layim', (layim) => {
        //1 实例化websocket
        this.websocket = new WebSocket(wsuri);
        //2.websocket open方法
        this.websocket.onopen = (event) => {
            //...
        }
        //3.webscoket接收消息的方法
        this.websocket.onmessage = (event) => {
            //getMessage接收消息
            layim.getMessage({
                                username: responseData.body.username
                                ,avatar: responseData.body.avatar
                                ,id: responseData.body.to_uid
                                ,type: "friend"
                                ,content: responseData.body.contents
                              });
        }
        //4.websocket关闭方法
        this.websocket.onclose = (event) => {
            //...
        }
        //5.websocket 错误方法
        this.websocket.onerror = (event) => {
            //...
        }
        
        layim.on('sendMessage', (data)=>{
            var to_uid = data.to.id;
            var contents = data.mine.content;
    
            var msgData = '{"client_type":5,"msg_type":19,"syncstamp":"'+ Date.parse(new Date()) +'","body":{"to_uid":"'+to_uid+'","contents":"'+ contents+'"}}';
            if ( this.websocket.readyState == 1 ) {
                this.websocket.send(msgData);
            }
        }
        
    }
}

3.切换Tab时隐藏和刷新

在Layim使用的过程中使用外部加载Layim,它的Dom结构单独存在,不属于Vue中,所以当切换Tab页就需要隐藏和显示Layim的html结构实体。

watch:{
    $route(route) {
        var layimDom = this.getLayim();
        if(layimDom != null){
            if(route.name == 'customerConsole' || route.name == 'ChatLists' ){
                let params = this.$route.params
                if( params.virtual_uid ){
                    this.$data.virtual_uid = params.virtual_uid;
                    this.$data.manager_id = params.manager_id;

                    layui.use('layim', (layim) => {
                        // 清空好友分组数据缓存
                        this.$destroy();
                        this.initLayim(layim,initMessage);
                    });
                }
                layimDom.removeAttribute('hidden');
            }else{
                layimDom.setAttribute('hidden',true);
            }
        }
    }
},

在多个客服人员切换Tab初始化时,this.$destroy();方法很关键,在这里有一个坑,在Layim重新请求新的接口,但是Layim依然不会初始化数据,销毁了变量才会重新初始化数据。

4.添加右键菜单

路径: static/layim/src/lay/modules/layim.js,代码如下,

/* 绑定好友右击事件 */
$('body').on('mousedown', '.layim-list-friend li ul li', function(e){
	// 清空所有右击弹框
    emptyTips();
    if(3 != e.which) {
    	return;
    }
	// 不再派发事件
	e.stopPropagation();

	var othis = $(this);
    if (othis.hasClass('layim-null')) return;

	// 移除所有选中的样式
    $('.layim-list-friend li ul li').removeAttr("style","");
    // 标注为选中
    othis.css({'background-color':'rgba(0,0,0,.05)'});
    var id = $(this).data('id');
    var im_rid = $(this).data('rid');
	var uid = Date.now().toString(36);
    var im_top = $(this).data('top');
    var im_flag= $(this).data('flag');
	var space_icon = '&nbsp;&nbsp;';
	var space_text = '&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;';
    console.log(im_flag,im_top);
    var top_text = im_top == 2 ? '已经置顶' : '置顶会话';
    var flag_text = '标记会话';

    if(im_top == 2){
        var disabled = 'layui-btn-disabled';
    }else{
        var disabled = '';
    }

    var html = [
       			'<ul id="contextmenu_'+id + '" data-top="'+im_top+'"  data-flag="'+im_flag+'"  data-id="'+id+'" data-rid="'+im_rid+'" data-mold="1">',
       			'<li data-type="menuTop" class="'+disabled+'" ><i class="layui-icon" >&#xe604;</i>'+space_icon+top_text+'</li>',
       			'<li data-type="menuFlag"><i class="layui-icon">&#xe66e;</i>'+space_icon+flag_text +'</li>',
       			'<li data-type="menuDel"><i class="layui-icon" >&#xe640;</i>'+space_icon+'删除好友</li></ul>',
       		].join('');

    layer.tips(html, othis, {
       	tips: 1
       	,time: 0
       	,shift: 5
       	,fix: true
       	,skin: 'ayui-box layui-layim-contextmenu'
       	,success: function(layero){
       	    var liCount = (html.split('</li>')).length;
        	var stopmp = function (e) { stope(e); };
        	layero.off('mousedowm',stopmp).on('mousedowm',stopmp);
        	var layerobj = $('#contextmenu_'+id).parents('.layui-layim-contextmenu');
        	// 移动弹框位置
   			var top = layerobj.css('top').toLowerCase().replace('px','');
   			var left = layerobj.css('left').toLowerCase().replace('px','');
   			top = getTipTop(1, top, liCount);
   			left = 30 + parseInt(left);
   			layerobj.css({'width':'150px', 'left':left+'px', 'top':top+'px'});
   			$('.layui-layim-contextmenu li').css({'padding-left':'18px'});
       	}
	});
});
  • 对菜单上的事件做处理

置顶事件,参考代码:

menuTop: function(){
	    var uid = $(this).parent().data('id');
	    var rid = $(this).parent().data('rid');
        var im_top = $(this).parent().data('top');
        var top = im_top == 1 ? 2 : 1;
        var data = {"id":uid,"im_rid":rid,"im_top":top};

         $.ajax({
          url: '/api/customer/setcustomertop'
          ,type: "GET"
          ,data: data
          ,dataType: 'json'
          ,success: function(res){
                topList({
                  type: 'friend' //或者group
                  ,id: uid //好友或者群组ID
                });
          },
          error:function(res){
          }
        });
    },

标记事件,参考代码:

menuFlag: function(){
    	var uid = $(this).parent().data('id');
    	var rid = $(this).parent().data('rid');
    	var im_flag = $(this).parent().data('flag');
    	var flag = im_flag == 1 ? 2 : 1;
    	var data = {"id":uid,"im_rid":rid,"im_flag":flag};
    	$.ajax({
    	  url: '/customer/setcustomerflag'
    	  ,type: "GET"
    	  ,data: data
    	  ,dataType: 'json'
    	  ,success: function(res){
    	    flagList({
    	      type: 'friend' //或者group
    	      ,id: uid //好友或者群组ID
              ,flag:res.data.im_flag
    	    });
    	  },
    	  error:function(res){
    	      console.log(res);
    	  }
    	});
    },

Ajax成功后返回的Dom操作:

  • 置顶,把当前行的li移动到最第一个元素的位置,更新缓存,更新缓存的一步很重要。
  var topList = function(data){
      var listElem = layimMain.find('.layim-list-'+ data.type);
      var obj = {};
      var html = '';
      if(cache[data.type]){
            if(data.type === 'friend'){
                layui.each(cache.friend, function(index1, item1){
                  layui.each(item1.list, function(index, item){
                    if(data.id == item.id){
                        var li = listElem.find('>li').eq(index1);
                        var list = li.find('.layui-layim-list>li');
                        var ul = listElem.find('.layui-layim-list');
                        li.find('.layui-layim-list>li').eq(index).attr('data-top',2);
                        html += li.find('.layui-layim-list>li').eq(index).prop("outerHTML");
                        li.find('.layui-layim-list>li').eq(index).remove();
                        //从cache的friend里面也删除掉好友
                        //console.log(html,item);
                        ul.prepend(html);
                        var arrTop = cache.friend[index1].list[index];
                        arrTop.im_top = 2;
                        cache.friend[index1].list.splice(index, 1);
                        cache.friend[index1].list.unshift(arrTop);
                        return friend = true;
                    }
                  });
                });
              }
      }
  }

遇到了很多坑,终于凑合着完成了…
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stark张宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值