diff --git a/public/css/chat.css b/public/css/chat.css index c88a6ee..f7a5b69 100644 --- a/public/css/chat.css +++ b/public/css/chat.css @@ -444,6 +444,31 @@ a:hover { color: #999; } +.user-badge-icon { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + line-height: 1; +} + +.chat-hover-tooltip { + position: fixed; + z-index: 10030; + max-width: min(220px, calc(100vw - 16px)); + padding: 5px 8px; + border: 1px solid #244d77; + border-radius: 4px; + background: linear-gradient(to bottom, #fffef2, #fff6c9); + color: #243b53; + font-size: 11px; + line-height: 1.4; + box-shadow: 0 6px 18px rgba(17, 34, 51, 0.24); + pointer-events: none; + white-space: normal; + word-break: break-word; +} + /* 在线人数统计 */ .online-stats { background: var(--bg-bar); @@ -901,4 +926,4 @@ a:hover { #mobile-drawer-users { display: none !important; } -} \ No newline at end of file +} diff --git a/resources/views/chat/partials/layout/right-panel.blade.php b/resources/views/chat/partials/layout/right-panel.blade.php index 4baf970..01a40d6 100644 --- a/resources/views/chat/partials/layout/right-panel.blade.php +++ b/resources/views/chat/partials/layout/right-panel.blade.php @@ -72,3 +72,6 @@ 在线: 0 人 + +{{-- 右侧名单即时提示层:替代浏览器原生 title 的延迟提示 --}} + diff --git a/resources/views/chat/partials/scripts.blade.php b/resources/views/chat/partials/scripts.blade.php index fda2fd6..6f4e3bd 100644 --- a/resources/views/chat/partials/scripts.blade.php +++ b/resources/views/chat/partials/scripts.blade.php @@ -40,6 +40,8 @@ const BLOCKED_SYSTEM_SENDERS_STORAGE_KEY = 'chat_blocked_system_senders'; const CHAT_SOUND_MUTED_STORAGE_KEY = 'chat_sound_muted'; const BLOCKABLE_SYSTEM_SENDERS = ['钓鱼播报', '星海小博士', '百家乐', '跑马', '神秘箱子']; + const hoverTooltip = document.getElementById('chat-hover-tooltip'); + let activeTooltipTrigger = null; // ── 消息区:手机端双触发打开用户名片(PC 端靠 ondblclick 内联属性)── // span[data-u] 由 clickableUser() 生成,touchend 委托至容器避免每条消息单独绑定 @@ -833,6 +835,94 @@ } } + /** + * 根据鼠标位置摆放即时提示层,避免超出浏览器可视区域。 + * + * @param {MouseEvent} event 鼠标事件对象 + */ + function positionHoverTooltip(event) { + if (!hoverTooltip) { + return; + } + + const offset = 14; + const tooltipWidth = hoverTooltip.offsetWidth; + const tooltipHeight = hoverTooltip.offsetHeight; + const maxLeft = window.innerWidth - tooltipWidth - 8; + const maxTop = window.innerHeight - tooltipHeight - 8; + const nextLeft = Math.min(event.clientX + offset, Math.max(8, maxLeft)); + const nextTop = Math.min(event.clientY + offset, Math.max(8, maxTop)); + + hoverTooltip.style.left = `${nextLeft}px`; + hoverTooltip.style.top = `${nextTop}px`; + } + + /** + * 立即显示右侧名单图标提示,替代浏览器原生 title 的延迟行为。 + * + * @param {HTMLElement|null} trigger 触发提示的图标元素 + * @param {MouseEvent} event 鼠标事件对象 + */ + function showHoverTooltip(trigger, event) { + if (!hoverTooltip || !trigger) { + return; + } + + const tooltipText = trigger.dataset.instantTooltip || ''; + if (!tooltipText) { + return; + } + + activeTooltipTrigger = trigger; + hoverTooltip.textContent = tooltipText; + hoverTooltip.style.display = 'block'; + positionHoverTooltip(event); + } + + /** + * 隐藏右侧名单的即时提示层。 + */ + function hideHoverTooltip() { + if (!hoverTooltip) { + return; + } + + activeTooltipTrigger = null; + hoverTooltip.style.display = 'none'; + hoverTooltip.textContent = ''; + } + + // 通过事件委托托管动态生成的名单徽章,确保鼠标移入后立刻显示提示。 + document.addEventListener('mouseover', (event) => { + const trigger = event.target.closest('[data-instant-tooltip]'); + if (!trigger) { + return; + } + + showHoverTooltip(trigger, event); + }); + + document.addEventListener('mousemove', (event) => { + if (!activeTooltipTrigger) { + return; + } + + positionHoverTooltip(event); + }); + + document.addEventListener('mouseout', (event) => { + const trigger = event.target.closest('[data-instant-tooltip]'); + if (!trigger || trigger !== activeTooltipTrigger) { + return; + } + + if (event.relatedTarget && trigger.contains(event.relatedTarget)) { + return; + } + + hideHoverTooltip(); + }); + // ── 渲染在线人员列表(支持排序) ────────────────── /** * 核心渲染函数:将在线用户渲染到指定容器(桌面端名单区和手机端抽屉共用) @@ -889,14 +979,18 @@ let badges = ''; if (user.position_icon) { const posTitle = (user.position_name || '在职') + ' · ' + username; + const safePosTitle = escapeHtml(String(posTitle)); + const safePositionIcon = escapeHtml(String(user.position_icon || '🎖️')); badges += - `${user.position_icon}`; + `${safePositionIcon}`; } else if (user.is_admin) { - badges += `🎖️`; + badges += `🎖️`; } else if (user.vip_icon) { const vipColor = user.vip_color || '#f59e0b'; + const safeVipTitle = escapeHtml(String(user.vip_name || 'VIP')); + const safeVipIcon = escapeHtml(String(user.vip_icon || '👑')); badges += - `${user.vip_icon}`; + `${safeVipIcon}`; } // 女生名字使用玫粉色