附件图片双击放大显示
This commit is contained in:
@@ -368,6 +368,14 @@
|
|||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onImgThumbDblClick(img) {
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.className = 'fullscreen-overlay';
|
||||||
|
overlay.innerHTML = `<img src="${img.src}" style="max-width:100%;max-height:100%;object-fit:contain" alt="">`;
|
||||||
|
overlay.addEventListener('click', () => overlay.remove());
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
}
|
||||||
|
|
||||||
function onBubbleDblClick(e) {
|
function onBubbleDblClick(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const bubble = e.currentTarget;
|
const bubble = e.currentTarget;
|
||||||
@@ -382,7 +390,7 @@
|
|||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 临时“输入中...”气泡
|
// 临时"输入中..."气泡
|
||||||
function insertTempBubble() {
|
function insertTempBubble() {
|
||||||
clearTempBubble();
|
clearTempBubble();
|
||||||
const tempId = 'temp_' + Date.now();
|
const tempId = 'temp_' + Date.now();
|
||||||
@@ -391,10 +399,30 @@
|
|||||||
messagesContainer.insertAdjacentHTML('beforeend', renderMessageHTML(tempMsg));
|
messagesContainer.insertAdjacentHTML('beforeend', renderMessageHTML(tempMsg));
|
||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
tempBubbleTimer = setTimeout(() => { clearTempBubble(); }, 8000);
|
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() {
|
function clearTempBubble() {
|
||||||
if (tempBubbleTimer) { clearTimeout(tempBubbleTimer); tempBubbleTimer = null; }
|
if (tempBubbleTimer) { clearTimeout(tempBubbleTimer); tempBubbleTimer = null; }
|
||||||
|
if (pollTimer) { clearInterval(pollTimer); pollTimer = null; }
|
||||||
messageCache = messageCache.filter(m => !m.isTemp);
|
messageCache = messageCache.filter(m => !m.isTemp);
|
||||||
const tempEls = messagesContainer.querySelectorAll('[data-msg-id^="temp_"]');
|
const tempEls = messagesContainer.querySelectorAll('[data-msg-id^="temp_"]');
|
||||||
tempEls.forEach(el => el.remove());
|
tempEls.forEach(el => el.remove());
|
||||||
@@ -404,6 +432,7 @@
|
|||||||
let wsReconnectTimer = null;
|
let wsReconnectTimer = null;
|
||||||
let reconnectAttempts = 0;
|
let reconnectAttempts = 0;
|
||||||
const MAX_RECONNECT_DELAY = 30000;
|
const MAX_RECONNECT_DELAY = 30000;
|
||||||
|
let pollTimer = null; // 降级轮询定时器
|
||||||
|
|
||||||
function initWebSocket() {
|
function initWebSocket() {
|
||||||
const token = getToken();
|
const token = getToken();
|
||||||
@@ -417,6 +446,7 @@
|
|||||||
};
|
};
|
||||||
ws.onmessage = (e) => {
|
ws.onmessage = (e) => {
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
|
console.log('📩 WS 收到:', data.type);
|
||||||
if (data.type === 'new_message') {
|
if (data.type === 'new_message') {
|
||||||
const msg = data.message;
|
const msg = data.message;
|
||||||
if (currentRoom === msg.room_id) {
|
if (currentRoom === msg.room_id) {
|
||||||
@@ -733,7 +763,7 @@
|
|||||||
const p = await res.json();
|
const p = await res.json();
|
||||||
document.getElementById('detail-title').textContent = p.item;
|
document.getElementById('detail-title').textContent = p.item;
|
||||||
let attachHtml = '';
|
let attachHtml = '';
|
||||||
if (p.attachments?.length) attachHtml = '<div class="attachments">' + p.attachments.map(a => `<img class="img-thumb" src="${a.file_path}" alt="${a.file_path}">`).join('') + '</div>';
|
if (p.attachments?.length) attachHtml = '<div class="attachments">' + p.attachments.map(a => `<img class="img-thumb" src="${a.file_path}" alt="${a.file_path}" ondblclick="onImgThumbDblClick(this)">`).join('') + '</div>';
|
||||||
const historyHtml = p.history?.map(h => `<li><span class="history-time">${h.timestamp}</span> ${h.user}: ${h.action}</li>`).join('') || '';
|
const historyHtml = p.history?.map(h => `<li><span class="history-time">${h.timestamp}</span> ${h.user}: ${h.action}</li>`).join('') || '';
|
||||||
document.getElementById('detail-content').innerHTML = `
|
document.getElementById('detail-content').innerHTML = `
|
||||||
<div class="detail-row"><strong>数量:</strong>${p.quantity}</div>
|
<div class="detail-row"><strong>数量:</strong>${p.quantity}</div>
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ app.get('/api/rooms/:roomId/purchases/export', auth, (req, res) => {
|
|||||||
// WebSocket
|
// WebSocket
|
||||||
const clients = new Map();
|
const clients = new Map();
|
||||||
wss.on('connection', (ws, req) => {
|
wss.on('connection', (ws, req) => {
|
||||||
//console.log('✅ WebSocket 客户端已连接');
|
console.log('✅ WebSocket 客户端已连接');
|
||||||
const url = new URL(req.url, 'http://localhost');
|
const url = new URL(req.url, 'http://localhost');
|
||||||
const token = url.searchParams.get('token');
|
const token = url.searchParams.get('token');
|
||||||
if (!token) return ws.close();
|
if (!token) return ws.close();
|
||||||
|
|||||||
Reference in New Issue
Block a user