overlay-container.ts 3.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-unused-vars */
import Vue, { CreateElement, VNode } from 'vue';
import { QXEvent } from 'qx-util';
import { IOverlayContainer } from '@ibiz-template/runtime';
import router from '@/router';
import { piniaInstance } from '@/store';

/**
 * 全局弹出承载组件
 *
 * @author chitanda
 * @date 2022-11-09 12:11:09
 * @export
 * @class OverlayContainer
 */
export class OverlayContainer<O> implements IOverlayContainer {
  /**
   * vue 飘窗呈现实例
   *
   * @author chitanda
   * @date 2022-11-09 12:11:09
   * @protected
   * @type {Vue}
   */
  protected vm?: Vue;

  /**
   * 具体模态组件
   *
   * @author chitanda
   * @date 2022-11-09 12:11:34
   * @protected
   * @type {*}
   */
  protected modal: any;

  /**
   * 外面调用dismiss时传的result结果
   *
   * @author lxm
   * @date 2022-11-09 20:11:06
   * @protected
   * @type {unknown}
   */
  protected result?: unknown;

  /**
   * 内部事件
   *
   * @author chitanda
   * @date 2022-11-09 12:11:42
   * @protected
   */
  protected evt: QXEvent<{ dismiss: (data?: unknown) => void }> = new QXEvent();

  /**
   * 创建全局呈现
   *
   * @author chitanda
   * @date 2022-11-09 14:11:52
   * @param {unknown} component
   * @param {(h: CreateElement) => VNode} render
   * @param {IPopoverOptions} [opts]
   */
  constructor(
    protected component: unknown,
    protected render: (h: CreateElement) => VNode,
    protected opts?: O,
  ) {
    this.init();
  }

  /**
   * 初始化飘窗
   *
   * @author chitanda
   * @date 2022-11-09 12:11:55
   * @protected
   * @return {*}  {void}
   */
  protected init(): void {
    const self = this;
    const { render, opts } = this;
    const container = document.createElement('div');
    document.body.appendChild(container);
    const vm = new Vue({
      el: container,
      pinia: piniaInstance,
      router,
      beforeDestroy() {
        vm!.$el.remove();
      },
      mounted() {
        self.modal = this.$refs.root;
      },
      destroyed() {
        self.evt.emit('dismiss', self.result);
      },
      render(h) {
        return h(
          self.component as string,
          {
            ref: 'root',
            props: { opts },
            on: {
              dismiss() {
                vm!.$destroy();
              },
            },
          },
          [render(h)],
        );
      },
    });
    this.vm = vm;
  }

  /**
   * 打开飘窗
   *
   * @author chitanda
   * @date 2022-11-09 12:11:52
   * @param {HTMLElement} target
   * @return {*}  {Promise<void>}
   */
  async present(): Promise<void> {
    return this.modal.present();
  }

  /**
   * 手动调用关闭飘窗
   *
   * @author chitanda
   * @date 2022-11-09 12:11:39
   * @param {unknown} [data]
   * @return {*}  {Promise<void>}
   */
  async dismiss(data?: unknown): Promise<void> {
    this.result = data;
    this.modal.dismiss();
  }

  /**
   * 订阅窗口关闭
   *
   * @author chitanda
   * @date 2022-11-09 12:11:20
   * @template T
   * @return {*}  {Promise<T>}
   */
  async onWillDismiss<T = unknown>(): Promise<T> {
    return new Promise<T>(resolve => {
      const callback = (data: unknown) => {
        resolve(data as T);
        this.evt.off('dismiss', callback);
      };
      this.evt.on('dismiss', callback);
    });
  }
}