Files
common4web/src/utils/directives/Popup.ts
2025-11-25 11:12:21 +08:00

120 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<void>;
afterHidden?: (type: PopupType, value: string | HTMLElement) => Promise<void>;
}
// Popup 弹出提示 DOM 节点,全局唯一
let popup: HTMLElement | null;
const VPopup: Directive = {
mounted(el: HTMLElement, binding: DirectiveBinding<PopupConfig>) {
// 转配置
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;