Merge branch 'main' of https://github.com/vbenjs/vue-vben-admin into dev
This commit is contained in:
commit
7b5fb4f164
@ -14,7 +14,7 @@ export function setRefreshTokenCookie(
|
|||||||
) {
|
) {
|
||||||
setCookie(event, 'jwt', refreshToken, {
|
setCookie(event, 'jwt', refreshToken, {
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
maxAge: 24 * 60 * 60 * 1000,
|
maxAge: 24 * 60 * 60, // unit: seconds
|
||||||
sameSite: 'none',
|
sameSite: 'none',
|
||||||
secure: true,
|
secure: true,
|
||||||
});
|
});
|
||||||
|
@ -137,11 +137,19 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
|
|
||||||
### drawerApi
|
### drawerApi
|
||||||
|
|
||||||
| 方法 | 描述 | 类型 |
|
| 方法 | 描述 | 类型 | 版本限制 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| setState | 动态设置弹窗状态属性 | `(((prev: ModalState) => Partial<ModalState>)\| Partial<ModalState>)=>drawerApi` |
|
| setState | 动态设置弹窗状态属性 | `(((prev: ModalState) => Partial<ModalState>)\| Partial<ModalState>)=>drawerApi` |
|
||||||
| open | 打开弹窗 | `()=>void` |
|
| open | 打开弹窗 | `()=>void` | --- |
|
||||||
| close | 关闭弹窗 | `()=>void` |
|
| close | 关闭弹窗 | `()=>void` | --- |
|
||||||
| setData | 设置共享数据 | `<T>(data:T)=>drawerApi` |
|
| setData | 设置共享数据 | `<T>(data:T)=>drawerApi` | --- |
|
||||||
| getData | 获取共享数据 | `<T>()=>T` |
|
| getData | 获取共享数据 | `<T>()=>T` | --- |
|
||||||
| useStore | 获取可响应式状态 | - |
|
| useStore | 获取可响应式状态 | - | --- |
|
||||||
|
| lock | 将抽屉标记为提交中,锁定当前状态 | `(isLock:boolean)=>drawerApi` | >5.5.3 |
|
||||||
|
| unlock | lock方法的反操作,解除抽屉的锁定状态,也是lock(false)的别名 | `()=>drawerApi` | >5.5.3 |
|
||||||
|
|
||||||
|
::: info lock
|
||||||
|
|
||||||
|
`lock`方法用于锁定抽屉的状态,一般用于提交数据的过程中防止用户重复提交或者抽屉被意外关闭、表单数据被改变等等。当处于锁定状态时,抽屉的确认按钮会变为loading状态,同时禁用取消按钮和关闭按钮、禁止ESC或者点击遮罩等方式关闭抽屉、开启抽屉的spinner动画以遮挡弹窗内容。调用`close`方法关闭处于锁定状态的抽屉时,会自动解锁。要主动解除这种状态,可以调用`unlock`方法或者再次调用lock方法并传入false参数。
|
||||||
|
|
||||||
|
:::
|
||||||
|
@ -155,9 +155,10 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
| getData | 获取共享数据 | `<T>()=>T` | - |
|
| getData | 获取共享数据 | `<T>()=>T` | - |
|
||||||
| useStore | 获取可响应式状态 | - | - |
|
| useStore | 获取可响应式状态 | - | - |
|
||||||
| lock | 将弹窗标记为提交中,锁定当前状态 | `(isLock:boolean)=>modalApi` | >5.5.2 |
|
| lock | 将弹窗标记为提交中,锁定当前状态 | `(isLock:boolean)=>modalApi` | >5.5.2 |
|
||||||
|
| unlock | lock方法的反操作,解除弹窗的锁定状态,也是lock(false)的别名 | `()=>modalApi` | >5.5.3 |
|
||||||
|
|
||||||
::: info lock
|
::: info lock
|
||||||
|
|
||||||
`lock`方法用于锁定当前弹窗的状态,一般用于提交数据的过程中防止用户重复提交或者弹窗被意外关闭、表单数据被改变等等。当处于锁定状态时,弹窗的确认按钮会变为loading状态,同时禁用确认按钮、隐藏关闭按钮、禁止ESC或者点击遮罩等方式关闭弹窗、开启弹窗的spinner动画以遮挡弹窗内容。调用`close`方法关闭处于锁定状态的弹窗时,会自动解锁。
|
`lock`方法用于锁定当前弹窗的状态,一般用于提交数据的过程中防止用户重复提交或者弹窗被意外关闭、表单数据被改变等等。当处于锁定状态时,弹窗的确认按钮会变为loading状态,同时禁用取消按钮和关闭按钮、禁止ESC或者点击遮罩等方式关闭弹窗、开启弹窗的spinner动画以遮挡弹窗内容。调用`close`方法关闭处于锁定状态的弹窗时,会自动解锁。要主动解除这种状态,可以调用`unlock`方法或者再次调用lock方法并传入false参数。
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
@ -72,7 +72,7 @@ pnpm install
|
|||||||
|
|
||||||
## 其他
|
## 其他
|
||||||
|
|
||||||
如果你想更进一步精简,你可以删除参考一下文件或者文件夹的作用,判断自己是否需要,不需要删除即可:
|
如果你想更进一步精简,你可以删除参考以下文件或者文件夹的作用,判断自己是否需要,不需要删除即可:
|
||||||
|
|
||||||
- `.changeset` 文件夹用于管理版本变更
|
- `.changeset` 文件夹用于管理版本变更
|
||||||
- `.github` 文件夹用于存放 GitHub 的配置文件
|
- `.github` 文件夹用于存放 GitHub 的配置文件
|
||||||
|
@ -52,6 +52,7 @@ export class DrawerApi {
|
|||||||
placement: 'right',
|
placement: 'right',
|
||||||
showCancelButton: true,
|
showCancelButton: true,
|
||||||
showConfirmButton: true,
|
showConfirmButton: true,
|
||||||
|
submitting: false,
|
||||||
title: '',
|
title: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,7 +93,11 @@ export class DrawerApi {
|
|||||||
// 如果 onBeforeClose 返回 false,则不关闭弹窗
|
// 如果 onBeforeClose 返回 false,则不关闭弹窗
|
||||||
const allowClose = this.api.onBeforeClose?.() ?? true;
|
const allowClose = this.api.onBeforeClose?.() ?? true;
|
||||||
if (allowClose) {
|
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;
|
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;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解除抽屉的锁定状态
|
||||||
|
* @description 解除由lock方法设置的锁定状态,是lock(false)的别名
|
||||||
|
*/
|
||||||
|
unlock() {
|
||||||
|
return this.lock(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,12 +75,12 @@ export interface DrawerProps {
|
|||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示遮罩
|
* 是否显示遮罩
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
modal?: boolean;
|
modal?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否自动聚焦
|
* 是否自动聚焦
|
||||||
*/
|
*/
|
||||||
@ -89,12 +89,12 @@ export interface DrawerProps {
|
|||||||
* 弹窗遮罩模糊效果
|
* 弹窗遮罩模糊效果
|
||||||
*/
|
*/
|
||||||
overlayBlur?: number;
|
overlayBlur?: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 抽屉位置
|
* 抽屉位置
|
||||||
* @default right
|
* @default right
|
||||||
*/
|
*/
|
||||||
placement?: DrawerPlacement;
|
placement?: DrawerPlacement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否显示取消按钮
|
* 是否显示取消按钮
|
||||||
* @default true
|
* @default true
|
||||||
@ -105,6 +105,10 @@ export interface DrawerProps {
|
|||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
showConfirmButton?: boolean;
|
showConfirmButton?: boolean;
|
||||||
|
/**
|
||||||
|
* 提交中(锁定抽屉状态)
|
||||||
|
*/
|
||||||
|
submitting?: boolean;
|
||||||
/**
|
/**
|
||||||
* 弹窗标题
|
* 弹窗标题
|
||||||
*/
|
*/
|
||||||
|
@ -36,6 +36,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||||||
appendToMain: false,
|
appendToMain: false,
|
||||||
closeIconPlacement: 'right',
|
closeIconPlacement: 'right',
|
||||||
drawerApi: undefined,
|
drawerApi: undefined,
|
||||||
|
submitting: false,
|
||||||
zIndex: 1000,
|
zIndex: 1000,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,6 +74,7 @@ const {
|
|||||||
placement,
|
placement,
|
||||||
showCancelButton,
|
showCancelButton,
|
||||||
showConfirmButton,
|
showConfirmButton,
|
||||||
|
submitting,
|
||||||
title,
|
title,
|
||||||
titleTooltip,
|
titleTooltip,
|
||||||
zIndex,
|
zIndex,
|
||||||
@ -91,12 +93,12 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
function interactOutside(e: Event) {
|
function interactOutside(e: Event) {
|
||||||
if (!closeOnClickModal.value) {
|
if (!closeOnClickModal.value || submitting.value) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function escapeKeyDown(e: KeyboardEvent) {
|
function escapeKeyDown(e: KeyboardEvent) {
|
||||||
if (!closeOnPressEscape.value) {
|
if (!closeOnPressEscape.value || submitting.value) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +106,11 @@ function escapeKeyDown(e: KeyboardEvent) {
|
|||||||
function pointerDownOutside(e: Event) {
|
function pointerDownOutside(e: Event) {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
const dismissableDrawer = target?.dataset.dismissableDrawer;
|
const dismissableDrawer = target?.dataset.dismissableDrawer;
|
||||||
if (!closeOnClickModal.value || dismissableDrawer !== id) {
|
if (
|
||||||
|
submitting.value ||
|
||||||
|
!closeOnClickModal.value ||
|
||||||
|
dismissableDrawer !== id
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,6 +175,7 @@ const getAppendTo = computed(() => {
|
|||||||
<SheetClose
|
<SheetClose
|
||||||
v-if="closable && closeIconPlacement === 'left'"
|
v-if="closable && closeIconPlacement === 'left'"
|
||||||
as-child
|
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"
|
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">
|
<slot name="close-icon">
|
||||||
@ -209,6 +216,7 @@ const getAppendTo = computed(() => {
|
|||||||
<SheetClose
|
<SheetClose
|
||||||
v-if="closable && closeIconPlacement === 'right'"
|
v-if="closable && closeIconPlacement === 'right'"
|
||||||
as-child
|
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"
|
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">
|
<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>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
@ -253,6 +265,7 @@ const getAppendTo = computed(() => {
|
|||||||
:is="components.DefaultButton || VbenButton"
|
:is="components.DefaultButton || VbenButton"
|
||||||
v-if="showCancelButton"
|
v-if="showCancelButton"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
:disabled="submitting"
|
||||||
@click="() => drawerApi?.onCancel()"
|
@click="() => drawerApi?.onCancel()"
|
||||||
>
|
>
|
||||||
<slot name="cancelText">
|
<slot name="cancelText">
|
||||||
@ -263,7 +276,7 @@ const getAppendTo = computed(() => {
|
|||||||
<component
|
<component
|
||||||
:is="components.PrimaryButton || VbenButton"
|
:is="components.PrimaryButton || VbenButton"
|
||||||
v-if="showConfirmButton"
|
v-if="showConfirmButton"
|
||||||
:loading="confirmLoading"
|
:loading="confirmLoading || submitting"
|
||||||
@click="() => drawerApi?.onConfirm()"
|
@click="() => drawerApi?.onConfirm()"
|
||||||
>
|
>
|
||||||
<slot name="confirmText">
|
<slot name="confirmText">
|
||||||
|
@ -188,4 +188,12 @@ export class ModalApi {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解除弹窗的锁定状态
|
||||||
|
* @description 解除由lock方法设置的锁定状态,是lock(false)的别名
|
||||||
|
*/
|
||||||
|
unlock() {
|
||||||
|
return this.lock(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,12 +200,13 @@ const getAppendTo = computed(() => {
|
|||||||
"
|
"
|
||||||
:modal="modal"
|
:modal="modal"
|
||||||
:open="state?.isOpen"
|
:open="state?.isOpen"
|
||||||
:show-close="submitting ? false : closable"
|
:show-close="closable"
|
||||||
:z-index="zIndex"
|
:z-index="zIndex"
|
||||||
:overlay-blur="overlayBlur"
|
:overlay-blur="overlayBlur"
|
||||||
close-class="top-3"
|
close-class="top-3"
|
||||||
@close-auto-focus="handleFocusOutside"
|
@close-auto-focus="handleFocusOutside"
|
||||||
@closed="() => modalApi?.onClosed()"
|
@closed="() => modalApi?.onClosed()"
|
||||||
|
:close-disabled="submitting"
|
||||||
@escape-key-down="escapeKeyDown"
|
@escape-key-down="escapeKeyDown"
|
||||||
@focus-outside="handleFocusOutside"
|
@focus-outside="handleFocusOutside"
|
||||||
@interact-outside="interactOutside"
|
@interact-outside="interactOutside"
|
||||||
|
@ -23,6 +23,7 @@ const props = withDefaults(
|
|||||||
appendTo?: HTMLElement | string;
|
appendTo?: HTMLElement | string;
|
||||||
class?: ClassType;
|
class?: ClassType;
|
||||||
closeClass?: ClassType;
|
closeClass?: ClassType;
|
||||||
|
closeDisabled?: boolean;
|
||||||
modal?: boolean;
|
modal?: boolean;
|
||||||
open?: boolean;
|
open?: boolean;
|
||||||
overlayBlur?: number;
|
overlayBlur?: number;
|
||||||
@ -30,7 +31,7 @@ const props = withDefaults(
|
|||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
}
|
}
|
||||||
>(),
|
>(),
|
||||||
{ appendTo: 'body', showClose: true },
|
{ appendTo: 'body', closeDisabled: false, showClose: true },
|
||||||
);
|
);
|
||||||
const emits = defineEmits<
|
const emits = defineEmits<
|
||||||
DialogContentEmits & { close: []; closed: []; opened: [] }
|
DialogContentEmits & { close: []; closed: []; opened: [] }
|
||||||
@ -108,6 +109,7 @@ defineExpose({
|
|||||||
|
|
||||||
<DialogClose
|
<DialogClose
|
||||||
v-if="showClose"
|
v-if="showClose"
|
||||||
|
:disabled="closeDisabled"
|
||||||
:class="
|
:class="
|
||||||
cn(
|
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',
|
'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',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useVbenDrawer } from '@vben/common-ui';
|
import { useVbenDrawer } from '@vben/common-ui';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
const [Drawer, drawerApi] = useVbenDrawer({
|
const [Drawer, drawerApi] = useVbenDrawer({
|
||||||
onCancel() {
|
onCancel() {
|
||||||
@ -15,12 +15,19 @@ const [Drawer, drawerApi] = useVbenDrawer({
|
|||||||
// drawerApi.close();
|
// drawerApi.close();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function lockDrawer() {
|
||||||
|
drawerApi.lock();
|
||||||
|
setTimeout(() => {
|
||||||
|
drawerApi.unlock();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Drawer title="基础抽屉示例" title-tooltip="标题提示内容">
|
<Drawer title="基础抽屉示例" title-tooltip="标题提示内容">
|
||||||
<template #extra> extra </template>
|
<template #extra> extra </template>
|
||||||
base demo
|
base demo
|
||||||
|
<Button type="primary" @click="lockDrawer">锁定抽屉状态</Button>
|
||||||
<!-- <template #prepend-footer> slot </template> -->
|
<!-- <template #prepend-footer> slot </template> -->
|
||||||
<!-- <template #append-footer> prepend slot </template> -->
|
<!-- <template #append-footer> prepend slot </template> -->
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useVbenModal } from '@vben/common-ui';
|
import { useVbenModal } from '@vben/common-ui';
|
||||||
|
|
||||||
import { message } from 'ant-design-vue';
|
import { Button, message } from 'ant-design-vue';
|
||||||
|
|
||||||
const [Modal, modalApi] = useVbenModal({
|
const [Modal, modalApi] = useVbenModal({
|
||||||
onCancel() {
|
onCancel() {
|
||||||
@ -18,9 +18,17 @@ const [Modal, modalApi] = useVbenModal({
|
|||||||
message.info('onOpened:打开动画结束');
|
message.info('onOpened:打开动画结束');
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function lockModal() {
|
||||||
|
modalApi.lock();
|
||||||
|
setTimeout(() => {
|
||||||
|
modalApi.unlock();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<Modal class="w-[600px]" title="基础弹窗示例" title-tooltip="标题提示内容">
|
<Modal class="w-[600px]" title="基础弹窗示例" title-tooltip="标题提示内容">
|
||||||
base demo
|
base demo
|
||||||
|
<Button type="primary" @click="lockModal">锁定弹窗</Button>
|
||||||
</Modal>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
Loading…
Reference in New Issue
Block a user