39235-vm/assets/js/main.js
2026-03-18 12:17:47 +00:00

196 lines
6.9 KiB
JavaScript

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 = `
<div class="d-flex">
<div class="toast-body">${message}</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>`;
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 =>
`<button class="btn btn-sm btn-outline-info me-1" onclick="document.querySelector('[data-chat-input]').value += '${s}'">${s}</button>`
).join('');
shortcutList.innerHTML = shortcuts.map((s, i) => `
<li class="list-group-item d-flex justify-content-between">
${s} <button class="btn btn-sm btn-danger" onclick="removeShortcut(${i})">删除</button>
</li>
`).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 = `
<div>
<div class="fw-semibold">${contact.phone}</div>
<div class="meta text-truncate" style="font-size: 0.8em; color: #666;">${contact.last_message || '暂无消息'}</div>
</div>
`;
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 = `<div>${msg.body}</div><div class="time">${formatTime(msg.created_at)}</div>`;
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();
});