调整客服自动接待和手动排队规则
This commit is contained in:
+40
-4
@@ -1,6 +1,7 @@
|
||||
var curSid = 0;
|
||||
var sessions = {};
|
||||
var drafts = {};
|
||||
var reassignedNotes = {};
|
||||
var queueItems = [];
|
||||
var queueOverdueNotified = new Set();
|
||||
var accepting = new Set();
|
||||
@@ -15,6 +16,7 @@ var staffExpired = false;
|
||||
var staffLoggingOut = false;
|
||||
var staffPollTimers = [];
|
||||
var staffCsrf = document.querySelector('meta[name="staff-csrf-token"]').content;
|
||||
var staffUsername = document.querySelector('meta[name="staff-username"]').content;
|
||||
var replyInput = document.getElementById("replyText");
|
||||
SupportUI.initAttachments(document.getElementById('chatBox'), '图片暂时无法访问,请客户重新发送附件。');
|
||||
|
||||
@@ -153,7 +155,7 @@ function renderQueue(){
|
||||
const wait = item.overdue
|
||||
? '<span class="queue-overdue">排队超时 '+escapeText(item.overdue_minutes)+' 分钟</span>'
|
||||
: '<span class="queue-wait">已等待 '+escapeText(age)+' 分钟</span>';
|
||||
return '<div class="queue-item'+(item.overdue ? ' is-overdue' : '')+'"><div class="qt">对话 #'+sid+wait+'<small>'+escapeText(item.ctime)+'</small></div><button type="button" onclick="accept('+sid+')">接入会话 →</button></div>';
|
||||
return '<div class="queue-item'+(item.overdue ? ' is-overdue' : '')+'"><div class="qt">对话 #'+sid+wait+'<small>'+escapeText(item.group_name || '综合客服')+' · '+escapeText(item.ctime)+'</small></div><button type="button" onclick="accept('+sid+')">接入会话 →</button></div>';
|
||||
}).join("")
|
||||
: '<div class="list-empty">'+(term ? "没有匹配的排队会话" : "当前没有排队会话")+'</div>';
|
||||
}
|
||||
@@ -198,7 +200,7 @@ async function accept(sid){
|
||||
loadQueue();
|
||||
poll();
|
||||
}catch(error){
|
||||
document.getElementById("queue").innerHTML = '<div class="list-empty">接入失败,请稍后重试</div>';
|
||||
document.getElementById("queue").innerHTML = '<div class="list-empty">'+escapeText(error.userMessage || '接入失败,请稍后重试')+'</div>';
|
||||
}finally{
|
||||
accepting.delete(sid);
|
||||
}
|
||||
@@ -213,7 +215,7 @@ function switchSession(sid){
|
||||
document.getElementById("chatContent").style.display = "flex";
|
||||
setChatView(true);
|
||||
document.getElementById("chatTitle").textContent = "客户 #" + sid;
|
||||
document.getElementById("chat-subtitle").textContent = "正在接待 · 人工会话";
|
||||
document.getElementById("chat-subtitle").textContent = "我负责 · "+(sessions[sid].group_name || "综合客服");
|
||||
document.getElementById("cInfo").textContent = "会话编号:#" + sid;
|
||||
document.getElementById("chatBox").innerHTML = "";
|
||||
sessions[sid].msgs.forEach(appendMsg);
|
||||
@@ -316,11 +318,23 @@ async function poll(){
|
||||
const list = await adminRequest("/api/human/active");
|
||||
list.forEach(function(session){
|
||||
if(!sessions[String(session.id)]) sessions[String(session.id)] = {lastId:0, msgs:[], unread:0};
|
||||
Object.assign(sessions[String(session.id)], {group_name:session.group_name, owner_username:session.owner_username});
|
||||
});
|
||||
await Promise.all(Object.keys(sessions).map(async function(sid){
|
||||
const session = sessions[sid];
|
||||
const state = await adminRequest("/api/human/poll?sid="+Number(sid)+"&after="+session.lastId);
|
||||
if(sessions[sid] !== session) return;
|
||||
if(state.status === 'waiting' || (state.owner_username && state.owner_username !== staffUsername)){
|
||||
if(!Object.prototype.hasOwnProperty.call(drafts,sid)) drafts[sid] = '';
|
||||
if(sid === curSid){
|
||||
drafts[sid] = replyInput.value;
|
||||
reassignedNotes[sid] = document.getElementById('noteInput').value;
|
||||
}else if(session.noteDraft){ reassignedNotes[sid] = session.noteDraft; }
|
||||
delete sessions[sid];
|
||||
if(sid === curSid) showEmptySession();
|
||||
renderRecoveredDrafts();
|
||||
return;
|
||||
}
|
||||
if(state.status !== "active"){
|
||||
delete sessions[sid];
|
||||
delete drafts[sid];
|
||||
@@ -339,6 +353,7 @@ async function poll(){
|
||||
}));
|
||||
document.getElementById("sync-status").hidden = true;
|
||||
renderList();
|
||||
renderRecoveredDrafts();
|
||||
}catch(error){
|
||||
document.getElementById("sync-status").hidden = false;
|
||||
}finally{
|
||||
@@ -346,6 +361,15 @@ async function poll(){
|
||||
}
|
||||
}
|
||||
|
||||
function renderRecoveredDrafts(){
|
||||
const ids = Object.keys(drafts).filter(function(sid){ return !sessions[sid] && (drafts[sid] || reassignedNotes[sid]); });
|
||||
document.getElementById('routing-draft-recovery').hidden = ids.length === 0;
|
||||
document.getElementById('routing-retired-drafts').innerHTML = ids.map(function(sid){
|
||||
return '<label>会话 #'+Number(sid)+' · 未发送草稿<textarea readonly rows="3">'+escapeText(drafts[sid] || '')+'</textarea></label>'+
|
||||
(reassignedNotes[sid] ? '<label>备注草稿<textarea readonly rows="3">'+escapeText(reassignedNotes[sid])+'</textarea></label>' : '');
|
||||
}).join('');
|
||||
}
|
||||
|
||||
async function sendReply(){
|
||||
if(!curSid || sendingSid || closingSid) return;
|
||||
const sid = curSid;
|
||||
@@ -454,7 +478,7 @@ document.getElementById("tools-from-list").onclick = function(){ toolsOpener = "
|
||||
document.getElementById("tools-close").onclick = function(){
|
||||
setToolsOpen(false);
|
||||
const fromList = toolsOpener === "tools-from-list" && !document.body.classList.contains("chat-view") && window.matchMedia("(max-width: 720px)").matches;
|
||||
document.getElementById(["ticket-inbox-toggle", "ticket-overdue-toggle"].includes(toolsOpener) ? toolsOpener : fromList ? "tools-from-list" : "tools-toggle").focus();
|
||||
document.getElementById(["ticket-inbox-toggle", "ticket-overdue-toggle", "routing-open"].includes(toolsOpener) ? toolsOpener : fromList ? "tools-from-list" : "tools-toggle").focus();
|
||||
};
|
||||
document.getElementById("tools-backdrop").onclick = document.getElementById("tools-close").onclick;
|
||||
document.addEventListener("keydown", function(event){
|
||||
@@ -494,3 +518,15 @@ var ticketDesk = window.AdminTickets.create({request:adminRequest, escape:escape
|
||||
loadTickets();
|
||||
poll();
|
||||
staffPollTimers = [setInterval(loadQueue, 3000), setInterval(loadTickets, 5000), setInterval(poll, 1000)];
|
||||
if(window.AdminRouting){
|
||||
var routingDesk = window.AdminRouting.create({username:staffUsername,request:adminRequest,
|
||||
changed:function(){ loadQueue(); poll(); }});
|
||||
routingDesk.refresh();
|
||||
staffPollTimers.push(setInterval(routingDesk.heartbeat, 20000));
|
||||
document.getElementById('routing-open').onclick = function(){
|
||||
toolsOpener = 'routing-open'; setToolsOpen(true);
|
||||
document.getElementById('routing-title').scrollIntoView({block:'start'});
|
||||
document.getElementById('routing-refresh').focus({preventScroll:true});
|
||||
routingDesk.refresh();
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user