modal-api.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import type { ModalApiOptions, ModalState } from './modal';
  2. import { Store } from '@vben-core/shared/store';
  3. import { bindMethods, isFunction } from '@vben-core/shared/utils';
  4. export class ModalApi {
  5. // 共享数据
  6. public sharedData: Record<'payload', any> = {
  7. payload: {},
  8. };
  9. public store: Store<ModalState>;
  10. private api: Pick<
  11. ModalApiOptions,
  12. | 'onBeforeClose'
  13. | 'onCancel'
  14. | 'onClosed'
  15. | 'onConfirm'
  16. | 'onOpenChange'
  17. | 'onOpened'
  18. >;
  19. // private prevState!: ModalState;
  20. private state!: ModalState;
  21. private withResolvers: PromiseWithResolvers<any> | undefined = void 0;
  22. constructor(options: ModalApiOptions = {}) {
  23. const {
  24. connectedComponent: _,
  25. onBeforeClose,
  26. onCancel,
  27. onClosed,
  28. onConfirm,
  29. onOpenChange,
  30. onOpened,
  31. ...storeState
  32. } = options;
  33. const defaultState: ModalState = {
  34. bordered: true,
  35. centered: false,
  36. class: '',
  37. closeOnClickModal: true,
  38. closeOnPressEscape: true,
  39. confirmDisabled: false,
  40. confirmLoading: false,
  41. contentClass: '',
  42. destroyOnClose: true,
  43. draggable: false,
  44. overflow: false,
  45. footer: true,
  46. footerClass: '',
  47. fullscreen: false,
  48. fullscreenButton: true,
  49. header: true,
  50. headerClass: '',
  51. isOpen: false,
  52. loading: false,
  53. modal: true,
  54. openAutoFocus: false,
  55. showCancelButton: true,
  56. showConfirmButton: true,
  57. title: '',
  58. animationType: 'slide',
  59. };
  60. this.store = new Store<ModalState>({
  61. ...defaultState,
  62. ...storeState,
  63. });
  64. this.store.subscribe((state) => {
  65. // 每次更新状态时,都会调用 onOpenChange 回调函数
  66. const prevIsOpen = this.state?.isOpen;
  67. this.state = state;
  68. if (state?.isOpen !== prevIsOpen) {
  69. this.api.onOpenChange?.(!!state?.isOpen);
  70. }
  71. });
  72. this.state = this.store.state;
  73. this.api = {
  74. onBeforeClose,
  75. onCancel,
  76. onClosed,
  77. onConfirm,
  78. onOpenChange,
  79. onOpened,
  80. };
  81. bindMethods(this);
  82. }
  83. /**
  84. * 关闭弹窗
  85. * @description 关闭弹窗时会调用 onBeforeClose 钩子函数,如果 onBeforeClose 返回 false,则不关闭弹窗
  86. */
  87. async close<T>(payload?: T) {
  88. // 通过 onBeforeClose 钩子函数来判断是否允许关闭弹窗
  89. // 如果 onBeforeClose 返回 false,则不关闭弹窗
  90. const allowClose = (await this.api.onBeforeClose?.()) ?? true;
  91. if (allowClose) {
  92. this.store.setState((prev) => ({
  93. ...prev,
  94. isOpen: false,
  95. }));
  96. if (payload) this.withResolvers?.resolve(payload);
  97. }
  98. }
  99. getData<T extends object = Record<string, any>>() {
  100. return (this.sharedData?.payload ?? {}) as T;
  101. }
  102. /**
  103. * 锁定弹窗状态(用于提交过程中的等待状态)
  104. * @description 锁定状态将禁用默认的取消按钮,使用spinner覆盖弹窗内容,隐藏关闭按钮,阻止手动关闭弹窗,将默认的提交按钮标记为loading状态
  105. * @param isLocked 是否锁定
  106. */
  107. lock(isLocked = true) {
  108. return this.setState({ submitting: isLocked });
  109. }
  110. /**
  111. * 取消操作
  112. */
  113. onCancel() {
  114. if (this.api.onCancel) {
  115. this.api.onCancel?.();
  116. } else {
  117. this.close();
  118. }
  119. }
  120. /**
  121. * 弹窗关闭动画播放完毕后的回调
  122. */
  123. onClosed() {
  124. if (!this.state.isOpen) {
  125. this.api.onClosed?.();
  126. this.withResolvers?.resolve(void 0);
  127. }
  128. }
  129. /**
  130. * 确认操作
  131. */
  132. onConfirm() {
  133. this.api.onConfirm?.();
  134. }
  135. /**
  136. * 弹窗打开动画播放完毕后的回调
  137. */
  138. onOpened() {
  139. if (this.state.isOpen) {
  140. this.api.onOpened?.();
  141. }
  142. }
  143. open<T = any>(
  144. withResolvers?: PromiseWithResolvers<T>,
  145. ): Promise<T> | undefined {
  146. this.store.setState((prev) => ({
  147. ...prev,
  148. isOpen: true,
  149. submitting: false,
  150. }));
  151. this.withResolvers = withResolvers;
  152. return this.withResolvers?.promise;
  153. }
  154. setData<T>(payload: T) {
  155. this.sharedData.payload = payload;
  156. return this;
  157. }
  158. setState(
  159. stateOrFn:
  160. | ((prev: ModalState) => Partial<ModalState>)
  161. | Partial<ModalState>,
  162. ) {
  163. if (isFunction(stateOrFn)) {
  164. this.store.setState(stateOrFn);
  165. } else {
  166. this.store.setState((prev) => ({ ...prev, ...stateOrFn }));
  167. }
  168. return this;
  169. }
  170. /**
  171. * 解除弹窗的锁定状态
  172. * @description 解除由lock方法设置的锁定状态,是lock(false)的别名
  173. */
  174. unlock() {
  175. return this.lock(false);
  176. }
  177. }