Authored by 周少峰

Merge branch 'feature/cusService-v2.0'

... ... @@ -20,7 +20,13 @@ const index = (req, res, next) => {
let userAgent = req.headers['user-agent'];
let unSupport = reg.test(userAgent);
let encryptedUid = aes.encryptionUid(req.user.uid);
let domains = common.domains;
let imCs = domains.imCs;
let imSocket = domains.imSocket;
let data = {
imCs,
imSocket,
encryptedUid,
layout: false
};
... ... @@ -96,9 +102,9 @@ const saveEval = (req, res, next) => {
* @param next
*/
const queryReason = (req, res, next) => {
const cvId = req.body.conversationId;
const type = req.body.type;
clientApi.queryReason(cvId)
clientApi.queryReason(type)
.then(result => {
res.json(result);
}).catch(next);
... ...
... ... @@ -23,7 +23,7 @@ let urls = {
lastTen: '/api/order/queryLastTenOrder',
leaveMsg: '/api/leavemessage/saveLeavemessage',
msgHistory: '/api/conversationMessage/pageList',
evalReason: '/api/evalute/queryReasonByConversationId'
evalReason: '/api/evalute/queryReasonBySettingType'
};
/**
... ... @@ -91,9 +91,9 @@ const saveEval = (params) => {
* @param cvId
* @returns {*}
*/
const queryReason = (cvId) => {
const queryReason = (type) => {
const params = {
conversationId: cvId
type
};
return api.post(urls.evalReason, params);
... ...
... ... @@ -60,6 +60,12 @@
{{> connect-fail}}
<input name="encryptedUid" type="text" type="hidden" value={{encryptedUid}}>
<script>
var gDomains = {
imCs: '{{{imCs}}}',
imSocket: '{{{imSocket}}}'
}
</script>
{{#if devEnv}}
<input name="assetsPrefix" type="text" type="hidden" value="">
<script src="//{{devHost}}:5002/libs.js"></script>
... ...
<div class="modal fade make-eval" id="makeEvaluation" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal fade make-eval" id="makeEvaluation" tabindex="-1" role="dialog"
aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog">
<div class="modal-content">
<div class="cus-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<button type="button" class="close">×</button>
<span class="title">请对此次服务进行评价</span>
</div>
<div class="cus-body">
... ... @@ -22,7 +23,7 @@
</div>
</div>
<div class="cus-footer">
<button class="btn dark submit">提交</button>
<button class="btn dark submit">提交并关闭</button>
<input type="hidden" name="promoter" value="1">
</div>
</div>
... ...
{
"name": "yohobuy-node",
"version": "5.5.28",
"version": "5.5.30",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ...
... ... @@ -8,14 +8,17 @@
'use strict';
var $ = require('yoho-jquery'),
uuid = require('uuid'),
emojiMap = require('./emoji-map'),
editArea = require('./edit-area'),
serviceApi = require('./service-api'),
view = require('./view'),
broswer = require('./broswer'),
utility = require('./utility'),
socketChat = require('./socket-chat'),
socketConf = require('./socket-config');
util = require('./utility'),
send = require('./socket/send'),
edit = require('./socket/edit'),
ClipPaste = require('./clip-paste'),
serviceApi = require('./service-api'),
receive = require('./socket/receive'),
socketChat = require('./socket/chat'),
socketConf = require('./socket/config'),
processInfo = require('./view').processInfo;
var allRTs,
endTime,
... ... @@ -23,14 +26,10 @@ var allRTs,
encryptedUid,
socketConfCM,
assetsPrefix,
cursorPosition,
configDomains,
titleInterval,
$html = $('html'),
$window = $(window),
$document = $(document),
$msgList = $('.msg-list'),
docTitle = document.title,
$msgEdit = $('.msg-edit'),
$leaveMsg = $('#leaveMsg'),
$close = $('.header .close'),
... ... @@ -38,7 +37,6 @@ var allRTs,
$sendImgInput = $('#sendImg'),
$history = $('.about-his.has-his'),
$msgArea = $('.msg-edit .msg-area'),
$makeEvalModal = $('#makeEvaluation'),
$rightHeadTab = $('.right-head .tab'),
$encryptedUid = $('input[name=encryptedUid]'),
$assetsPrefix = $('input[name=assetsPrefix]'),
... ... @@ -47,18 +45,7 @@ var allRTs,
var tipTpl = require('hbs/service/tip.hbs'),
csTpl = require('hbs/service/cs-msg.hbs'),
cusTpl = require('hbs/service/cus-msg.hbs'),
robotTpl = require('hbs/service/robot-msg.hbs'),
disTpl = require('hbs/service/discontent-row.hbs');
var processInfo = {
scrollLoad: false,
loadingHistory: false,
hasMore: true,
manual: false,
completeClose: false, // 评价完成后关闭
promoter: 1, // 评论发起者 1:客户自己 2:客服
savedEval: false // 是否保存过评论
};
robotTpl = require('hbs/service/robot-msg.hbs');
require('bootstrap');
require('../common');
... ... @@ -72,12 +59,17 @@ encryptedUid = $encryptedUid.val();
assetsPrefix = $assetsPrefix.val();
socketConfCM = socketConf.conversationMessage;
socketConfCM.encryptedUid = encryptedUid;
processInfo.encryptedUid = encryptedUid;
// 原始配置信息用于重新连线
originConf = JSON.parse(JSON.stringify(socketConf));
// 页面初始化
function pageInit() {
var _loadPage = function() {
// 客服评价
var evalView = new view.Evaluate();
/**
* 添加新的消息
*/
... ... @@ -87,48 +79,6 @@ function pageInit() {
}
/**
* 表情设置
* @param e
*/
function setEmoji(e) {
var i,
pos,
start,
end,
newVal,
textDom,
emojiText,
tag = $(e.target),
area = $('.msg-area'),
val = area.val(),
len = emojiMap.length,
emojiId = tag.data('id'),
comp = $('.emoji-component');
for (i = 0; i < len; i++) {
if (emojiMap[i].file === emojiId.toString()) {
emojiText = emojiMap[i].text;
}
}
if (cursorPosition) {
start = val.substring(0, cursorPosition);
end = val.substring(cursorPosition);
newVal = [start, emojiText, end].join('');
} else {
newVal = [val, emojiText].join('');
}
comp.hide();
area.val(newVal);
cursorPosition += emojiText.length;
textDom = area[0];
pos = cursorPosition ? cursorPosition : newVal.length;
broswer.setCursor(textDom, pos);
}
/**
* 系统通知
* @param tip 具体消息
*/
... ... @@ -168,42 +118,24 @@ function pageInit() {
var $countdown = $('.countdown');
$countdown.parents('.list-item').remove();
if (titleInterval) {
clearInterval(titleInterval);
document.title = docTitle;
}
util.removeBlinkAlert();
}
/**
* 处理发送消息
* @param e
* @param msgType
* @param msgContent
*/
function sendMessage(e, msgType, msgContent) {
function sendMessage() {
var $area = $('.msg-area'),
msg = $area.val().trim();
// 发送前共通处理
beforeSendMsg();
if (!msgContent) {
if (!msg) {
return;
}
socketConfCM.type = allRTs.CU_SEND;
socketConfCM.uuid = uuid.v4();
socketConfCM.chatMessage.content = msg;
socketConfCM.chatMessage.type = msgType || 1;
socketChat.send(socketConfCM);
$area.val('');
} else {
socketConfCM.type = allRTs.CU_SEND;
socketConfCM.uuid = uuid.v4();
socketConfCM.chatMessage.type = msgType || 1;
socketConfCM.chatMessage.content = msgContent;
socketChat.send(socketConfCM);
if (!msg) {
return;
}
send.text(msg);
$area.val('');
}
/**
... ... @@ -219,14 +151,6 @@ function pageInit() {
}
/**
* 人工客服
*/
function manualService() {
socketConfCM.type = allRTs.MANUAL_SERVICE;
socketChat.send(socketConfCM);
}
/**
* 正在进行人工会话
*/
function csChatting(message) {
... ... @@ -258,53 +182,54 @@ function pageInit() {
// 系统通知
systemTip(message.content);
processInfo.savedEval = false;
processInfo.manual = false;
// 显示人工客服
editArea.setIcons({
edit.setIcons({
'manual-service': true
});
}
/**
* 人工链接
* @param msgType 内部消息类型
* @param message 内部消息对象
* @param rec 接收对象
* @private
*/
function linkSuccess(msgType, message) {
function linkSuccess(rec) {
var OUT_SERVICE = 0,
LINE_UP = 1,
MANUAL_SERVICE = 2,
ADMIN_MANUAL_SERVICE = 3;
var OUT_SERVICE = 0, // 0 没上班
LINE_UP = 1, // 1 需要排队
MANUAL_SERVICE = 2, // 2 接入人工
ADMIN_MANUAL_SERVICE = 3; // 3 管理员分配
var message = rec.chatMessage;
var msgType = message.type;
var dom,
tipText;
switch (msgType) {
case OUT_SERVICE: // 0是没上班
tipText = message.content +
'您也可以选择<a class="leave-msg">留言</a>';
case OUT_SERVICE:
tipText = receive.leaMsgTip(rec);
dom = tipTpl({
content: tipText
});
break;
case LINE_UP: // 1是需要排队
tipText = message.content +
'您也可以选择<a class="leave-msg">留言</a>';
case LINE_UP:
tipText = receive.leaMsgTip(rec);
dom = tipTpl({
content: tipText
});
// 隐藏人工
editArea.setIcons({
edit.setIcons({
'manual-service': false
});
break;
case MANUAL_SERVICE: // 2是接入人工成功
case MANUAL_SERVICE:
tipText = message.content;
dom = tipTpl({
content: tipText
... ... @@ -314,7 +239,7 @@ function pageInit() {
processInfo.manual = true;
// 显示评价&隐藏人工
editArea.setIcons({
edit.setIcons({
emoji: true,
image: true,
evaluate: true,
... ... @@ -322,17 +247,16 @@ function pageInit() {
});
break;
case ADMIN_MANUAL_SERVICE: // 3是管理员分配客服成功
case ADMIN_MANUAL_SERVICE:
tipText = message.content;
dom = tipTpl({
content: tipText
});
// 接入人工客服需要评价
processInfo.manual = true;
// 显示评价&隐藏人工
editArea.setIcons({
edit.setIcons({
emoji: true,
image: true,
evaluate: true,
... ... @@ -357,46 +281,15 @@ function pageInit() {
}
/**
* 返回表情路径处理
* @param text 文本
* @private
*/
function emojiPrefix(text) {
if (typeof text === 'string') {
return text.replace(/src="(\d{3}).gif"/g, 'src="' + assetsPrefix + '/img/service/emoji/$1.gif"');
}
return text;
}
/**
* 处理客户消息
* @private
*/
function handleCusMsg(rec, msgType, message) {
var dom,
image,
mode = 2,
width = 100,
height = 100;
message.newContent = emojiPrefix(message.newContent);
rec.userHead = utility.autoProtocol(rec.userHead);
// 用户头像处理
if (!rec.userHead) {
rec.userHead = assetsPrefix + socketConf.defaultUserHead;
} else {
rec.userHead = rec.userHead
.replace(/\{mode\}/, mode)
.replace(/\{width\}/, width)
.replace(/\{height\}/, height);
}
image;
// 图片添加标签
if (msgType === 2) {
message.content = utility.autoProtocol(message.content);
message.newContent = '<img class="img-msg" src="' + message.content + '">';
dom = cusTpl({
userHead: rec.userHead,
userName: rec.userName,
... ... @@ -447,12 +340,7 @@ function pageInit() {
var dom,
image;
rec.csHead = utility.autoProtocol(rec.csHead);
message.newContent = emojiPrefix(message.newContent);
if (msgType === 2) {
message.content = utility.autoProtocol(message.content);
message.newContent = '<img class="img-msg" src="' + message.content + '">';
dom = csTpl({
csHead: rec.csHead,
csName: rec.csName,
... ... @@ -478,53 +366,6 @@ function pageInit() {
}
/**
* 显示评价弹框
*/
function showEvalModal(cptClose) {
var $evalModal = $('#makeEvaluation');
// 评价完成后关闭
processInfo.completeClose = cptClose;
// 没有接入人工
if (!processInfo.manual) {
return;
}
// 评价原因
function discontentHtml(len, data) {
var i,
dom = '';
for (i = 0; i < len; i = i + 2) {
dom += disTpl({
id1: data[i].id,
id1Content: data[i].content,
id2: data[i + 1] && data[i + 1].id,
id2Content: data[i + 1] && data[i + 1].content
});
}
$evalModal.find('.discontent').empty().append(dom);
}
// 拉取评价原因
serviceApi.reason({
conversationId: socketConfCM.conversationId
})
.done(function(res) {
var data = res.data,
len = data.length;
if (res && res.code === 200) {
// 评价原因
len && discontentHtml(len, data);
}
$evalModal.modal('show');
});
}
/**
* 处理收到消息
*/
function getMessage(rec) {
... ... @@ -535,19 +376,11 @@ function pageInit() {
isHidden = broswer.tabIsHidden(),
allTypes = socketConf.recType;
// 删除上个定时器
if (titleInterval) {
clearInterval(titleInterval);
}
// 提醒
isHidden && util.addBlinkAlert();
if (isHidden) {
titleInterval = setInterval(function() {
document.title = '您有新消息!';
setTimeout(function() {
document.title = docTitle;
}, 300);
}, 600);
}
// 预处理
receive.preProcess(rec);
switch (recType) {
case allTypes.ENTER:
... ... @@ -555,7 +388,7 @@ function pageInit() {
break;
case allTypes.LINK_SUCCESS:
linkSuccess(msgType, message);
linkSuccess(rec);
break;
case allTypes.IN_QUNEUE:
... ... @@ -585,10 +418,9 @@ function pageInit() {
break;
case allTypes.EVAL_INVITE:
// 客服发起
if (!processInfo.savedEval) {
processInfo.promoter = 2;
showEvalModal();
evalView.open();
}
break;
... ... @@ -598,7 +430,7 @@ function pageInit() {
case allTypes.OP_LEAVE:
socketChat.clear();
editArea.setIcons({
edit.setIcons({
emoji: false,
image: false
});
... ... @@ -607,7 +439,7 @@ function pageInit() {
case allTypes.OFFLINE:
socketChat.clear();
editArea.setIcons({
edit.setIcons({
emoji: false,
image: false
});
... ... @@ -652,14 +484,14 @@ function pageInit() {
},
onOpen: function() {
editArea.setIcons({
edit.setIcons({
evaluate: false,
'manual-service': false
});
},
onClose: function() {
editArea.setIcons({
edit.setIcons({
emoji: false,
image: false,
evaluate: false,
... ... @@ -697,14 +529,10 @@ function pageInit() {
heightBefore,
heightAfter;
var mode = 2,
width = 100,
height = 100;
msgList = msgList.reverse();
len = msgList.length;
if (msgList[0] && endTime === msgList[0].sendTime) {
if (!len || msgList[0] && endTime === msgList[0].sendTime) {
processInfo.hasMore = false;
}
... ... @@ -713,25 +541,10 @@ function pageInit() {
if (processInfo.hasMore) {
for (i = 0; i < len; i++) {
item = msgList[i];
item.chatMessage.newContent = emojiPrefix(item.chatMessage.newContent);
if (item.chatMessage.type === 2) {
item.chatMessage.newContent = utility.autoProtocol(item.chatMessage.newContent);
item.chatMessage.newContent =
['<img class="img-msg" src="', item.chatMessage.newContent, '">'].join('');
}
receive.preProcess(item);
switch (item.type) {
case allRTs.CU_SEND:
item.userHead = utility.autoProtocol(item.userHead);
if (!item.userHead) {
item.userHead = assetsPrefix + socketConf.defaultUserHead;
} else {
item.userHead = item.userHead
.replace(/\{mode\}/, mode)
.replace(/\{width\}/, width)
.replace(/\{height\}/, height);
}
dom += cusTpl({
userHead: item.userHead,
userName: item.userName,
... ... @@ -752,7 +565,7 @@ function pageInit() {
case allRTs.CS_SEND:
dom += csTpl({
csHead: utility.autoProtocol(item.csHead),
csHead: item.csHead,
csName: item.csName,
sendTimeShort: item.sendTimeLong,
newContent: item.chatMessage.newContent
... ... @@ -818,6 +631,15 @@ function pageInit() {
};
$.getJSON('//www.yohobuy.com/common/passport/?callback=?', param, function(jsonData) {
if (jsonData.code !== 200) {
return location.href = '//www.yohobuy.com/signin.html?refer=' + encodeURIComponent(location.href);
}
// 点击刷新提示
window.onbeforeunload = function() {
return '';
};
if (jsonData && jsonData.data && jsonData.data.result !== -1) {
socketConfCM.userHead = jsonData.data.headIco || '';
... ... @@ -832,12 +654,14 @@ function pageInit() {
});
}());
// tab页title重置
broswer.tabVisible(function() {
document.title = docTitle;
clearInterval(titleInterval);
// 剪切板粘贴发送图片
new ClipPaste('.msg-area', function(path) {
send.image(path);
});
// tab页title重置
broswer.tabVisible(util.removeBlinkAlert);
// 校验留言输入内容
$leaveMsg.find('textarea').bind('change', function() {
var $lm = $('#leaveMsg'),
... ... @@ -871,58 +695,6 @@ function pageInit() {
});
});
// 提交评价
$makeEvalModal.find('.submit').click(function() {
var $btnEval = $('.icon.evaluate'),
mEval = $('#makeEvaluation'),
reason = mEval.find('textarea').val().trim(),
star = mEval.find('.star.positive').length,
reasonTypes = mEval.find('.dis-row .type.chosen');
var data = {
conversationId: socketConfCM.conversationId,
encryptedUid: encryptedUid,
promoter: processInfo.promoter,
stars: star
};
var reasonIds,
idArray = [];
if (star < 4) {
$.each(reasonTypes, function(index, item) {
idArray.push($(item).data('id'));
});
reasonIds = idArray.join(':');
data.reasonIds = reasonIds;
data.reasonMsg = reason;
}
serviceApi.evaluate(data)
.done(function(res) {
processInfo.promoter = 1;
$btnEval.hide();
processInfo.savedEval = true;
if (res && res.code === 200) {
// 评价后通知客服
socketConfCM.type = allRTs.EVAL_NOTICE;
socketConfCM.uuid = uuid.v4();
socketChat.send(socketConfCM);
// 完成后关闭
if (processInfo.completeClose) {
window.close();
}
}
})
.always(function() {
mEval.modal('hide');
});
});
// 根节点高度设置
$html.css({
height: '100%'
... ... @@ -937,7 +709,7 @@ function pageInit() {
$('.qa-list .q').on('click', toggleAnswer);
// 发送订单信息
$sendOrder.on('click', function(e) {
$sendOrder.on('click', function() {
var tag = $(this),
no = tag.data('no'),
nm = tag.data('nm'),
... ... @@ -946,14 +718,15 @@ function pageInit() {
var msgContent = ['单号:', no, '金额:', '¥' + nm, '下单时间:', time, '状态:', status];
sendMessage(e, 10, msgContent);
send.order(msgContent);
});
// 发送图片
$sendImgInput.fileupload({
dataType: 'json',
acceptFileTypes: /(\.|\/)(bmp|gif|jpe?g|png)$/i,
maxFileSize: 5000000, // 5M
url: configDomains.imCs + '/fileManage/uploadFile',
url: gDomains.imCs + '/fileManage/uploadFile', // eslint-disable-line
progressall: function(e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
... ... @@ -966,11 +739,7 @@ function pageInit() {
beforeSendMsg();
// 上传成功后发送图片消息
socketConfCM.type = allRTs.CU_SEND;
socketConfCM.chatMessage.content = utility.autoProtocol(res.data.filePath);
socketConfCM.chatMessage.type = 2;
socketConfCM.uuid = uuid.v4();
socketChat.send(socketConfCM);
send.image(util.autoProtocol(res.data.filePath));
}
setTimeout(function() {
$('.progress-bar').fadeOut();
... ... @@ -1031,41 +800,6 @@ function pageInit() {
}
});
// 星评
$document.on('click', '.make-eval .star', function() {
var el,
i,
len,
tag = $(this),
index = tag.index(),
$detailReason = $('.detail-reason'),
textEl = $('.make-eval .star-text'),
startList = $('.make-eval .stars .star');
var starText = {
0: '非常不满意',
1: '不满意',
2: '一般',
3: '满意',
4: '非常满意'
};
for (i = 0, len = startList.length; i < len; i++) {
el = $(startList[i]);
el.index() <= index && el.addClass('positive');
el.index() > index && el.removeClass('positive');
}
index < 3 && $detailReason.show();
index >= 3 && $detailReason.hide();
textEl.text(starText[index]);
});
// 选择不满意类型
$document.on('click', '.dis-row .type', function(e) {
$(e.target).toggleClass('chosen');
});
// 留言
$msgList.on('click', '.leave-msg', leaveMsg);
... ... @@ -1095,11 +829,11 @@ function pageInit() {
},
2: function() {
showEvalModal();
evalView.open();
},
3: function() {
manualService();
send.manual();
}
};
... ... @@ -1110,7 +844,7 @@ function pageInit() {
});
// 设置表情
$document.on('click', '.emoji-component .emoji', setEmoji);
$document.on('click', '.emoji-component .emoji', edit.setEmoji);
// 表情组件隐藏
$document.on('click', function(e) {
... ... @@ -1137,8 +871,7 @@ function pageInit() {
// 关闭聊天窗口
$close.click(function() {
if (processInfo.manual && !processInfo.savedEval) {
// 没有保存过评论
showEvalModal(true);
evalView.open(true);
} else {
window.close();
}
... ... @@ -1146,7 +879,7 @@ function pageInit() {
// 失去焦点更新鼠标位置
$msgArea.on('blur', function() {
cursorPosition = $(this).getCursorPosition();
$(this).recordCursor();
});
// 消息图片放大显示
... ... @@ -1182,31 +915,13 @@ function pageInit() {
processInfo.hasMore &&
fetchHistoryMsg();
});
}
};
// 拉取域名信息
(function() {
$.ajax({
type: 'GET',
url: '/service/domains',
success: function(domains) {
configDomains = domains;
// 检查支持性
(function() {
if (!window.WebSocket) {
$('.un-support').show();
return false;
} else {
socketConf.servers.push(configDomains.imSocket);
pageInit();
}
}());
}
});
}());
// 检查支持性
if (util.isSupport()) {
socketConf.servers.push(gDomains.imSocket); // eslint-disable-line
_loadPage();
} else {
$('.un-support').show();
}
// 点击刷新提示
window.onbeforeunload = function() {
return '';
};
... ...
/**
* 粘贴剪切板
* @author qi.li@yoho.cn
* @date: 2017/03/09
*/
/* global gDomains injected from client.hbs*/ // eslint-disable-line
var $ = require('yoho-jquery');
var view = require('./view');
var Handlebars = require('yoho-handlebars');
// dialog html
var $view = '<div class="mask-dialog preview-dialog">' +
'<div class="dialog-mask"></div>' +
'<div class="dialog-main">' +
'<div class="head-part">' +
'<span class="head-title">是否发送图片</span> ' +
'<span class="head-close">x</span>' +
'</div>' +
'<div class="body-part">' +
'<img class="clip-img" src="{{src}}">' +
'</div>' +
'<div class="foot-part">' +
'<a class="foot-btn confirm-btn" href="javascript:;">发送</a>' +
'<a class="foot-btn cancel-btn" href="javascript:;">取消</a>' +
'</div>' +
'</div>' +
'</div>';
var $viewHbs = Handlebars.compile($view);
/**
* 粘贴事件
* @param sl 事件元素选择器
* @param cb 上传成功的回调
*/
function Paste(sl, cb) {
if (typeof sl !== 'string') {
return;
}
this.$el = document.querySelector(sl);
this.formData = null;
this.uploadCb = cb;
this.isSending = false;
this.bindEvents();
this.input = '';
}
Paste.prototype = {
bindEvents: function() {
var self = this;
this.$el.addEventListener('paste', function(e) {
self.input = $('.text.msg-area').val();
var cbd = e.clipboardData;
var ua = window.navigator.userAgent;
// 如果是 Safari 直接 return
if (!(e.clipboardData && e.clipboardData.items)) {
return;
}
// Mac平台下Chrome49版本以下 复制Finder中的文件的Bug Hack掉
if (cbd.items && cbd.items.length === 2 && cbd.items[0].kind === 'string' && cbd.items[1].kind === 'file' &&
cbd.types && cbd.types.length === 2 && cbd.types[0] === 'text/plain' && cbd.types[1] === 'Files' &&
ua.match(/Macintosh/i) && Number(ua.match(/Chrome\/(\d{2})/i)[1]) < 49) {
return;
}
for (var i = 0; i < cbd.items.length; i++) {
var item = cbd.items[i];
if (item.kind == 'file') {
var file = item.getAsFile();
if (file.size === 0) { return; }
self.formData = new FormData();
self.formData.append('files[]', file, 'clip.jpg');
var _upload = function() {
if (self.isSending) return;
self.isSending = true;
$.ajax({
type: 'POST',
url: gDomains.imCs + '/fileManage/uploadFile',
data: self.formData,
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false
})
.done(function(res) {
if (res.code === 200) {
var path = res.data.filePath;
self.uploadCb(path);
self.close();
}
})
.fail(function() {
alert('图片发送失败、点击发送重试!');
})
.always(function() {
self.isSending = false;
});
};
var _bindPrevEvents = function() {
var $prev = self.$preview;
$prev.on('click', '.head-close, .cancel-btn', self.close.bind(self))
.on('click', '.confirm-btn', _upload);
};
var _drawPreview = function(url) {
var $prev = $('.preview-dialog');
var $img = $prev.find('.clip-img');
var len = $img.length;
if (len) {
$img.attr('src', url);
self.$preview = $prev;
return;
}
var html = $viewHbs({src: url});
$('body').append(html);
$('.preview-dialog').css('z-index', 1090);
self.$preview = $('.preview-dialog');
// 绑定预览内事件
_bindPrevEvents();
};
var reader = new FileReader();
reader.onload = function(e) {
var base64 = e.target.result;
var image = new Image();
image.src = base64;
image.onload = function() {
$('.text.msg-area').val(self.input);
_drawPreview(base64);
self.open();
};
};
reader.readAsDataURL(file);
}
}
}, false);
},
open: function() {
var dh, vh = $(window).height();
var $main = $('.preview-dialog .dialog-main');
// 机器人不支持
if(!view.processInfo.manual) {
return;
}
this.$preview.show();
dh = $main.height();
$main.css('top', (vh - dh - 40) / 2);
},
close: function() {
this.$preview.hide();
}
};
Paste.prototype.constructor = Paste;
module.exports = Paste;
... ...
/**
* 光标处理
* @author: liqi <qi.li@yoho.cn>
* @date: 2017/02/15
*/
var cursor = {
posRecord: 0
};
/**
* $.fn.extend 获取输入框光标位置
* expample $(selector).recordCursor()
*/
$.fn.extend({
recordCursor: function() {
var pos = 0;
var $el = $(this).get(0);
if ('selectionStart' in $el) {
pos = $el.selectionStart;
} else if ('selection' in document) {
$el.focus();
var $sel = document.selection.createRange();
var $selLength = document.selection.createRange().text.length;
$sel.moveStart('character', -$el.value.length);
pos = $sel.text.length - $selLength;
}
cursor.posRecord = pos;
}
});
$.extend(cursor, {
/**
* 设置光标位置
* @param $el
* @param pos
*/
setCursor: function($el, pos) {
if ($el.setSelectionRange) {
// chrome and firefox support
$el.setSelectionRange(pos, pos);
$el.focus();
} else if ($el.createTextRange) {
// IE support
var range = $el.createTextRange();
range.collapse(true);
range.moveEnd('character', pos);
range.moveStart('character', pos);
range.select();
}
},
/**
* 在已记录光标处插入文本
* @param text 文本
*/
insertAtRecordCorsor: function(text) {
var self = cursor;
var $area = $('.msg-area');
var val = $area.val();
var record = self.posRecord;
if (record || record === 0) {
var start = val.substring(0, record);
var end = val.substring(record);
var newVal = [start, text, end].join('');
} else {
var newVal = [val, text].join('');
}
$area.val(newVal);
self.posRecord += text.length;
var $el = $area[0];
var pos = self.posRecord ? self.posRecord : newVal.length;
this.setCursor($el, pos);
}
})
module.exports = cursor;
... ...
... ... @@ -18,7 +18,10 @@ var _ajax = function(method, reqUrl, params) {
type: method || 'GET',
url: reqUrl,
data: params
});
})
.fail(function() {
alert('服务器开小差了,请稍后重试。');
});
};
/**
... ...
... ... @@ -43,7 +43,7 @@ function socketInit(opts) {
socketIns.onerror = options.onError || function() {};
return socketIns;
return socketIns || {};
}
if (window.WebSocket) {
... ... @@ -59,7 +59,7 @@ function socketInit(opts) {
connectId = setInterval(function() {
if (socket.readyState !== WebSocket.OPEN) {
if (times < 3) {
socket.close();
socket.close && socket.close();
socket = socketConnect();
times++;
} else {
... ... @@ -89,7 +89,7 @@ function sendMsg(msg) {
return;
}
if (socket.readyState === WebSocket.OPEN) {
if (socket && socket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(msg));
} else {
options.socketClosedCb();
... ... @@ -97,6 +97,14 @@ function sendMsg(msg) {
}
/**
* 链接可用
* @returns {*|boolean}
*/
function isOpen() {
return socket && socket.readyState === WebSocket.OPEN;
}
/**
* 关闭socket
*/
function clearSocket() {
... ... @@ -109,6 +117,7 @@ module.exports = {
socket: socket,
init: socketInit,
clear: clearSocket,
send: sendMsg
send: sendMsg,
isOpen: isOpen
};
... ...
... ... @@ -47,6 +47,7 @@ var config = {
encryptedUid: 0, // 加密ID Web使用
userHead: '', // 用户头像
userName: '', // 用户账号
isLeaveMessage: 0, // 是否开启留言 1:关闭 2:开启
csId: 0, // 客服 ID
type: 1, // type => [0:没人在线,1:排队,2:接通,3:管理员分配]
serviceSortId: 0,
... ...
var $ = require('yoho-jquery');
var cursor = require('../cursor');
var emMap = require('../emoji-map');
/**
* 消息编辑区
* @author: liqi <qi.li@yoho.cn>
* @date: 2016/11/26
* 表情设置
* @param e
*/
exports.setEmoji = function(e) {
var emText = '';
var tag = $(e.target);
var emId = tag.data('id');
var $em = $('.emoji-component');
var $ = require('yoho-jquery');
$.each(emMap, function(i, v) {
if (v.file === emId + '') {
emText = v.text;
}
});
cursor.insertAtRecordCorsor(emText);
$em.hide();
};
module.exports = {
/**
* 消息编辑区图标显示
* true: 显示 false: 隐藏
*/
setIcons: function(opt) {
var $util = $('.msg-edit .util');
/**
* 消息编辑区图标控制
* true: 显示 false: 隐藏
*/
exports.setIcons = function(opt) {
var $util = $('.msg-edit .util');
$.each(opt, function(key, val) {
var $el = $util.find('.' + key);
$.each(opt, function(key, val) {
var $el = $util.find('.' + key);
if (!$el.length || !val && val !== false) {
return;
}
val === true && $el.show();
val === false && $el.hide();
});
}
if (!$el.length || !val && val !== false) {
return;
}
val === true && $el.show();
val === false && $el.hide();
});
};
... ...
/**
* 消息接收模块
* @author LQ <qi.li@yoho.cn>
* @date 2017/03/20
*/
var socketConf = require('./config');
var utility = require('../utility');
var allTypes = socketConf.recType;
var assetsPath = $('input[name=assetsPrefix]').val() || '';
/**
* 表情图片路径解析
* @param path
* @returns {*}
*/
var _emjPath = function(path) {
var reg = /src="(\d{3}).gif"/g;
var subFolder = '/img/service/emoji/';
var prePath = ['src="', assetsPath, subFolder].join('');
if (typeof path !== 'string') {
return '';
}
return path.replace(reg, prePath + '$1.gif"');
};
/**
* 用户头像处理
* @param url
* @returns {*}
*/
var _userAvatar = function(url, mode, w, h) {
if (!url || typeof url !== 'string') {
return assetsPath + socketConf.defaultUserHead;
}
return url.replace(/\{mode\}/, mode || 2)
.replace(/\{width\}/, w || 100)
.replace(/\{height\}/, h || 100);
};
/**
* 图片添加img标签
* @param url 图片链接
* @private
*/
var _imgMsg = function(url) {
url = utility.autoProtocol(url);
return '<img class="img-msg" src="' + url + '">';
};
/**
* 图片标签添加
* @param msg 内部消息体
* @private
*/
var _createImgEl = function(msg) {
if (msg.type === 2) {
msg.newContent = _imgMsg(msg.content);
msg.content = utility.autoProtocol(msg.content);
msg.newContent = utility.autoProtocol(msg.newContent);
}
};
/**
* 接收消息体预处理
* @param rec 接收对象
*/
exports.preProcess = function(rec) {
var chatMsg = rec.chatMessage;
var recType = rec.type;
switch (recType) {
case allTypes.CU_SEND:
rec.userHead = utility.autoProtocol(rec.userHead);
rec.userHead = _userAvatar(rec.userHead);
chatMsg.newContent = _emjPath(chatMsg.newContent);
_createImgEl(chatMsg);
break;
case allTypes.CS_SEND:
rec.csHead = utility.autoProtocol(rec.csHead);
chatMsg.newContent = _emjPath(chatMsg.newContent);
_createImgEl(chatMsg);
break;
default:
break;
}
};
/**
* 是否显示留言
* @param rec 接收对象
*/
exports.leaMsgTip = function(rec) {
var SHOW_LEAVE_MSG = 2; // 显示留言
var chatMsg = rec.chatMessage;
var canLeave = rec.isLeaveMessage === SHOW_LEAVE_MSG;
var append = canLeave ? '您也可以选择<a class="leave-msg">留言</a>' : '';
var reg = /[,|,]$/g;
chatMsg.content = canLeave ?
chatMsg.content :
(chatMsg.content = chatMsg.content.replace(reg, ''));
return [chatMsg.content, append].join('');
};
... ...
/**
* 消息发送
* @author <qi.li@yoho.cn>
* @date 2017/03/13
*/
var uuid = require('uuid');
var chat = require('./chat');
var sockConf = require('./config');
var recTypes = sockConf.recType;
var conMSG = sockConf.conversationMessage;
/**
* 发送
* @param {msg} 消息体
*/
function _send(msg) {
conMSG.uuid = uuid.v4();
chat.send(msg);
}
/**
* 文本发送
*/
exports.text = function(content) {
var TEXT_TYPE = 1; // 文本消息
conMSG.type = recTypes.CU_SEND;
conMSG.chatMessage.content = content;
conMSG.chatMessage.type = TEXT_TYPE;
_send(conMSG);
};
/**
* 图片发送
*/
exports.image = function(path) {
var IMG_TYPE = 2; // 图片消息
conMSG.type = recTypes.CU_SEND;
conMSG.chatMessage.content = path;
conMSG.chatMessage.type = IMG_TYPE;
_send(conMSG);
};
/**
* 订单发送
*/
exports.order = function(content) {
var ORDER_TYPE = 10; // 订单消息
conMSG.type = recTypes.CU_SEND;
conMSG.chatMessage.type = ORDER_TYPE;
conMSG.chatMessage.content = content;
_send(conMSG);
};
/**
* 评价完成
*/
exports.completeEval = function() {
if (chat.isOpen()) {
conMSG.type = recTypes.EVAL_NOTICE;
_send(conMSG);
}
};
/**
* 人工客服
*/
exports.manual = function() {
conMSG.type = recTypes.MANUAL_SERVICE;
_send(conMSG);
};
... ...
var blinkInterval;
var docTitle = document.title;
/**
* 检查是否支持 websocket
* @returns {boolean}
*/
exports.isSupport = function() {
return !!window.WebSocket;
};
/**
* 添加闪烁提醒
* @returns {boolean}
*/
exports.addBlinkAlert = function() {
if (blinkInterval) {
clearInterval(blinkInterval);
}
blinkInterval = setInterval(function() {
document.title = '您有新消息';
setTimeout(function() {
document.title = docTitle;
}, 300);
}, 600);
};
/**
* 去除闪烁提醒
* @returns {boolean}
*/
exports.removeBlinkAlert = function() {
if (blinkInterval) {
clearInterval(blinkInterval);
document.title = docTitle;
}
};
/**
* 链接自适应协议
* @author: lq <qi.li@yoho.cn>
... ...
var $ = require('yoho-jquery');
var send = require('./socket/send');
var serviceApi = require('./service-api');
var socketConf = require('./socket/config');
var conMsg = socketConf.conversationMessage;
// 过程标示
var processSign = {
encryptedUid: '',
hasMore: true, // 更多消息
manual: false, // 人工客服
promoter: 1, // 评论发起者 1:客户自己 2:客服
savedEval: false, // 是否保存过评论
scrollLoad: false, // 滚动加载
completeClose: false, // 评价完成后关闭
loadingHistory: false // 加载消息
};
/**
* 获取评价原因
*/
var _fetchReason = (function() {
var cache;
return function(render) {
// 1:企业端 2:YOHO在线客服
// 3:BLK在线客服 4:MARS在线客服 5:机器人
var YOHO_CS = 2;
if (cache) {
return render(cache);
}
serviceApi.reason({
type: YOHO_CS
})
.done(function(res) {
if (res.code === 200) {
cache = res.data;
render(cache);
}
});
};
}());
/**
* 评价客服view
* @constructor
*/
function Evaluate() {
this.isOpen = false; // 是否已经开启
this.$view = $('#makeEvaluation');
this.disTpl = require('hbs/service/discontent-row.hbs');
this.bindEvents();
this.submitting = false;
}
/**
* 星评
*/
function _starChange(e) {
var self = this,
$ct = $(e.currentTarget),
index = $ct.index(),
$reason = self.$view.find('.detail-reason'),
$text = self.$view.find('.star-text'),
$list = self.$view.find('.star');
var starText = {
0: '非常不满意',
1: '不满意',
2: '一般',
3: '满意',
4: '非常满意'
};
for (var i = 0, len = $list.length; i < len; i++) {
var $el = $($list[i]);
$el.index() <= index && $el.addClass('positive');
$el.index() > index && $el.removeClass('positive');
}
index < 3 && $reason.show();
index >= 3 && $reason.hide();
$text.text(starText[index]);
}
/**
* 评价提交
*/
var _evalSubmit = function() {
var self = this,
$btnEval = $('.icon.evaluate'),
$modal = $('#makeEvaluation'),
reason = $modal.find('textarea').val().trim(),
star = $modal.find('.star.positive').length,
reasonTypes = $modal.find('.dis-row .type.chosen');
if (self.submitting) {
return;
}
self.submitting = true;
var data = {
conversationId: conMsg.conversationId,
encryptedUid: processSign.encryptedUid,
promoter: processSign.promoter,
stars: star
};
var reasonIds,
idArray = [];
if (star < 4) {
$.each(reasonTypes, function(index, item) {
idArray.push($(item).data('id'));
});
reasonIds = idArray.join(':');
data.reasonIds = reasonIds;
data.reasonMsg = reason;
}
serviceApi.evaluate(data)
.done(function(res) {
$btnEval.hide();
processSign.savedEval = true;
if (res && res.code === 200) {
send.completeEval();
self.close();
setTimeout(function() {
if (processSign.completeClose) {
window.close();
}
}, 500);
}
})
.always(function() {
self.submitting = false;
});
};
Evaluate.prototype = {
bindEvents: function() {
var self = this;
this.$view.on('click', '.star', _starChange.bind(self))
.on('click', '.submit', _evalSubmit.bind(self))
.on('click', '.dis-row .type', function(e) {
$(e.target).toggleClass('chosen');
})
.on('click', '.close', function() {
self.close();
});
},
open: function(close) {
var self = this;
if (self.isOpen) return;
processSign.completeClose = close;
if (!processSign.manual) {
return;
}
var _resetEval = function() {
var btnText = close ? '提交并关闭' : '提交';
self.$view.find('.star-text').html('非常满意');
self.$view.find('.star').addClass('positive');
self.$view.find('.other-reason').val('');
self.$view.find('.detail-reason').hide();
self.$view.find('.submit').html(btnText);
};
var _show = function(data) {
var dom = '';
_resetEval();
self.isOpen = true;
if (!data || !data.length) return self.$view.modal('show');
for (var i = 0, len = data.length; i < len; i = i + 2) {
dom += self.disTpl({
id1: data[i].id,
id1Content: data[i].content,
id2: data[i + 1] && data[i + 1].id,
id2Content: data[i + 1] && data[i + 1].content
});
}
self.$view.find('.discontent').empty().append(dom);
self.$view.modal('show');
};
_fetchReason(_show);
},
close: function() {
this.isOpen = false;
this.$view.modal('hide');
}
};
Evaluate.prototype.constructor = Evaluate;
module.exports = {
processInfo: processSign,
Evaluate: Evaluate
};
... ...
... ... @@ -20,6 +20,7 @@
position: relative;
display: block;
height: auto;
max-width: 90%;
margin: 0 auto;
margin-top: 50px;
cursor: pointer;
... ...
... ... @@ -1055,4 +1055,5 @@ $color-3a3a3a: #3a3a3a;
@import "_img-zoom-in.css";
@import "_un-support.css";
@import "_connect-fail.css";
@import "_mask-dialog.css";
}
... ...
.mask-dialog {
display: none;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
.dialog-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1070;
background-color: #000;
filter: alpha(opacity=50);
opacity: 0.4;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
}
.dialog-main {
position: relative;
width: 600px;
padding: 20px;
z-index: 1100;
margin: 0 auto;
background: #fff;
border: 1px solid #c2bbbb;
box-shadow: 1px 1px 10px #000;
.head-part {
margin-bottom: 20px;
.head-title {
font-size: 14px;
}
.head-close {
position: absolute;
right: 25px;
top: 15px;
font-size: 22px;
cursor: pointer;
}
}
.body-part {
margin-bottom: 20px;
text-align: center;
.clip-img {
max-width: 560px;
max-height: 400px;
border: 1px solid #eee;
}
}
.foot-part {
text-align: center;
.foot-btn {
display: inline-block;
height: 30px;
width: 120px;
margin: 0 40px;
color: #fff;
line-height: 30px;
text-align: center;
text-decoration: none;
background: #fff;
border: 1px solid #000;
cursor: pointer;
}
.confirm-btn {
background: #000;
}
.cancel-btn {
color: #000;
}
}
}
}
... ...