/* 文档来源 https://blog.csdn.net/weixin_41277748/article/details/138855857 代码来源 https://github.com/DearWQ/wq-admin-system/blob/main/src/directive/toolTip/MyToolTip.vue */ // 引入组件 import {createApp, nextTick} from "vue"; import MyToolTip from './index.vue' function getViewportSize() { return { windowWidth: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, windowHeight: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight }; } let windowWidth = getViewportSize().windowWidth let windowHeight = getViewportSize().windowHeight // 位置定位 function calculationLocation(el, target, placements) { if (!el || !target) return; el.tooltipPosition.y = 0; el.tooltipPosition.x = 0; // @ts-ignore let el_dom = target.w_tooltip.firstElementChild.getBoundingClientRect() let target_dom = target.getBoundingClientRect() if (placements === "left") { el.tooltipPosition.x = 0 el.tooltipPosition.y = target_dom.y - target_dom.height / 2 + 14 el.tooltipPosition.right = windowWidth - target_dom.left + target_dom.height / 2 - 22 } else if (placements === "left-right") { el.tooltipPosition.x = 0 el.tooltipPosition.y = target_dom.y - (el_dom?.height || 0) / 2 + target_dom.height / 2 el.tooltipPosition.left = target_dom.left + target_dom.width + 10 } else if (placements === "bottom") { el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - (el_dom?.width || 0) / 2 el.tooltipPosition.y = target_dom.y + target_dom.height + 10 if (el.tooltipPosition.x > (windowWidth-el_dom.width) && target_dom.left + el_dom.width > windowWidth) { el.tooltipPosition.x = windowWidth - el_dom.width el.arrowPosition.left = (el_dom.width / 10) * 8 } if (el.tooltipPosition.x < 0) { el.arrowPosition.left = (el_dom.width / 10) * 2 } } else if (placements === "right") { el.tooltipPosition.x = 0 el.tooltipPosition.y = target_dom.y - (el_dom?.height || 0) / 2 + target_dom.height / 2 el.tooltipPosition.left = target_dom.left + target_dom.width + 10 } else if (placements === "right-left") { //提示文字的位置 el.tooltipPosition.x = 0; el.tooltipPosition.y = target_dom.y - target_dom.height / 2; el.tooltipPosition.right = windowWidth - target_dom.left + target_dom.height / 2 //三角形的位置 el.arrowPosition.top = target_dom.height } else if (placements === "top") { el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - (el_dom?.width || 0) / 2 el.tooltipPosition.y = target_dom.y - target_dom.height - 11 if (el.tooltipPosition.x > (windowWidth-el_dom.width) && target_dom.left + el_dom.width > windowWidth) { el.tooltipPosition.x = windowWidth - el_dom.width el.arrowPosition.left = (el_dom.width / 10) * 9 } if (el.tooltipPosition.x < 0) { el.arrowPosition.left = (el_dom.width / 10) * 2 } } el.tooltipPosition.x = el.tooltipPosition.x < 0 ? 0 : el.tooltipPosition.x el.tooltipPosition.y = el.tooltipPosition.y < 0 ? 0 : el.tooltipPosition.y } /** * 处理边界情况 * @param {HTMLElement} el * @param direction * return {string} props */ function dealDomBoundary(el, direction) { let target_dom = el.getBoundingClientRect() //处理上下左右边界情况 if (target_dom.left < 150 && direction === 'left') { direction = 'left-right' } if (target_dom.right > (windowWidth - 150) && direction === 'right') { direction = 'right-left' } if (target_dom.top > windowHeight / 100 * 90 && direction === 'bottom') { direction = 'top' } if (target_dom.top < windowHeight / 100 * 10 && direction === 'top') { direction = 'bottom' } return direction } // 方向 const allPlacements = ['left', 'bottom', 'right', 'top'] function getElStyleAttr(element, attr) { const styles = window.getComputedStyle(element) // @ts-ignore return styles[attr] } const isOverflow = (target) => { const scrollWidth = target.scrollWidth const offsetWidth = target.offsetWidth const range = document.createRange() range.setStart(target, 0) range.setEnd(target, target.childNodes.length) const rangeWidth = range.getBoundingClientRect().width const padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0) return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth } export const ellipsisTooltip = localStorage.getItem('useEllipseTooltip') ? { mounted(el, binding) { //获取指令的参数 const { value: { placement, content, destroyOnLeave } = {} } = binding; // 加上超出...样式 el.style.overflow = "hidden"; el.style.textOverflow = "ellipsis"; el.style.whiteSpace = "nowrap"; windowWidth = getViewportSize().windowWidth windowHeight = getViewportSize().windowHeight //鼠标移开时 清除元素 const onMouseLeave = () => { if (el.w_tipInstance) { el.w_tipInstance.hiddenTip() el.w_tooltip.remove() el.w_tipInstance = null el.w_tooltip = null } }; const onMouseEnter = () => { // 判断内容长度 需要展示 if (isOverflow(el)) { // @ts-ignore const directiveList = allPlacements.filter(placement => binding.modifiers[placement]) const placements = directiveList.length ? directiveList : allPlacements if (!el.w_tooltip) { // 创建tooltip实例 const vm = createApp(MyToolTip) // 创建根元素 el.w_tooltip = document.createElement('div') // 挂载到页面 document.body.appendChild(el.w_tooltip) el.w_tooltip.id = `tooltip_${Math.floor(Math.random() * 10000)}` el.w_tipInstance = vm.mount(el.w_tooltip) } // 设置 tooltip 显示方向 处理边界情况 let direction = dealDomBoundary(el, placement || placements[0] || 'top') el.w_tipInstance.placements = direction; // 设置显示内容 el.w_tipInstance.setContent(content || el.innerText) // 使 tooltip 显示 el.w_tipInstance.showTip() nextTick(() => { // 计算 tooltip 在页面中的位置 calculationLocation(el.w_tipInstance, el, direction) }) el._scrollHandler = () => { // 重新定位位置 if (el.w_tipInstance) calculationLocation(el.w_tipInstance, el, placements[0]) } window.addEventListener('scroll', el._scrollHandler) const _destroyOnLeave = destroyOnLeave || true if (_destroyOnLeave) el.addEventListener("mouseleave", onMouseLeave); } }; el.addEventListener("mouseenter", onMouseEnter); }, unmounted(el) { if (el.w_tooltip) { document.body.removeChild(el.w_tooltip) } window.removeEventListener('scroll', el._scrollHandler) } } : {}