const apiFetch = async (url, options = {}) => { const opts = { headers: { 'Content-Type': 'application/json' }, ...options, }; if (opts.body && typeof opts.body !== 'string') { opts.body = JSON.stringify(opts.body); } const res = await fetch(url, opts); if (!res.ok) { throw new Error(`API error: ${res.status}`); } return res.json(); }; const showToast = (message, type = 'success') => { const container = document.querySelector('.toast-container'); if (!container) return; const toast = document.createElement('div'); toast.className = `toast align-items-center text-bg-${type} border-0 show`; toast.setAttribute('role', 'alert'); toast.innerHTML = `
${message}
`; container.appendChild(toast); setTimeout(() => toast.remove(), 3200); }; const formatTime = (value) => { if (!value) return ''; const d = new Date(value); if (Number.isNaN(d.getTime())) return value; return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }; const initAgent = () => { const listEl = document.querySelector('[data-contacts]'); const chatTitle = document.querySelector('[data-chat-title]'); const chatMeta = document.querySelector('[data-chat-meta]'); const chatBody = document.querySelector('[data-chat-body]'); const form = document.querySelector('[data-chat-form]'); const input = document.querySelector('[data-chat-input]'); const emojiTrigger = document.querySelector('[data-emoji-trigger]'); const emojiPicker = document.querySelector('[data-emoji-picker]'); const shortcutContainer = document.querySelector('[data-shortcuts]'); const shortcutList = document.getElementById('shortcutList'); const newShortcut = document.getElementById('newShortcut'); const addShortcutBtn = document.getElementById('addShortcut'); let contacts = []; let activeId = null; let shortcuts = JSON.parse(localStorage.getItem('sms_shortcuts') || '[]'); // Quick reply logic const renderShortcuts = () => { shortcutList.innerHTML = ''; shortcutContainer.innerHTML = ''; shortcuts.forEach((s, i) => { const li = document.createElement('li'); li.className = 'list-group-item d-flex justify-content-between'; li.innerHTML = `${s}`; shortcutList.appendChild(li); const btn = document.createElement('button'); btn.className = 'btn btn-sm btn-outline-primary me-1 mb-1'; btn.textContent = s; btn.onclick = () => { input.value = s; }; shortcutContainer.appendChild(btn); }); }; window.removeShortcut = (index) => { shortcuts.splice(index, 1); localStorage.setItem('sms_shortcuts', JSON.stringify(shortcuts)); renderShortcuts(); }; addShortcutBtn.onclick = () => { const val = newShortcut.value.trim(); if (!val) return; shortcuts.push(val); localStorage.setItem('sms_shortcuts', JSON.stringify(shortcuts)); newShortcut.value = ''; renderShortcuts(); }; renderShortcuts(); // Emoji picker emojiTrigger.addEventListener("click", (e) => { e.stopPropagation(); emojiPicker.classList.toggle("d-none"); }); emojiPicker.addEventListener("emoji-click", e => { input.value += e.detail.unicode; emojiPicker.classList.add('d-none'); }); document.addEventListener('click', (e) => { if (!emojiTrigger.contains(e.target) && !emojiPicker.contains(e.target)) { emojiPicker.classList.add('d-none'); } }); // Country selector const countryListEl = document.getElementById('countryList'); const countrySearch = document.getElementById('countrySearch'); const selectedCode = document.getElementById('selectedCode'); const phoneInput = document.getElementById('phoneNumber'); const startChatBtn = document.getElementById('startChat'); const renderCountries = (filter = "") => { countryListEl.innerHTML = ''; countries.filter(c => c.name.includes(filter)).forEach(c => { const item = document.createElement('div'); item.className = 'country-item'; item.innerHTML = `${c.name} (${c.code})`; item.onclick = () => { selectedCode.textContent = c.code; countrySearch.value = ""; renderCountries(); }; countryListEl.appendChild(item); }); }; countrySearch.addEventListener('input', (e) => renderCountries(e.target.value)); renderCountries(); startChatBtn.addEventListener('click', async () => { const fullPhone = selectedCode.textContent + phoneInput.value; if (!phoneInput.value) return showToast('请输入手机号', 'danger'); try { const res = await apiFetch('/api/contacts.php', { method: 'POST', body: { phone: fullPhone } }); bootstrap.Modal.getInstance(document.getElementById('countryModal')).hide(); phoneInput.value = ''; await loadContacts(); await setActive(res.id); } catch (e) { showToast('无法启动聊天', 'danger'); } }); // ESC to exit document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { activeId = null; chatTitle.textContent = '请选择联系人'; chatMeta.textContent = '状态'; chatBody.innerHTML = ''; renderContacts(); } }); const renderContacts = () => { listEl.innerHTML = ''; contacts.forEach((contact) => { const item = document.createElement('div'); item.className = `contact-item ${contact.id === activeId ? 'active' : ''}`; item.innerHTML = `
${contact.phone}
${contact.last_message || '暂无消息'}
`; listEl.appendChild(item); }); }; window.setActive = async (id) => { activeId = id; const contact = contacts.find(c => c.id === id); chatTitle.textContent = contact ? contact.phone : '请选择联系人'; chatMeta.textContent = contact ? "手机号: " + contact.phone : "状态"; renderContacts(); await loadMessages(); }; window.deleteContact = async (id) => { if (!confirm('确定删除此联系人?')) return; try { await apiFetch(`/api/contacts.php?action=delete&id=${id}`, { method: 'POST' }); await loadContacts(); showToast('已删除联系人'); if (activeId === id) { activeId = null; chatTitle.textContent = '请选择联系人'; chatMeta.textContent = '状态'; chatBody.innerHTML = ''; } } catch (e) { showToast('删除失败', 'danger'); } }; window.blockContact = async (id) => { if (!confirm('确定拉黑此联系人?')) return; try { await apiFetch(`/api/contacts.php?action=block&id=${id}`, { method: 'POST' }); await loadContacts(); showToast('已拉黑联系人'); if (activeId === id) { activeId = null; chatTitle.textContent = '请选择联系人'; chatMeta.textContent = '状态'; chatBody.innerHTML = ''; } } catch (e) { showToast('操作失败', 'danger'); } }; const loadContacts = async () => { const data = await apiFetch('/api/contacts.php'); contacts = data.contacts || []; renderContacts(); }; const loadMessages = async () => { if (!activeId) return; const data = await apiFetch(`/api/messages.php?contact_id=${activeId}`); chatBody.innerHTML = (data.messages || []).map(msg => `
${msg.body}
${formatTime(msg.created_at)}
`).join(''); chatBody.scrollTop = chatBody.scrollHeight; }; form?.addEventListener('submit', async (e) => { e.preventDefault(); if (!activeId) return; const body = input.value.trim(); if (!body) return; await apiFetch('/api/messages.php', { method: 'POST', body: { contact_id: activeId, body } }); input.value = ''; await loadMessages(); }); loadContacts(); setInterval(loadContacts, 3000); }; document.addEventListener('DOMContentLoaded', () => { if (document.body.dataset.page === 'agent') initAgent(); });