diff --git a/server/public/index.html b/server/public/index.html index e8729ed..6242558 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -368,6 +368,14 @@ document.body.appendChild(overlay); } + function onImgThumbDblClick(img) { + const overlay = document.createElement('div'); + overlay.className = 'fullscreen-overlay'; + overlay.innerHTML = ``; + overlay.addEventListener('click', () => overlay.remove()); + document.body.appendChild(overlay); + } + function onBubbleDblClick(e) { e.stopPropagation(); const bubble = e.currentTarget; @@ -382,7 +390,7 @@ document.body.appendChild(overlay); } - // 临时“输入中...”气泡 + // 临时"输入中..."气泡 function insertTempBubble() { clearTempBubble(); const tempId = 'temp_' + Date.now(); @@ -391,10 +399,30 @@ messagesContainer.insertAdjacentHTML('beforeend', renderMessageHTML(tempMsg)); scrollToBottom(); tempBubbleTimer = setTimeout(() => { clearTempBubble(); }, 8000); + // 降级轮询:如果 WebSocket 消息丢失,定时从服务端拉取 + if (pollTimer) clearInterval(pollTimer); + let pollCount = 0; + pollTimer = setInterval(async () => { + pollCount++; + if (!currentRoom || pollCount > 5) { clearInterval(pollTimer); pollTimer = null; return; } + try { + const token = getToken(); + const res = await fetch(API + `/api/rooms/${currentRoom}/messages`, { headers: { 'Authorization': `Bearer ${token}` } }); + const msgs = await res.json(); + const lastCacheId = messageCache.filter(m => !m.isTemp && !m.isPlaceholder).slice(-1)[0]?.id; + const newMsgs = msgs.filter(m => !lastCacheId || m.id > lastCacheId); + if (newMsgs.length) { + clearInterval(pollTimer); pollTimer = null; + clearTempBubble(); + newMsgs.forEach(m => appendMessage(m)); + } + } catch(e) {} + }, 2000); } function clearTempBubble() { if (tempBubbleTimer) { clearTimeout(tempBubbleTimer); tempBubbleTimer = null; } + if (pollTimer) { clearInterval(pollTimer); pollTimer = null; } messageCache = messageCache.filter(m => !m.isTemp); const tempEls = messagesContainer.querySelectorAll('[data-msg-id^="temp_"]'); tempEls.forEach(el => el.remove()); @@ -404,6 +432,7 @@ let wsReconnectTimer = null; let reconnectAttempts = 0; const MAX_RECONNECT_DELAY = 30000; + let pollTimer = null; // 降级轮询定时器 function initWebSocket() { const token = getToken(); @@ -417,6 +446,7 @@ }; ws.onmessage = (e) => { const data = JSON.parse(e.data); + console.log('📩 WS 收到:', data.type); if (data.type === 'new_message') { const msg = data.message; if (currentRoom === msg.room_id) { @@ -733,7 +763,7 @@ const p = await res.json(); document.getElementById('detail-title').textContent = p.item; let attachHtml = ''; - if (p.attachments?.length) attachHtml = '
' + p.attachments.map(a => `${a.file_path}`).join('') + '
'; + if (p.attachments?.length) attachHtml = '
' + p.attachments.map(a => `${a.file_path}`).join('') + '
'; const historyHtml = p.history?.map(h => `
  • ${h.timestamp} ${h.user}: ${h.action}
  • `).join('') || ''; document.getElementById('detail-content').innerHTML = `
    数量:${p.quantity}
    diff --git a/server/server.js b/server/server.js index 7b165ed..edc45b7 100644 --- a/server/server.js +++ b/server/server.js @@ -236,7 +236,7 @@ app.get('/api/rooms/:roomId/purchases/export', auth, (req, res) => { // WebSocket const clients = new Map(); wss.on('connection', (ws, req) => { - //console.log('✅ WebSocket 客户端已连接'); + console.log('✅ WebSocket 客户端已连接'); const url = new URL(req.url, 'http://localhost'); const token = url.searchParams.get('token'); if (!token) return ws.close();