import Vue, { VNode, CreateElement } from 'vue'; import { createPopper, Instance } from '@popperjs/core/lib/popper-lite.js'; import preventOverflow from '@popperjs/core/lib/modifiers/preventOverflow.js'; import flip from '@popperjs/core/lib/modifiers/flip.js'; import arrow from '@popperjs/core/lib/modifiers/arrow.js'; import offset from '@popperjs/core/lib/modifiers/offset.js'; import { Placement } from '@popperjs/core/lib'; import { isFunc } from 'qx-util'; import { AppServiceBase } from '@ibizstudio/runtime'; import './ibz-tooltip.less'; interface Store { getters: { getZIndex: Function; }; commit: Function; } /** * tooltip工具类 * * @export * @class IBzTooltipUtil */ export class IBzTooltipUtil { /** * Prop承载容器 * * @private * @type {HTMLDivElement} * @memberof IBzTooltipUtil */ private container: HTMLDivElement = document.createElement('div'); /** * vue实例 * * @private * @type {Vue} * @memberof IBzTooltipUtil */ private vueExample!: Vue; /** * 飘窗实例 * * @private * @type {Instance} * @memberof IBzTooltipUtil */ private popperExample?: Instance; /** * 是否显示tooltip * * @private * @memberof IBzTooltipUtil */ private showTip = false; /** * 显示层级 * * @private * @memberof IBzTooltipUtil */ private zIndex = 100; /** * 提示控制器 * * @private * @type {*} * @memberof IBzTooltipUtil */ private c: any; /** * 鼠标是否悬浮 * * @memberof IBzTooltipUtil */ isHover = false; /** * 定制器 * * @type {*} * @memberof IBzTooltipUtil */ timer: any = null; /** * Creates an instance of IBzTooltipUtil. * @param {*} c * @memberof IBzTooltipUtil */ constructor(c: any) { this.c = c; const fn = offset.fn; offset.fn = (data: any) => { if (data) { Object.assign(data.options, { offset: [4, 4] }); } fn(data); }; this.initStore(); } /** * 鼠标移入 * * @memberof IBzTooltipUtil */ private mouseenter(): void { if (this.timer) { clearTimeout(this.timer); this.timer = null; } this.isHover = true; } /** * 鼠标移出 * * @memberof IBzTooltipUtil */ private mouseleave(): void { this.isHover = false; this.timer = setTimeout(() => { if (!this.c.isHover) { this.popperDestroy(); } }, 50); } /** * Vue状态管理器 * * @type {Store} * @memberof IBzTooltipUtil */ store: Store | null = null; /** * 初始化Vue状态管理器 * * @memberof IBzTooltipUtil */ private initStore() { const appService = AppServiceBase.getInstance(); this.store = appService.getAppStore(); } /** * 初始化vue实例 * * @private * @returns {void} * @memberof IBzTooltipUtil */ private initVueExample(): void { const self = this; if (!self.store) { self.initStore(); } self.container.className = 'ibz-tooltip-wrapper'; const div = document.createElement('div'); self.container.appendChild(div); document.body.appendChild(self.container); this.vueExample = new Vue({ el: div, data: { content: null, renderContent: null, }, methods: { click(e: MouseEvent) { e.stopPropagation(); }, }, render(h: CreateElement) { let content: any; if (this.renderContent) { content = (this.renderContent as any)(h); } else if (this.content) { content =
{this.content}
; } return (
self.mouseenter()} on-mouseleave={() => self.mouseleave()}>
{content}
); }, }); } /** * 打开提示框 * * @param {Element} el * @param {((h: CreateElement) => any | string | VNode | VNode[])} [content] * @param {Placement} [position='auto'] * @param {('local' | 'async')} [mode='local'] 提示框呈现模式,本地模式 | 异步模式。异步模式需要请求呈现 * @param {*} [params] * @memberof IBzTooltipUtil */ public openPopover(el: Element, content?: (h: CreateElement) => any | string | VNode | VNode[], position: Placement = 'auto', mode: 'local' | 'async' = 'local', params?: any): void { if (!this.vueExample) { this.initVueExample(); setTimeout(() => { this.openPopover(el, content, position, mode, params); }, 100); return; } this.popperDestroy(); const zIndex = this.store?.getters.getZIndex(); if (zIndex) { this.zIndex = zIndex + 1; this.store?.commit('upZIndex'); } // 更新vue实例内容 this.showTip = true; if (isFunc(content)) { Object.assign(this.vueExample.$data, { content: null, renderContent: content }); } else { Object.assign(this.vueExample.$data, { content, renderContent: null }); } const vueRef: any = this.vueExample.$el; this.popperExample = createPopper(el, vueRef, { placement: position, modifiers: [preventOverflow, flip, arrow, offset], }); this.vueExample.$forceUpdate(); } /** * 销毁提示框 * * @memberof IBzTooltipUtil */ public popperDestroy(): void { if (this.popperExample) { this.popperExample.destroy(); this.popperExample = undefined; this.showTip = false; this.vueExample.$forceUpdate(); } } /** * tip重新计算位置 * * @memberof IBzTooltipUtil */ popperTick(): void { this.popperExample?.forceUpdate(); } }