// Workspace notes, transfers, recalls and historical message search. function updateAdminTools(){ document.getElementById('transfer-session').disabled = !curSid || Boolean(sendingSid) || Boolean(closingSid); document.getElementById('agent-emoji-toggle').disabled = !curSid || Boolean(sendingSid) || Boolean(closingSid); document.getElementById('noteInput').disabled = !curSid || Boolean(sessions[curSid].noteLoading); document.getElementById('note-save').disabled = !curSid || !sessions[curSid].noteLoaded || Boolean(sessions[curSid].noteLoading); } async function loadSessionNote(sid){ const session = sessions[sid]; const input = document.getElementById('noteInput'); input.value = session.noteDraft || ''; if(session.noteLoaded){ updateAdminTools(); return; } session.noteLoading = true; updateAdminTools(); try{ const state = await adminRequest('/api/human/poll?sid='+Number(sid)+'&after=0'); if(sessions[sid] !== session) return; session.noteDraft = state.note || ''; session.noteLoaded = true; if(curSid === sid) input.value = session.noteDraft; }catch(error){ if(curSid === sid) setReplyStatus('备注加载失败,请重新选择会话后重试。', true); }finally{ session.noteLoading = false; updateAdminTools(); } } async function saveNote(){ if(!curSid || !sessions[curSid].noteLoaded) return; const sid = curSid, session = sessions[sid]; const note = document.getElementById('noteInput').value; session.noteDraft = note; try{ await postAdmin('/api/human/note', {sid:Number(sid), note:note}); if(curSid === sid) setReplyStatus(session.noteDraft === note ? '备注已保存' : '先前备注已保存,当前修改尚未保存'); }catch(error){ if(curSid === sid) setReplyStatus('备注保存失败,内容已保留。', true); } } async function transferSession(){ if(!curSid || closingSid || sendingSid) return; if(!window.confirm('确定转接这个对话?转接后其他客服可在排队列表接入。')) return; const sid = curSid; closingSid = sid; updateComposer(); try{ await postAdmin('/api/human/transfer', {sid:Number(sid)}); delete sessions[sid]; if(curSid === sid) showEmptySession(); renderList(); loadQueue(); }catch(error){ if(curSid === sid) setReplyStatus('转接未成功,会话仍保留。', true); }finally{ closingSid = ''; updateComposer(); } } function applyAdminRecall(sid, mid){ const message = sessions[sid].msgs.find(function(item){ return item.id === mid; }); if(message){ message.side = 'system'; message.content = '该消息已撤回'; } if(curSid === sid){ const row = document.querySelector('#chatBox [data-message-id="'+Number(mid)+'"]'); if(row){ row.className = 'msg system-msg'; row.textContent = '该消息已撤回'; } } return Boolean(message); } async function recallMsg(mid){ if(!curSid) return; const sid = curSid; try{ await postAdmin('/api/human/recall', {mid:mid}); if(sessions[sid]){ applyAdminRecall(sid, mid); renderList(); } }catch(error){ if(curSid === sid) setReplyStatus('撤回失败,请稍后重试。', true); } } let searchVersion = 0; async function doSearch(){ const keyword = document.getElementById('searchBox').value.trim(); const version = ++searchVersion; const body = document.getElementById('searchBody'); if(!keyword){ body.textContent = '请输入关键词搜索历史消息'; return; } body.textContent = '正在搜索…'; try{ const list = await adminRequest('/api/search_chat?q='+encodeURIComponent(keyword)); if(version !== searchVersion) return; const roles = {user:'客户', assistant:'客服', system:'系统'}; body.innerHTML = list.length ? list.map(function(item){ return '

'+escapeText(item.title)+'

'+escapeText(item.content)+'

'+escapeText(roles[item.role] || item.role)+' · '+escapeText(item.ctime)+'
'; }).join('') : '没有匹配的历史消息'; }catch(error){ if(version === searchVersion) body.textContent = '搜索失败,请稍后重试'; } } document.getElementById('history-search-toggle').onclick = function(){ document.getElementById('searchResult').showModal(); document.getElementById('searchBox').focus(); }; document.getElementById('search-close').onclick = function(){ document.getElementById('searchResult').close(); }; document.getElementById('history-search-form').addEventListener('submit', function(event){ event.preventDefault(); doSearch(); }); document.getElementById('note-save').onclick = saveNote; document.getElementById('noteInput').addEventListener('input', function(){ if(curSid) sessions[curSid].noteDraft = this.value; }); document.getElementById('notifications-toggle').onclick = function(){ SupportUI.enableNotifications(this); }; function updateNightBadge(){ const hour = new Date().getHours(); document.getElementById('nightBadge').hidden = hour >= 8 && hour < 22; } updateNightBadge(); setInterval(updateNightBadge, 60000);