import type { Directive, DirectiveBinding } from "vue"; import Toolkit from "../Toolkit"; export enum PopupType { TEXT, IMG, HTML, EL } /** */ export type PopupConfig = { type: PopupType, value?: string | HTMLElement; canShow?: () => boolean; beforeShow?: (type: PopupType, value: string | HTMLElement) => Promise; afterHidden?: (type: PopupType, value: string | HTMLElement) => Promise; } // Popup 弹出提示 DOM 节点,全局唯一 let popup: HTMLElement | null; const VPopup: Directive = { mounted(el: HTMLElement, binding: DirectiveBinding) { // 转配置 let config: PopupConfig; if (binding.arg && binding.arg === "config") { config = binding.value as PopupConfig; } else { config = { type: PopupType.TEXT, value: binding.value as any as string, canShow: () => true }; } // Popup 节点 if (!popup) { popup = document.getElementById("tui-popup"); } let isShowing = false; // 显示 el.addEventListener("mouseenter", async e => { if (!config.value) { return; } if (config.beforeShow) { await config.beforeShow(config.type, config.value); } if (config.canShow && config.canShow() && popup) { let el: HTMLElement | null = null; if (!config) { el = document.createElement("div"); el.className = "text"; el.textContent = config as string; popup.appendChild(el); } switch (config.type) { case PopupType.TEXT: // 文本 el = document.createElement("div"); el.className = "text"; el.textContent = config.value as string; popup.appendChild(el); break; case PopupType.IMG: // 图片 el = document.createElement("img"); (el as HTMLImageElement).src = config.value as string; popup.appendChild(el); break; case PopupType.HTML: // HTML 字符串 popup.appendChild(Toolkit.toDOM(config.value as string)); break; case PopupType.EL: // DOM 节点 if (config.value instanceof HTMLElement) { const valueEl = config.value as HTMLElement; valueEl.style.display = "block"; popup.appendChild(valueEl); break; } else { console.error(config); throw new Error("Vue 指令错误:v-popup:el 的值不是 HTML 元素"); } } popup.style.left = (e.x + 20) + "px"; popup.style.top = (e.y + 14) + "px"; popup.style.visibility = "visible"; isShowing = true; } }, false); // 移动 el.addEventListener("mousemove", async (e) => { if (config.canShow && config.canShow() && isShowing && popup) { popup.style.left = (e.x + 20) + "px"; popup.style.top = (e.y + 14) + "px"; } }, false); // 隐藏 el.addEventListener("mouseleave", async () => { if (popup) { popup.style.visibility = "hidden"; popup.innerText = ""; popup.style.left = "0px"; popup.style.top = "0px"; // 隐藏后事件 if (config.afterHidden && config.value) { await config.afterHidden(config.type, config.value); } } }, false); } }; export default VPopup;