feat: add opened and closed events for dialog (#4775)
							parent
							
								
									353e8be289
								
							
						
					
					
						commit
						06ba7cb224
					
				| 
						 | 
					@ -110,12 +110,14 @@ const [Modal, modalApi] = useVbenModal({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
以下事件,只有在 `useVbenModal({onCancel:()=>{}})` 中传入才会生效。
 | 
					以下事件,只有在 `useVbenModal({onCancel:()=>{}})` 中传入才会生效。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 事件名 | 描述 | 类型 |
 | 
					| 事件名 | 描述 | 类型 | 版本号 |
 | 
				
			||||||
| --- | --- | --- |
 | 
					| --- | --- | --- | --- |
 | 
				
			||||||
| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` |
 | 
					| onBeforeClose | 关闭前触发,返回 `false`则禁止关闭 | `()=>boolean` |  |
 | 
				
			||||||
| onCancel | 点击取消按钮触发 | `()=>void` |
 | 
					| onCancel | 点击取消按钮触发 | `()=>void` |  |
 | 
				
			||||||
| onConfirm | 点击确认按钮触发 | `()=>void` |
 | 
					| onClosed | 关闭动画播放完毕时触发 | `()=>void` | >5.4.3 |
 | 
				
			||||||
| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` |
 | 
					| onConfirm | 点击确认按钮触发 | `()=>void` |  |
 | 
				
			||||||
 | 
					| onOpenChange | 关闭或者打开弹窗时触发 | `(isOpen:boolean)=>void` |  |
 | 
				
			||||||
 | 
					| onOpened | 打开动画播放完毕时触发 | `()=>void` | >5.4.3 |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Slots
 | 
					### Slots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,4 +110,19 @@ describe('modalApi', () => {
 | 
				
			||||||
    expect(modalApi.store.state.title).toBe('Batch Title');
 | 
					    expect(modalApi.store.state.title).toBe('Batch Title');
 | 
				
			||||||
    expect(modalApi.store.state.confirmText).toBe('Batch Confirm');
 | 
					    expect(modalApi.store.state.confirmText).toBe('Batch Confirm');
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should call onClosed callback when provided', () => {
 | 
				
			||||||
 | 
					    const onClosed = vi.fn();
 | 
				
			||||||
 | 
					    const modalApiWithHook = new ModalApi({ onClosed });
 | 
				
			||||||
 | 
					    modalApiWithHook.onClosed();
 | 
				
			||||||
 | 
					    expect(onClosed).toHaveBeenCalled();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should call onOpened callback when provided', () => {
 | 
				
			||||||
 | 
					    const onOpened = vi.fn();
 | 
				
			||||||
 | 
					    const modalApiWithHook = new ModalApi({ onOpened });
 | 
				
			||||||
 | 
					    modalApiWithHook.open();
 | 
				
			||||||
 | 
					    modalApiWithHook.onOpened();
 | 
				
			||||||
 | 
					    expect(onOpened).toHaveBeenCalled();
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,12 @@ import { bindMethods, isFunction } from '@vben-core/shared/utils';
 | 
				
			||||||
export class ModalApi {
 | 
					export class ModalApi {
 | 
				
			||||||
  private api: Pick<
 | 
					  private api: Pick<
 | 
				
			||||||
    ModalApiOptions,
 | 
					    ModalApiOptions,
 | 
				
			||||||
    'onBeforeClose' | 'onCancel' | 'onConfirm' | 'onOpenChange'
 | 
					    | 'onBeforeClose'
 | 
				
			||||||
 | 
					    | 'onCancel'
 | 
				
			||||||
 | 
					    | 'onClosed'
 | 
				
			||||||
 | 
					    | 'onConfirm'
 | 
				
			||||||
 | 
					    | 'onOpenChange'
 | 
				
			||||||
 | 
					    | 'onOpened'
 | 
				
			||||||
  >;
 | 
					  >;
 | 
				
			||||||
  // private prevState!: ModalState;
 | 
					  // private prevState!: ModalState;
 | 
				
			||||||
  private state!: ModalState;
 | 
					  private state!: ModalState;
 | 
				
			||||||
| 
						 | 
					@ -23,8 +28,10 @@ export class ModalApi {
 | 
				
			||||||
      connectedComponent: _,
 | 
					      connectedComponent: _,
 | 
				
			||||||
      onBeforeClose,
 | 
					      onBeforeClose,
 | 
				
			||||||
      onCancel,
 | 
					      onCancel,
 | 
				
			||||||
 | 
					      onClosed,
 | 
				
			||||||
      onConfirm,
 | 
					      onConfirm,
 | 
				
			||||||
      onOpenChange,
 | 
					      onOpenChange,
 | 
				
			||||||
 | 
					      onOpened,
 | 
				
			||||||
      ...storeState
 | 
					      ...storeState
 | 
				
			||||||
    } = options;
 | 
					    } = options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,8 +84,10 @@ export class ModalApi {
 | 
				
			||||||
    this.api = {
 | 
					    this.api = {
 | 
				
			||||||
      onBeforeClose,
 | 
					      onBeforeClose,
 | 
				
			||||||
      onCancel,
 | 
					      onCancel,
 | 
				
			||||||
 | 
					      onClosed,
 | 
				
			||||||
      onConfirm,
 | 
					      onConfirm,
 | 
				
			||||||
      onOpenChange,
 | 
					      onOpenChange,
 | 
				
			||||||
 | 
					      onOpened,
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    bindMethods(this);
 | 
					    bindMethods(this);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -115,6 +124,15 @@ export class ModalApi {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 弹窗关闭动画播放完毕后的回调
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onClosed() {
 | 
				
			||||||
 | 
					    if (!this.state.isOpen) {
 | 
				
			||||||
 | 
					      this.api.onClosed?.();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 确认操作
 | 
					   * 确认操作
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
| 
						 | 
					@ -122,6 +140,15 @@ export class ModalApi {
 | 
				
			||||||
    this.api.onConfirm?.();
 | 
					    this.api.onConfirm?.();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 弹窗打开动画播放完毕后的回调
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onOpened() {
 | 
				
			||||||
 | 
					    if (this.state.isOpen) {
 | 
				
			||||||
 | 
					      this.api.onOpened?.();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  open() {
 | 
					  open() {
 | 
				
			||||||
    this.store.setState((prev) => ({ ...prev, isOpen: true }));
 | 
					    this.store.setState((prev) => ({ ...prev, isOpen: true }));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -139,6 +139,11 @@ export interface ModalApiOptions extends ModalState {
 | 
				
			||||||
   * 点击取消按钮的回调
 | 
					   * 点击取消按钮的回调
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  onCancel?: () => void;
 | 
					  onCancel?: () => void;
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 弹窗关闭动画结束的回调
 | 
				
			||||||
 | 
					   * @returns
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onClosed?: () => void;
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * 点击确定按钮的回调
 | 
					   * 点击确定按钮的回调
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
| 
						 | 
					@ -149,4 +154,9 @@ export interface ModalApiOptions extends ModalState {
 | 
				
			||||||
   * @returns
 | 
					   * @returns
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  onOpenChange?: (isOpen: boolean) => void;
 | 
					  onOpenChange?: (isOpen: boolean) => void;
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * 弹窗打开动画结束的回调
 | 
				
			||||||
 | 
					   * @returns
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  onOpened?: () => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,10 +188,12 @@ function handleFocusOutside(e: Event) {
 | 
				
			||||||
      :show-close="closable"
 | 
					      :show-close="closable"
 | 
				
			||||||
      close-class="top-3"
 | 
					      close-class="top-3"
 | 
				
			||||||
      @close-auto-focus="handleFocusOutside"
 | 
					      @close-auto-focus="handleFocusOutside"
 | 
				
			||||||
 | 
					      @closed="() => modalApi?.onClosed()"
 | 
				
			||||||
      @escape-key-down="escapeKeyDown"
 | 
					      @escape-key-down="escapeKeyDown"
 | 
				
			||||||
      @focus-outside="handleFocusOutside"
 | 
					      @focus-outside="handleFocusOutside"
 | 
				
			||||||
      @interact-outside="interactOutside"
 | 
					      @interact-outside="interactOutside"
 | 
				
			||||||
      @open-auto-focus="handerOpenAutoFocus"
 | 
					      @open-auto-focus="handerOpenAutoFocus"
 | 
				
			||||||
 | 
					      @opened="() => modalApi?.onOpened()"
 | 
				
			||||||
      @pointer-down-outside="pointerDownOutside"
 | 
					      @pointer-down-outside="pointerDownOutside"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <DialogHeader
 | 
					      <DialogHeader
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,9 @@ const props = withDefaults(
 | 
				
			||||||
  >(),
 | 
					  >(),
 | 
				
			||||||
  { showClose: true },
 | 
					  { showClose: true },
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
const emits = defineEmits<{ close: [] } & DialogContentEmits>();
 | 
					const emits = defineEmits<
 | 
				
			||||||
 | 
					  { close: []; closed: []; opened: [] } & DialogContentEmits
 | 
				
			||||||
 | 
					>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const delegatedProps = computed(() => {
 | 
					const delegatedProps = computed(() => {
 | 
				
			||||||
  const {
 | 
					  const {
 | 
				
			||||||
| 
						 | 
					@ -44,7 +46,13 @@ const delegatedProps = computed(() => {
 | 
				
			||||||
const forwarded = useForwardPropsEmits(delegatedProps, emits);
 | 
					const forwarded = useForwardPropsEmits(delegatedProps, emits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const contentRef = ref<InstanceType<typeof DialogContent> | null>(null);
 | 
					const contentRef = ref<InstanceType<typeof DialogContent> | null>(null);
 | 
				
			||||||
 | 
					function onAnimationEnd() {
 | 
				
			||||||
 | 
					  if (props.open) {
 | 
				
			||||||
 | 
					    emits('opened');
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    emits('closed');
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
defineExpose({
 | 
					defineExpose({
 | 
				
			||||||
  getContentRef: () => contentRef.value,
 | 
					  getContentRef: () => contentRef.value,
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -57,6 +65,7 @@ defineExpose({
 | 
				
			||||||
    </Transition>
 | 
					    </Transition>
 | 
				
			||||||
    <DialogContent
 | 
					    <DialogContent
 | 
				
			||||||
      ref="contentRef"
 | 
					      ref="contentRef"
 | 
				
			||||||
 | 
					      @animationend="onAnimationEnd"
 | 
				
			||||||
      v-bind="forwarded"
 | 
					      v-bind="forwarded"
 | 
				
			||||||
      :class="
 | 
					      :class="
 | 
				
			||||||
        cn(
 | 
					        cn(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,16 @@ const [Modal, modalApi] = useVbenModal({
 | 
				
			||||||
  onCancel() {
 | 
					  onCancel() {
 | 
				
			||||||
    modalApi.close();
 | 
					    modalApi.close();
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  onClosed() {
 | 
				
			||||||
 | 
					    message.info('onClosed:关闭动画结束');
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  onConfirm() {
 | 
					  onConfirm() {
 | 
				
			||||||
    message.info('onConfirm');
 | 
					    message.info('onConfirm');
 | 
				
			||||||
    // modalApi.close();
 | 
					    // modalApi.close();
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  onOpened() {
 | 
				
			||||||
 | 
					    message.info('onOpened:打开动画结束');
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue