mount.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { router } from "@/router";
  2. import { createVNode, reactive, render, watch, watchEffect } from "vue";
  3. import Locale from "@/config/locale.vue";
  4. import type { App, Ref, VNode } from "vue";
  5. interface ComponentConstructor<P = any> {
  6. new (...args: any[]): {
  7. $props: P;
  8. };
  9. }
  10. type Mutable<Type> = {
  11. -readonly [Key in keyof Type]: Type[Key];
  12. };
  13. export type MountContext<P = any> = {
  14. props?: P;
  15. children?: unknown;
  16. element?: HTMLElement;
  17. app?: App;
  18. };
  19. function mount<P>(
  20. component: Readonly<P>,
  21. { props, children, element, app }: MountContext<P> = {}
  22. ) {
  23. let el = element;
  24. let vNode: VNode | undefined = createVNode(
  25. Locale,
  26. {},
  27. {
  28. default: () => createVNode(component, props as any, children),
  29. }
  30. );
  31. if (app && app._context) vNode.appContext = app._context;
  32. if (el) {
  33. render(vNode, el);
  34. } else if (typeof document !== "undefined") {
  35. render(vNode, (el = document.createElement("div")));
  36. }
  37. const destroy = () => {
  38. if (el) render(null, el);
  39. el = undefined;
  40. vNode = undefined;
  41. };
  42. return { vNode, destroy, el };
  43. }
  44. let app: App;
  45. export const setApp = (application: App) => (app = application);
  46. export const extendProps = <T extends {}, E extends {}>(
  47. origin: T,
  48. append: E
  49. ): T & E => {
  50. const props = reactive({ ...append }) as T & E;
  51. watchEffect(() => {
  52. for (const key in origin) {
  53. (props as any)[key] = origin[key];
  54. }
  55. });
  56. return props;
  57. };
  58. export const mountComponent = <P>(
  59. comp: ComponentConstructor<P>,
  60. props: Mutable<P>,
  61. children?: any
  62. ) => {
  63. const element = document.createElement("div");
  64. const { destroy: destroyRaw } = mount(comp, {
  65. element,
  66. props,
  67. app: app,
  68. children,
  69. } as any);
  70. const destroy = () => {
  71. destroyRaw();
  72. if (document.body.contains(element)) {
  73. document.body.removeChild(element);
  74. }
  75. stopWatch();
  76. };
  77. const stopWatch = watch(() => router.currentRoute.value.name, destroy);
  78. return destroy;
  79. };
  80. import Dialog from "@/components/dialog/index.vue";
  81. import { DialogProps, dialogPropsKeys } from "@/components/dialog/type";
  82. export type QuiskExpose = {
  83. submit?: () => void;
  84. quit?: () => void;
  85. } & Partial<{ [key in keyof DialogProps]?: Ref<DialogProps[key]> }>;
  86. export const quiskMountFactory =
  87. <P>(comp: ComponentConstructor<P>, dprops: DialogProps) =>
  88. <T = boolean>(
  89. props: Mutable<P>,
  90. dRef?: (expose: { quit: () => void; submit: () => void }) => void
  91. ): Promise<T> => {
  92. let ref: QuiskExpose;
  93. return new Promise((resolve) => {
  94. const api = {
  95. onQuit: async () => {
  96. const ret = ref.quit && ((await ref.quit()) as any);
  97. if (ret) {
  98. resolve(ret);
  99. } else {
  100. resolve(false as any);
  101. }
  102. destroy();
  103. },
  104. onSubmit: async () => {
  105. const ret = ref.submit && ((await ref.submit()) as any);
  106. if (ret) {
  107. resolve(ret);
  108. } else {
  109. resolve(true as any);
  110. }
  111. destroy();
  112. },
  113. };
  114. const layoutProps = reactive({
  115. ...dprops,
  116. ref: undefined,
  117. show: true,
  118. ...api,
  119. });
  120. const destroy = mountComponent(Dialog, layoutProps, {
  121. default: () =>
  122. createVNode(comp, {
  123. ...props,
  124. ref: (v: any) => {
  125. for (const key in v) {
  126. if (dialogPropsKeys.includes(key as any)) {
  127. (layoutProps as any)[key] = v[key];
  128. }
  129. }
  130. ref = v;
  131. },
  132. }),
  133. });
  134. dRef &&
  135. dRef({
  136. submit: () => api.onSubmit(),
  137. quit: () => api.onQuit(),
  138. });
  139. });
  140. };