迁移聊天室前端工具并优化消息渲染

This commit is contained in:
2026-04-25 03:34:31 +08:00
parent e3cba255f9
commit f1d8d20180
12 changed files with 786 additions and 154 deletions
@@ -83,7 +83,7 @@
</select>
<input id="mob-user-search-input" type="text" placeholder="搜索用户..."
style="flex:2;font-size:11px;border:1px solid #b0c8e0;border-radius:3px;padding:2px 6px;color:#333;"
oninput="renderMobileUserList()">
oninput="scheduleRenderMobileUserList()">
</div>
{{-- 用户列表容器 --}}
@@ -127,6 +127,10 @@
* @type {string|null}
*/
let _mobileDrawerOpen = null;
let _mobileUserListRenderTimer = null;
let _mobileRoomsOnlineStatusCache = null;
let _mobileRoomsOnlineStatusCacheAt = 0;
const MOBILE_ROOMS_ONLINE_STATUS_CACHE_TTL = 10000;
/**
* 打开指定抽屉
@@ -228,6 +232,21 @@
if (footerEl) footerEl.textContent = count;
}
/**
* 调度手机端在线名单渲染,避免搜索输入时同步重建整份名单。
*/
function scheduleRenderMobileUserList() {
if (_mobileUserListRenderTimer !== null) {
return;
}
const scheduleRender = window.requestAnimationFrame || ((callback) => window.setTimeout(callback, 16));
_mobileUserListRenderTimer = scheduleRender(() => {
_mobileUserListRenderTimer = null;
renderMobileUserList();
});
}
/**
* 拉取房间列表并渲染到手机端房间容器
*/
@@ -235,34 +254,64 @@
const container = document.getElementById('mob-rooms-online-list');
if (!container) return;
if (_mobileRoomsOnlineStatusCache && Date.now() - _mobileRoomsOnlineStatusCacheAt < MOBILE_ROOMS_ONLINE_STATUS_CACHE_TTL) {
renderMobileRoomList(_mobileRoomsOnlineStatusCache, container);
return;
}
container.innerHTML = '<div style="text-align:center;color:#aaa;padding:16px;font-size:11px;">加载中...</div>';
fetch('{{ route('chat.rooms-online-status') }}')
.then(r => r.json())
.then(data => {
if (!data.rooms || !data.rooms.length) {
container.innerHTML = '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>';
return;
}
const currentRoomId = window.chatContext?.roomId;
const roomRows = data.rooms.map(room => {
const roomId = Number.parseInt(room.id, 10);
if (!Number.isInteger(roomId)) {
return '';
}
_mobileRoomsOnlineStatusCache = data;
_mobileRoomsOnlineStatusCacheAt = Date.now();
renderMobileRoomList(data, container);
})
.catch(() => {
container.innerHTML = '<div style="text-align:center;color:#f00;padding:10px;font-size:11px;">加载失败</div>';
});
}
const isCurrent = roomId === currentRoomId;
const bg = isCurrent ? '#ecf4ff' : '#fff';
const nameColor = isCurrent ? '#336699' : (room.door_open ? '#444' : '#bbb');
const safeRoomName = escapeMobileDrawerHtml(String(room.name ?? ''));
const safeOnlineCount = Math.max(Number.parseInt(room.online, 10) || 0, 0);
const badge = safeOnlineCount > 0
? `<span style="background:#e8f5e9;color:#2e7d32;border-radius:8px;padding:0 6px;font-size:10px;font-weight:bold;">${safeOnlineCount}人</span>`
: `<span style="background:#f5f5f5;color:#bbb;border-radius:8px;padding:0 6px;font-size:10px;">空</span>`;
const currentTag = isCurrent ? `<span style="font-size:9px;color:#7090b0;margin-left:3px;">当前</span>` : '';
const clickAttr = isCurrent ? '' : `onclick="location.href='/room/${roomId}'"`;
/**
* 渲染手机端房间列表。
*
* @param {Object} data 接口返回数据
* @param {HTMLElement} container 目标容器
*/
function renderMobileRoomList(data, container) {
if (window.ChatRoomTools?.renderRoomsOnlineStatusToContainer) {
window.ChatRoomTools.renderRoomsOnlineStatusToContainer(data, container, {
currentRoomId: window.chatContext?.roomId,
variant: 'mobile',
emptyHtml: '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>',
});
return;
}
return `<div ${clickAttr}
if (!data.rooms || !data.rooms.length) {
container.innerHTML = '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>';
return;
}
const currentRoomId = window.chatContext?.roomId;
const roomRows = data.rooms.map(room => {
const roomId = Number.parseInt(room.id, 10);
if (!Number.isInteger(roomId)) {
return '';
}
const isCurrent = roomId === currentRoomId;
const bg = isCurrent ? '#ecf4ff' : '#fff';
const nameColor = isCurrent ? '#336699' : (room.door_open ? '#444' : '#bbb');
const safeRoomName = escapeMobileDrawerHtml(String(room.name ?? ''));
const safeOnlineCount = Math.max(Number.parseInt(room.online, 10) || 0, 0);
const badge = safeOnlineCount > 0
? `<span style="background:#e8f5e9;color:#2e7d32;border-radius:8px;padding:0 6px;font-size:10px;font-weight:bold;">${safeOnlineCount}人</span>`
: `<span style="background:#f5f5f5;color:#bbb;border-radius:8px;padding:0 6px;font-size:10px;">空</span>`;
const currentTag = isCurrent ? `<span style="font-size:9px;color:#7090b0;margin-left:3px;">当前</span>` : '';
const clickAttr = isCurrent ? '' : `onclick="location.href='/room/${roomId}'"`;
return `<div ${clickAttr}
style="display:flex;align-items:center;justify-content:space-between;
padding:6px 10px;border-bottom:1px solid #eef2f8;background:${bg};
cursor:${isCurrent ? 'default' : 'pointer'};">
@@ -270,13 +319,9 @@
${safeRoomName}${currentTag}
</span>${badge}
</div>`;
}).filter(Boolean).join('');
}).filter(Boolean).join('');
container.innerHTML = roomRows || '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>';
})
.catch(() => {
container.innerHTML = '<div style="text-align:center;color:#f00;padding:10px;font-size:11px;">加载失败</div>';
});
container.innerHTML = roomRows || '<div style="text-align:center;color:#bbb;padding:16px;font-size:11px;">暂无房间</div>';
}
/**