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 = ' ';
var space_text = ' ';
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" ></i>'+space_icon+top_text+'</li>',
'<li data-type="menuFlag"><i class="layui-icon"></i>'+space_icon+flag_text +'</li>',
'<li data-type="menuDel"><i class="layui-icon" ></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;
}
});
});
}
}
}
遇到了很多坑,终于凑合着完成了…