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 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 shortcutList = document.getElementById('shortcutList'); const newShortcutInput = document.getElementById('newShortcut'); const addShortcutBtn = document.getElementById('addShortcut'); const shortcutContainer = document.querySelector('[data-shortcuts]'); let contacts = []; let activeId = null; let shortcuts = JSON.parse(localStorage.getItem('shortcuts') || '["你好", "请问有什么可以帮您?", "谢谢"]'); // Country selector const countryList = document.getElementById('countryList'); const countrySearch = document.getElementById('countrySearch'); const selectedCode = document.getElementById('selectedCode'); const phoneInput = document.getElementById('phoneNumber'); const renderCountryList = (filter = '') => { countryList.innerHTML = ''; countries.filter(c => c.name.includes(filter) || c.code.includes(filter)).forEach(c => { const item = document.createElement('div'); item.className = 'country-item'; item.textContent = `${c.name} (${c.code})`; item.onclick = () => { selectedCode.textContent = c.code; countrySearch.value = c.name; renderCountryList(); }; countryList.appendChild(item); }); }; countrySearch.addEventListener('input', e => renderCountryList(e.target.value)); renderCountryList(); document.getElementById('startChat').onclick = async () => { const phone = selectedCode.textContent + phoneInput.value; if (!phoneInput.value) return; try { // Send to API to create/get contact const res = await apiFetch('/api/contacts.php', { method: 'POST', body: { phone } }); const newContact = res.contact; contacts.unshift(newContact); renderContacts(); setActive(newContact.id); bootstrap.Modal.getInstance(document.getElementById('countryModal')).hide(); } catch (err) { showToast('无法启动聊天', 'danger'); } }; const renderShortcuts = () => { shortcutContainer.innerHTML = shortcuts.map(s => `` ).join(''); shortcutList.innerHTML = shortcuts.map((s, i) => `
  • ${s}
  • `).join(''); }; window.removeShortcut = (i) => { shortcuts.splice(i, 1); localStorage.setItem('shortcuts', JSON.stringify(shortcuts)); renderShortcuts(); }; addShortcutBtn.addEventListener('click', () => { if(newShortcutInput.value) { shortcuts.push(newShortcutInput.value); localStorage.setItem('shortcuts', JSON.stringify(shortcuts)); newShortcutInput.value = ''; renderShortcuts(); } }); emojiTrigger.addEventListener('click', () => emojiPicker.classList.toggle('d-none')); emojiPicker.addEventListener('emoji-click', e => { input.value += e.detail.unicode; emojiPicker.classList.add('d-none'); }); const renderContacts = () => { listEl.innerHTML = ''; contacts.forEach((contact) => { const item = document.createElement('div'); item.className = `contact-item ${contact.id === activeId ? 'active' : ''}`; item.style.padding = '10px'; item.style.borderBottom = '1px solid #eee'; item.style.cursor = 'pointer'; item.innerHTML = `
    ${contact.phone}
    ${contact.last_message || '暂无消息'}
    `; item.addEventListener('click', () => setActive(contact.id)); listEl.appendChild(item); }); }; const renderMessages = (messages) => { chatBody.innerHTML = ''; messages.forEach((msg) => { const item = document.createElement('div'); item.className = `message ${msg.direction === 'out' ? 'out' : 'in'}`; item.innerHTML = `
    ${msg.body}
    ${formatTime(msg.created_at)}
    `; chatBody.appendChild(item); }); chatBody.scrollTop = chatBody.scrollHeight; }; 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}`); renderMessages(data.messages || []); }; const setActive = async (id) => { activeId = id; const contact = contacts.find(c => c.id === id); chatTitle.textContent = contact ? contact.phone : '未知联系人'; renderContacts(); await loadMessages(); }; 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(); }); renderShortcuts(); loadContacts(); setInterval(loadContacts, 3000); }; document.addEventListener('DOMContentLoaded', () => { if (document.body.dataset.page === 'agent') initAgent(); });