186 lines
6.3 KiB
JavaScript
186 lines
6.3 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
const app = document.getElementById('chat-app');
|
|
if (!app) return;
|
|
|
|
const twilioSelect = document.getElementById('twilio-select');
|
|
const conversationList = document.getElementById('conversation-list');
|
|
const messageList = document.getElementById('message-list');
|
|
const messageForm = document.getElementById('message-form');
|
|
const messageInput = document.getElementById('message-input');
|
|
const conversationTitle = document.getElementById('conversation-title');
|
|
const conversationStatus = document.getElementById('conversation-status');
|
|
const searchInput = document.getElementById('search-input');
|
|
const addContactForm = document.getElementById('add-contact-form');
|
|
const alertBox = document.getElementById('chat-alert');
|
|
|
|
let activeConversationId = null;
|
|
let activeTwilioId = twilioSelect ? twilioSelect.value : null;
|
|
let pollTimer = null;
|
|
|
|
const showAlert = (text, type = 'info') => {
|
|
if (!alertBox) return;
|
|
alertBox.textContent = text;
|
|
alertBox.className = `alert alert-${type} alert-inline`;
|
|
alertBox.classList.remove('d-none');
|
|
setTimeout(() => alertBox.classList.add('d-none'), 3000);
|
|
};
|
|
|
|
const formatTime = (iso) => {
|
|
if (!iso) return '';
|
|
const dt = new Date(iso.replace(' ', 'T'));
|
|
return dt.toLocaleString();
|
|
};
|
|
|
|
const renderConversations = (items) => {
|
|
conversationList.innerHTML = '';
|
|
if (!items.length) {
|
|
conversationList.innerHTML = '<div class="p-3 muted">暂无会话。添加号码开始对话。</div>';
|
|
return;
|
|
}
|
|
items.forEach((item) => {
|
|
const div = document.createElement('div');
|
|
div.className = 'conversation-item' + (item.id === activeConversationId ? ' active' : '');
|
|
div.dataset.id = item.id;
|
|
div.innerHTML = `
|
|
<div class="conversation-avatar">${(item.name || item.phone).slice(0, 2).toUpperCase()}</div>
|
|
<div class="conversation-meta">
|
|
<div class="fw-semibold">${item.name || item.phone}</div>
|
|
<small>${item.last_message || '暂无消息'}</small>
|
|
</div>
|
|
<small class="text-muted">${item.last_time ? formatTime(item.last_time) : ''}</small>
|
|
`;
|
|
div.addEventListener('click', () => {
|
|
activeConversationId = item.id;
|
|
conversationTitle.textContent = item.name || item.phone;
|
|
conversationStatus.textContent = item.phone;
|
|
fetchMessages();
|
|
fetchConversations();
|
|
});
|
|
conversationList.appendChild(div);
|
|
});
|
|
};
|
|
|
|
const renderMessages = (items) => {
|
|
messageList.innerHTML = '';
|
|
if (!items.length) {
|
|
messageList.innerHTML = '<div class="muted">暂无消息,开始发送第一条短信。</div>';
|
|
return;
|
|
}
|
|
items.forEach((msg) => {
|
|
const div = document.createElement('div');
|
|
div.className = `message ${msg.direction === 'outbound' ? 'outbound' : 'inbound'}`;
|
|
div.innerHTML = `
|
|
<div>${msg.body}</div>
|
|
<span class="timestamp">${formatTime(msg.created_at)}</span>
|
|
`;
|
|
messageList.appendChild(div);
|
|
});
|
|
messageList.scrollTop = messageList.scrollHeight;
|
|
};
|
|
|
|
const fetchConversations = async () => {
|
|
if (!activeTwilioId) return;
|
|
const res = await fetch(`api/conversations.php?twilio_number_id=${activeTwilioId}`);
|
|
const data = await res.json();
|
|
renderConversations(data.items || []);
|
|
if (!activeConversationId && data.items && data.items.length) {
|
|
activeConversationId = data.items[0].id;
|
|
conversationTitle.textContent = data.items[0].name || data.items[0].phone;
|
|
conversationStatus.textContent = data.items[0].phone;
|
|
fetchMessages();
|
|
}
|
|
};
|
|
|
|
const fetchMessages = async () => {
|
|
if (!activeConversationId) {
|
|
renderMessages([]);
|
|
return;
|
|
}
|
|
const res = await fetch(`api/messages.php?conversation_id=${activeConversationId}`);
|
|
const data = await res.json();
|
|
renderMessages(data.items || []);
|
|
};
|
|
|
|
const startPolling = () => {
|
|
if (pollTimer) clearInterval(pollTimer);
|
|
pollTimer = setInterval(() => {
|
|
fetchConversations();
|
|
fetchMessages();
|
|
}, 5000);
|
|
};
|
|
|
|
if (twilioSelect) {
|
|
twilioSelect.addEventListener('change', () => {
|
|
activeTwilioId = twilioSelect.value;
|
|
activeConversationId = null;
|
|
conversationTitle.textContent = '请选择会话';
|
|
conversationStatus.textContent = '';
|
|
fetchConversations();
|
|
fetchMessages();
|
|
});
|
|
}
|
|
|
|
if (searchInput) {
|
|
searchInput.addEventListener('input', () => {
|
|
const term = searchInput.value.toLowerCase();
|
|
const items = conversationList.querySelectorAll('.conversation-item');
|
|
items.forEach((item) => {
|
|
const text = item.textContent.toLowerCase();
|
|
item.style.display = text.includes(term) ? 'flex' : 'none';
|
|
});
|
|
});
|
|
}
|
|
|
|
if (messageForm) {
|
|
messageForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const body = messageInput.value.trim();
|
|
if (!body) return;
|
|
if (!activeConversationId) {
|
|
showAlert('请先选择一个会话。', 'warning');
|
|
return;
|
|
}
|
|
messageInput.value = '';
|
|
const res = await fetch('api/send_message.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ conversation_id: activeConversationId, body })
|
|
});
|
|
const data = await res.json();
|
|
if (!data.success) {
|
|
showAlert(data.error || '发送失败', 'danger');
|
|
}
|
|
fetchMessages();
|
|
fetchConversations();
|
|
});
|
|
}
|
|
|
|
if (addContactForm) {
|
|
addContactForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const formData = new FormData(addContactForm);
|
|
formData.append('twilio_number_id', activeTwilioId || '');
|
|
const res = await fetch('api/add_contact.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
activeConversationId = data.conversation_id;
|
|
fetchConversations();
|
|
fetchMessages();
|
|
showAlert('已添加号码并创建会话。', 'success');
|
|
const modal = bootstrap.Modal.getInstance(document.getElementById('addContactModal'));
|
|
if (modal) modal.hide();
|
|
addContactForm.reset();
|
|
} else {
|
|
showAlert(data.error || '添加失败', 'danger');
|
|
}
|
|
});
|
|
}
|
|
|
|
fetchConversations();
|
|
fetchMessages();
|
|
startPolling();
|
|
});
|