From 2f09d5e2ed26daaf23887b6df396fd25dde14db8 Mon Sep 17 00:00:00 2001 From: lkddi Date: Sat, 25 Apr 2026 08:18:01 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E8=81=8A=E5=A4=A9=E5=AE=A4?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=85=B3=E9=94=AE=E9=80=BB=E8=BE=91=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/js/chat-room/message-queue.js | 3 +++ resources/js/chat-room/preferences-status.js | 6 +++++- resources/js/chat-room/rooms.js | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/resources/js/chat-room/message-queue.js b/resources/js/chat-room/message-queue.js index 06349b9..25c1733 100644 --- a/resources/js/chat-room/message-queue.js +++ b/resources/js/chat-room/message-queue.js @@ -9,11 +9,13 @@ export function createMessageQueue(options = {}) { const limit = Math.max(Number.parseInt(options.limit, 10) || 200, 1); const queue = []; + // 标记当前帧是否已排队刷新,避免高频消息在同一帧内重复触发渲染。 let flushScheduled = false; const scheduler = typeof options.scheduler === "function" ? options.scheduler : (callback) => { + // 浏览器优先跟随 RAF 合并刷新,测试或无 RAF 环境回退到短 timeout。 const requestFrame = globalThis.requestAnimationFrame || ((handler) => globalThis.setTimeout(handler, 16)); return requestFrame(callback); @@ -34,6 +36,7 @@ export function createMessageQueue(options = {}) { enqueue(message) { queue.push(message); + // 队列超过上限时丢弃最旧消息,避免离线积压拖慢后续批量渲染。 while (queue.length > limit) { queue.shift(); } diff --git a/resources/js/chat-room/preferences-status.js b/resources/js/chat-room/preferences-status.js index f260449..b1d44cc 100644 --- a/resources/js/chat-room/preferences-status.js +++ b/resources/js/chat-room/preferences-status.js @@ -169,7 +169,7 @@ export function bindSoundMuteControl(onChange) { } /** - * 绑定系统播报屏蔽菜单打开与菜单内点击拦截事件。 + * 绑定功能菜单、每日状态编辑与系统播报屏蔽的统一事件代理。 * * @returns {void} */ @@ -198,6 +198,7 @@ export function bindBlockMenuControls() { return; } + // 功能菜单由 Blade 动态渲染,使用 document 代理避免重复绑定新节点。 const featureMenuTrigger = event.target.closest("[data-chat-feature-menu-toggle]"); if (featureMenuTrigger) { event.preventDefault(); @@ -206,6 +207,7 @@ export function bindBlockMenuControls() { return; } + // 每日状态编辑器仍保留存量全局函数,这里只负责把 data-* 事件转发出去。 const dailyStatusCloseButton = event.target.closest("[data-chat-daily-status-close]"); if (dailyStatusCloseButton) { event.preventDefault(); @@ -246,6 +248,7 @@ export function bindBlockMenuControls() { return; } + // 快捷功能区包含本地清理、签到和跳转类动作,统一收口到当前代理入口。 const localClearButton = event.target.closest("[data-chat-feature-local-clear]"); if (localClearButton) { event.preventDefault(); @@ -289,6 +292,7 @@ export function bindBlockMenuControls() { return; } + // 系统播报屏蔽菜单需要阻止内部点击冒泡,避免点击复选框时菜单被外层关闭。 const trigger = event.target.closest("[data-chat-block-menu-toggle]"); if (trigger) { event.preventDefault(); diff --git a/resources/js/chat-room/rooms.js b/resources/js/chat-room/rooms.js index 548689c..df0c6ab 100644 --- a/resources/js/chat-room/rooms.js +++ b/resources/js/chat-room/rooms.js @@ -39,6 +39,8 @@ export function resolveRoomUrl(roomId, roomUrlResolver = undefined) { /** * 生成可放入 onclick 属性的安全跳转语句。 + * 这里暂时保留属性字符串,是为了兼容现有 HTML 字符串渲染入口。 + * URL 先 JSON 字符串化再转义,避免地址内容突破属性上下文。 * * @param {number} roomId * @param {(roomId:number) => string} [roomUrlResolver]