Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
@@ -52,6 +52,7 @@ export class DrawerApi {
|
||||
placement: 'right',
|
||||
showCancelButton: true,
|
||||
showConfirmButton: true,
|
||||
submitting: false,
|
||||
title: '',
|
||||
};
|
||||
|
||||
@@ -92,7 +93,11 @@ export class DrawerApi {
|
||||
// 如果 onBeforeClose 返回 false,则不关闭弹窗
|
||||
const allowClose = this.api.onBeforeClose?.() ?? true;
|
||||
if (allowClose) {
|
||||
this.store.setState((prev) => ({ ...prev, isOpen: false }));
|
||||
this.store.setState((prev) => ({
|
||||
...prev,
|
||||
isOpen: false,
|
||||
submitting: false,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +113,15 @@ export class DrawerApi {
|
||||
return (this.sharedData?.payload ?? {}) as T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定抽屉状态(用于提交过程中的等待状态)
|
||||
* @description 锁定状态将禁用默认的取消按钮,使用spinner覆盖抽屉内容,隐藏关闭按钮,阻止手动关闭弹窗,将默认的提交按钮标记为loading状态
|
||||
* @param isLocked 是否锁定
|
||||
*/
|
||||
lock(isLocked: boolean = true) {
|
||||
return this.setState({ submitting: isLocked });
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消操作
|
||||
*/
|
||||
@@ -165,4 +179,12 @@ export class DrawerApi {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除抽屉的锁定状态
|
||||
* @description 解除由lock方法设置的锁定状态,是lock(false)的别名
|
||||
*/
|
||||
unlock() {
|
||||
return this.lock(false);
|
||||
}
|
||||
}
|
||||
|
@@ -75,12 +75,12 @@ export interface DrawerProps {
|
||||
* @default false
|
||||
*/
|
||||
loading?: boolean;
|
||||
|
||||
/**
|
||||
* 是否显示遮罩
|
||||
* @default true
|
||||
*/
|
||||
modal?: boolean;
|
||||
|
||||
/**
|
||||
* 是否自动聚焦
|
||||
*/
|
||||
@@ -89,12 +89,12 @@ export interface DrawerProps {
|
||||
* 弹窗遮罩模糊效果
|
||||
*/
|
||||
overlayBlur?: number;
|
||||
|
||||
/**
|
||||
* 抽屉位置
|
||||
* @default right
|
||||
*/
|
||||
placement?: DrawerPlacement;
|
||||
|
||||
/**
|
||||
* 是否显示取消按钮
|
||||
* @default true
|
||||
@@ -105,6 +105,10 @@ export interface DrawerProps {
|
||||
* @default true
|
||||
*/
|
||||
showConfirmButton?: boolean;
|
||||
/**
|
||||
* 提交中(锁定抽屉状态)
|
||||
*/
|
||||
submitting?: boolean;
|
||||
/**
|
||||
* 弹窗标题
|
||||
*/
|
||||
|
@@ -36,6 +36,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
appendToMain: false,
|
||||
closeIconPlacement: 'right',
|
||||
drawerApi: undefined,
|
||||
submitting: false,
|
||||
zIndex: 1000,
|
||||
});
|
||||
|
||||
@@ -73,6 +74,7 @@ const {
|
||||
placement,
|
||||
showCancelButton,
|
||||
showConfirmButton,
|
||||
submitting,
|
||||
title,
|
||||
titleTooltip,
|
||||
zIndex,
|
||||
@@ -91,12 +93,12 @@ watch(
|
||||
);
|
||||
|
||||
function interactOutside(e: Event) {
|
||||
if (!closeOnClickModal.value) {
|
||||
if (!closeOnClickModal.value || submitting.value) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
function escapeKeyDown(e: KeyboardEvent) {
|
||||
if (!closeOnPressEscape.value) {
|
||||
if (!closeOnPressEscape.value || submitting.value) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -104,7 +106,11 @@ function escapeKeyDown(e: KeyboardEvent) {
|
||||
function pointerDownOutside(e: Event) {
|
||||
const target = e.target as HTMLElement;
|
||||
const dismissableDrawer = target?.dataset.dismissableDrawer;
|
||||
if (!closeOnClickModal.value || dismissableDrawer !== id) {
|
||||
if (
|
||||
submitting.value ||
|
||||
!closeOnClickModal.value ||
|
||||
dismissableDrawer !== id
|
||||
) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -169,6 +175,7 @@ const getAppendTo = computed(() => {
|
||||
<SheetClose
|
||||
v-if="closable && closeIconPlacement === 'left'"
|
||||
as-child
|
||||
:disabled="submitting"
|
||||
class="data-[state=open]:bg-secondary ml-[2px] cursor-pointer rounded-full opacity-80 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none"
|
||||
>
|
||||
<slot name="close-icon">
|
||||
@@ -209,6 +216,7 @@ const getAppendTo = computed(() => {
|
||||
<SheetClose
|
||||
v-if="closable && closeIconPlacement === 'right'"
|
||||
as-child
|
||||
:disabled="submitting"
|
||||
class="data-[state=open]:bg-secondary ml-[2px] cursor-pointer rounded-full opacity-80 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none"
|
||||
>
|
||||
<slot name="close-icon">
|
||||
@@ -233,7 +241,11 @@ const getAppendTo = computed(() => {
|
||||
})
|
||||
"
|
||||
>
|
||||
<VbenLoading v-if="showLoading" class="size-full" spinning />
|
||||
<VbenLoading
|
||||
v-if="showLoading || submitting"
|
||||
class="size-full"
|
||||
spinning
|
||||
/>
|
||||
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -253,6 +265,7 @@ const getAppendTo = computed(() => {
|
||||
:is="components.DefaultButton || VbenButton"
|
||||
v-if="showCancelButton"
|
||||
variant="ghost"
|
||||
:disabled="submitting"
|
||||
@click="() => drawerApi?.onCancel()"
|
||||
>
|
||||
<slot name="cancelText">
|
||||
@@ -263,7 +276,7 @@ const getAppendTo = computed(() => {
|
||||
<component
|
||||
:is="components.PrimaryButton || VbenButton"
|
||||
v-if="showConfirmButton"
|
||||
:loading="confirmLoading"
|
||||
:loading="confirmLoading || submitting"
|
||||
@click="() => drawerApi?.onConfirm()"
|
||||
>
|
||||
<slot name="confirmText">
|
||||
|
@@ -188,4 +188,12 @@ export class ModalApi {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解除弹窗的锁定状态
|
||||
* @description 解除由lock方法设置的锁定状态,是lock(false)的别名
|
||||
*/
|
||||
unlock() {
|
||||
return this.lock(false);
|
||||
}
|
||||
}
|
||||
|
@@ -200,12 +200,13 @@ const getAppendTo = computed(() => {
|
||||
"
|
||||
:modal="modal"
|
||||
:open="state?.isOpen"
|
||||
:show-close="submitting ? false : closable"
|
||||
:show-close="closable"
|
||||
:z-index="zIndex"
|
||||
:overlay-blur="overlayBlur"
|
||||
close-class="top-3"
|
||||
@close-auto-focus="handleFocusOutside"
|
||||
@closed="() => modalApi?.onClosed()"
|
||||
:close-disabled="submitting"
|
||||
@escape-key-down="escapeKeyDown"
|
||||
@focus-outside="handleFocusOutside"
|
||||
@interact-outside="interactOutside"
|
||||
|
@@ -23,6 +23,7 @@ const props = withDefaults(
|
||||
appendTo?: HTMLElement | string;
|
||||
class?: ClassType;
|
||||
closeClass?: ClassType;
|
||||
closeDisabled?: boolean;
|
||||
modal?: boolean;
|
||||
open?: boolean;
|
||||
overlayBlur?: number;
|
||||
@@ -30,7 +31,7 @@ const props = withDefaults(
|
||||
zIndex?: number;
|
||||
}
|
||||
>(),
|
||||
{ appendTo: 'body', showClose: true },
|
||||
{ appendTo: 'body', closeDisabled: false, showClose: true },
|
||||
);
|
||||
const emits = defineEmits<
|
||||
DialogContentEmits & { close: []; closed: []; opened: [] }
|
||||
@@ -108,6 +109,7 @@ defineExpose({
|
||||
|
||||
<DialogClose
|
||||
v-if="showClose"
|
||||
:disabled="closeDisabled"
|
||||
:class="
|
||||
cn(
|
||||
'data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-3 top-3 h-6 w-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none',
|
||||
|
Reference in New Issue
Block a user