163 lines
5.2 KiB
JavaScript
163 lines
5.2 KiB
JavaScript
// 状态
|
|
let currentUser = "";
|
|
let conversations = [];
|
|
|
|
// 初始化
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
loadConversations();
|
|
});
|
|
|
|
// Toast
|
|
function showToast(msg, type = "success") {
|
|
const el = document.getElementById("toast");
|
|
el.textContent = msg;
|
|
el.className = "toast toast-" + type;
|
|
el.style.display = "block";
|
|
el.style.opacity = "1";
|
|
setTimeout(() => { el.style.opacity = "0"; setTimeout(() => el.style.display = "none", 300); }, 2000);
|
|
}
|
|
|
|
// 加载会话列表
|
|
async function loadConversations() {
|
|
try {
|
|
const resp = await fetch("/api/conversations");
|
|
const data = await resp.json();
|
|
conversations = data.conversations || [];
|
|
renderConversations();
|
|
|
|
if (conversations.length === 0) {
|
|
showToast('暂无会话,请先点击\"同步拉取\"获取消息', "error");
|
|
}
|
|
} catch (e) {
|
|
showToast("加载会话失败: " + e.message, "error");
|
|
}
|
|
}
|
|
|
|
// 渲染会话列表
|
|
function renderConversations() {
|
|
const list = document.getElementById("conversationList");
|
|
if (conversations.length === 0) {
|
|
list.innerHTML = '<div style="padding:20px;color:#999;text-align:center;">暂无会话</div>';
|
|
return;
|
|
}
|
|
list.innerHTML = conversations.map(c => `
|
|
<div class="conv-item ${c.external_userid === currentUser ? 'active' : ''}"
|
|
onclick="selectConversation('${c.external_userid}')">
|
|
<div class="userid">${c.external_userid}</div>
|
|
<div class="preview">${escapeHtml(c.latest_content || '[非文本消息]')}</div>
|
|
<div class="time">${formatTime(c.latest_time)}</div>
|
|
</div>
|
|
`).join("");
|
|
}
|
|
|
|
// 选择会话
|
|
async function selectConversation(userid) {
|
|
currentUser = userid;
|
|
document.getElementById("msgHeader").textContent = "客户: " + userid;
|
|
document.getElementById("sendBtn").disabled = false;
|
|
document.getElementById("msgInput").disabled = false;
|
|
renderConversations();
|
|
await loadMessages(userid);
|
|
}
|
|
|
|
// 加载消息
|
|
async function loadMessages(userid) {
|
|
try {
|
|
const resp = await fetch(`/api/messages?external_userid=${userid}`);
|
|
const data = await resp.json();
|
|
const msgs = data.messages || [];
|
|
renderMessages(msgs);
|
|
} catch (e) {
|
|
showToast("加载消息失败: " + e.message, "error");
|
|
}
|
|
}
|
|
|
|
// 渲染消息
|
|
function renderMessages(msgs) {
|
|
const container = document.getElementById("messageList");
|
|
if (msgs.length === 0) {
|
|
container.innerHTML = '<div class="empty-state">暂无消息</div>';
|
|
return;
|
|
}
|
|
container.innerHTML = msgs.map(m => `
|
|
<div class="msg-bubble ${m.direction === 'outbound' ? 'msg-outbound' : 'msg-inbound'}">
|
|
<div>${escapeHtml(m.content || `[${m.msgtype}]`)}</div>
|
|
<div class="msg-time">${formatTime(m.send_time)}</div>
|
|
</div>
|
|
`).join("");
|
|
container.scrollTop = container.scrollHeight;
|
|
}
|
|
|
|
// 发送消息
|
|
async function sendMessage() {
|
|
if (!currentUser) return;
|
|
const input = document.getElementById("msgInput");
|
|
const content = input.value.trim();
|
|
if (!content) return;
|
|
|
|
const btn = document.getElementById("sendBtn");
|
|
btn.disabled = true;
|
|
btn.textContent = "发送中...";
|
|
|
|
try {
|
|
const resp = await fetch("/api/send", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ external_userid: currentUser, content }),
|
|
});
|
|
const result = await resp.json();
|
|
if (result.errcode === 0) {
|
|
input.value = "";
|
|
showToast("发送成功");
|
|
await loadMessages(currentUser);
|
|
} else {
|
|
showToast("发送失败: " + (result.errmsg || JSON.stringify(result)), "error");
|
|
}
|
|
} catch (e) {
|
|
showToast("发送失败: " + e.message, "error");
|
|
} finally {
|
|
btn.disabled = false;
|
|
btn.textContent = "发送";
|
|
}
|
|
}
|
|
|
|
// 同步拉取
|
|
async function syncMessages() {
|
|
const statusEl = document.getElementById("syncStatus");
|
|
statusEl.textContent = "同步中...";
|
|
try {
|
|
const resp = await fetch("/api/sync", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({}),
|
|
});
|
|
const data = await resp.json();
|
|
if (data.errcode === 0) {
|
|
showToast(`同步成功,获取 ${data.total} 条消息,新增入库 ${data.saved} 条`);
|
|
await loadConversations();
|
|
if (currentUser) await loadMessages(currentUser);
|
|
} else {
|
|
showToast("同步失败: " + (data.errmsg || JSON.stringify(data)), "error");
|
|
}
|
|
} catch (e) {
|
|
showToast("同步失败: " + e.message, "error");
|
|
} finally {
|
|
statusEl.textContent = "";
|
|
}
|
|
}
|
|
|
|
// 格式化时间
|
|
function formatTime(iso) {
|
|
if (!iso) return "";
|
|
const d = new Date(iso);
|
|
const pad = (n) => String(n).padStart(2, "0");
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
}
|
|
|
|
// HTML 转义
|
|
function escapeHtml(text) {
|
|
const div = document.createElement("div");
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|