diff --git a/public/css/chat.css b/public/css/chat.css index 28fbe2d..1cf691a 100644 --- a/public/css/chat.css +++ b/public/css/chat.css @@ -452,14 +452,10 @@ a:hover { line-height: 1; } -.user-badge-icon::after { - content: attr(data-instant-tooltip); - position: absolute; - left: calc(100% + 8px); - top: 50%; +.chat-hover-tooltip { + position: fixed; z-index: 10030; - min-width: max-content; - max-width: 200px; + max-width: min(220px, calc(100vw - 20px)); padding: 4px 8px; border: 1px solid #244d77; border-radius: 4px; @@ -469,33 +465,29 @@ a:hover { line-height: 1.35; white-space: nowrap; box-shadow: 0 6px 18px rgba(17, 34, 51, 0.24); - transform: translateY(-50%); - opacity: 0; - visibility: hidden; pointer-events: none; } -.user-badge-icon::before { +.chat-hover-tooltip::after { content: ''; position: absolute; - left: calc(100% + 4px); top: 50%; - z-index: 10031; width: 6px; height: 6px; - border-left: 1px solid #244d77; - border-bottom: 1px solid #244d77; background: #fff9d8; transform: translateY(-50%) rotate(45deg); - opacity: 0; - visibility: hidden; - pointer-events: none; } -.user-badge-icon:hover::after, -.user-badge-icon:hover::before { - opacity: 1; - visibility: visible; +.chat-hover-tooltip[data-side="right"]::after { + left: -4px; + border-left: 1px solid #244d77; + border-bottom: 1px solid #244d77; +} + +.chat-hover-tooltip[data-side="left"]::after { + right: -4px; + border-top: 1px solid #244d77; + border-right: 1px solid #244d77; } /* 在线人数统计 */ diff --git a/resources/views/chat/partials/layout/right-panel.blade.php b/resources/views/chat/partials/layout/right-panel.blade.php index 4baf970..76ace94 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 人 + +{{-- 图标悬浮文字提示:脱离滚动容器渲染,避免被右侧名单区域裁切 --}} + diff --git a/resources/views/chat/partials/scripts.blade.php b/resources/views/chat/partials/scripts.blade.php index c6d2ddd..70d45e6 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,105 @@ } } + /** + * 把小气泡提示定位到图标旁边,不侵入右侧名单结构。 + * + * @param {HTMLElement} trigger 当前悬浮的图标元素 + */ + function positionHoverTooltip(trigger) { + if (!hoverTooltip || !trigger) { + return; + } + + const offset = 10; + const rect = trigger.getBoundingClientRect(); + const tooltipWidth = hoverTooltip.offsetWidth; + const tooltipHeight = hoverTooltip.offsetHeight; + const fitsRight = rect.right + offset + tooltipWidth <= window.innerWidth - 8; + const side = fitsRight ? 'right' : 'left'; + const nextLeft = fitsRight + ? rect.right + offset + : Math.max(8, rect.left - tooltipWidth - offset); + const nextTop = Math.min( + Math.max(8, rect.top + (rect.height - tooltipHeight) / 2), + window.innerHeight - tooltipHeight - 8 + ); + + hoverTooltip.dataset.side = side; + hoverTooltip.style.left = `${nextLeft}px`; + hoverTooltip.style.top = `${nextTop}px`; + } + + /** + * 显示图标旁边的小气泡文字提示。 + * + * @param {HTMLElement|null} trigger 当前悬浮的图标元素 + */ + function showHoverTooltip(trigger) { + if (!hoverTooltip || !trigger) { + return; + } + + const tooltipText = trigger.dataset.instantTooltip || ''; + if (!tooltipText) { + return; + } + + activeTooltipTrigger = trigger; + hoverTooltip.textContent = tooltipText; + hoverTooltip.style.display = 'block'; + positionHoverTooltip(trigger); + } + + /** + * 隐藏图标提示气泡。 + */ + function hideHoverTooltip() { + if (!hoverTooltip) { + return; + } + + activeTooltipTrigger = null; + hoverTooltip.style.display = 'none'; + hoverTooltip.textContent = ''; + delete hoverTooltip.dataset.side; + } + + // 通过事件委托处理动态生成的徽章提示,确保 hover 后立刻显示。 + document.addEventListener('mouseover', (event) => { + const trigger = event.target.closest('.user-badge-icon[data-instant-tooltip]'); + if (!trigger) { + return; + } + + showHoverTooltip(trigger); + }); + + document.addEventListener('mouseout', (event) => { + const trigger = event.target.closest('.user-badge-icon[data-instant-tooltip]'); + if (!trigger || trigger !== activeTooltipTrigger) { + return; + } + + if (event.relatedTarget && trigger.contains(event.relatedTarget)) { + return; + } + + hideHoverTooltip(); + }); + + window.addEventListener('scroll', () => { + if (activeTooltipTrigger) { + positionHoverTooltip(activeTooltipTrigger); + } + }, true); + + window.addEventListener('resize', () => { + if (activeTooltipTrigger) { + positionHoverTooltip(activeTooltipTrigger); + } + }); + // ── 渲染在线人员列表(支持排序) ────────────────── /** * 核心渲染函数:将在线用户渲染到指定容器(桌面端名单区和手机端抽屉共用)