feat: modal&drawer support appendToMain and zIndex (#5092)

* feat: modal/drawer support append to main content

* feat: modal zIndex support

* fix: drawer prop define

* chore: type

* fix: modal/drawer position fixed while append to body

* docs: typo

* chore: add full-width drawer in content area

* chore: remove unnecessary class
pull/58/MERGE
Netfan 2024-12-10 17:37:06 +08:00 committed by GitHub
parent 018ddc75c6
commit e419b03cab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 271 additions and 31 deletions

View File

@ -74,6 +74,7 @@ const [Drawer, drawerApi] = useVbenDrawer({
| 属性名 | 描述 | 类型 | 默认值 | | 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| appendToMain | 是否挂载到内容区域默认挂载到body | `boolean` | `false` |
| title | 标题 | `string\|slot` | - | | title | 标题 | `string\|slot` | - |
| titleTooltip | 标题提示信息 | `string\|slot` | - | | titleTooltip | 标题提示信息 | `string\|slot` | - |
| description | 描述信息 | `string\|slot` | - | | description | 描述信息 | `string\|slot` | - |
@ -95,6 +96,13 @@ const [Drawer, drawerApi] = useVbenDrawer({
| contentClass | modal内容区域的class | `string` | - | | contentClass | modal内容区域的class | `string` | - |
| footerClass | modal底部区域的class | `string` | - | | footerClass | modal底部区域的class | `string` | - |
| headerClass | modal顶部区域的class | `string` | - | | headerClass | modal顶部区域的class | `string` | - |
| zIndex | 抽屉的ZIndex层级 | `number` | `1000` |
::: info appendToMain
`appendToMain`可以指定将抽屉挂载到内容区域打开抽屉时内容区域以外的部分标签栏、导航菜单等等不会被遮挡。默认情况下抽屉会挂载到body上。但是挂载到内容区域时作为页面根容器的`Page`组件,需要设置`auto-content-height`属性,以便抽屉能够正确计算高度。
:::
### Event ### Event

View File

@ -80,6 +80,7 @@ const [Modal, modalApi] = useVbenModal({
| 属性名 | 描述 | 类型 | 默认值 | | 属性名 | 描述 | 类型 | 默认值 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| appendToMain | 是否挂载到内容区域默认挂载到body | `boolean` | `false` |
| title | 标题 | `string\|slot` | - | | title | 标题 | `string\|slot` | - |
| titleTooltip | 标题提示信息 | `string\|slot` | - | | titleTooltip | 标题提示信息 | `string\|slot` | - |
| description | 描述信息 | `string\|slot` | - | | description | 描述信息 | `string\|slot` | - |
@ -106,6 +107,13 @@ const [Modal, modalApi] = useVbenModal({
| footerClass | modal底部区域的class | `string` | - | | footerClass | modal底部区域的class | `string` | - |
| headerClass | modal顶部区域的class | `string` | - | | headerClass | modal顶部区域的class | `string` | - |
| bordered | 是否显示border | `boolean` | `false` | | bordered | 是否显示border | `boolean` | `false` |
| zIndex | 弹窗的ZIndex层级 | `number` | `1000` |
::: info appendToMain
`appendToMain`可以指定将弹窗挂载到内容区域打开带这招的弹窗时内容区域以外的部分标签栏、导航菜单等等不会被遮挡。默认情况下弹窗会挂载到body上。但是挂载到内容区域时作为页面根容器的`Page`组件,需要设置`auto-content-height`属性,以便弹窗能够正确计算高度。
:::
### Event ### Event

View File

@ -7,6 +7,9 @@ export const CSS_VARIABLE_LAYOUT_HEADER_HEIGHT = `--vben-header-height`;
/** layout footer 组件的高度 */ /** layout footer 组件的高度 */
export const CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT = `--vben-footer-height`; export const CSS_VARIABLE_LAYOUT_FOOTER_HEIGHT = `--vben-footer-height`;
/** 内容区域的组件ID */
export const ELEMENT_ID_MAIN_CONTENT = `__vben_main_content`;
/** /**
* @zh_CN * @zh_CN
*/ */

View File

@ -40,6 +40,7 @@
"@vben-core/composables": "workspace:*", "@vben-core/composables": "workspace:*",
"@vben-core/icons": "workspace:*", "@vben-core/icons": "workspace:*",
"@vben-core/shadcn-ui": "workspace:*", "@vben-core/shadcn-ui": "workspace:*",
"@vben-core/shared": "workspace:*",
"@vben-core/typings": "workspace:*", "@vben-core/typings": "workspace:*",
"@vueuse/core": "catalog:", "@vueuse/core": "catalog:",
"vue": "catalog:" "vue": "catalog:"

View File

@ -11,6 +11,7 @@ import {
} from '@vben-core/composables'; } from '@vben-core/composables';
import { Menu } from '@vben-core/icons'; import { Menu } from '@vben-core/icons';
import { VbenIconButton } from '@vben-core/shadcn-ui'; import { VbenIconButton } from '@vben-core/shadcn-ui';
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
import { useMouse, useScroll, useThrottleFn } from '@vueuse/core'; import { useMouse, useScroll, useThrottleFn } from '@vueuse/core';
@ -457,6 +458,8 @@ function handleHeaderToggle() {
emit('toggleSidebar'); emit('toggleSidebar');
} }
} }
const idMainContent = ELEMENT_ID_MAIN_CONTENT;
</script> </script>
<template> <template>
@ -553,6 +556,7 @@ function handleHeaderToggle() {
<!-- </div> --> <!-- </div> -->
<LayoutContent <LayoutContent
:id="idMainContent"
:content-compact="contentCompact" :content-compact="contentCompact"
:content-compact-width="contentCompactWidth" :content-compact-width="contentCompactWidth"
:padding="contentPadding" :padding="contentPadding"

View File

@ -7,6 +7,11 @@ import type { Component, Ref } from 'vue';
export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top'; export type DrawerPlacement = 'bottom' | 'left' | 'right' | 'top';
export interface DrawerProps { export interface DrawerProps {
/**
*
* @default false
*/
appendToMain?: boolean;
/** /**
* *
*/ */
@ -59,12 +64,12 @@ export interface DrawerProps {
* *
*/ */
headerClass?: ClassType; headerClass?: ClassType;
/** /**
* *
* @default false * @default false
*/ */
loading?: boolean; loading?: boolean;
/** /**
* *
* @default true * @default true
@ -74,12 +79,12 @@ export interface DrawerProps {
* *
*/ */
openAutoFocus?: boolean; openAutoFocus?: boolean;
/** /**
* *
* @default right * @default right
*/ */
placement?: DrawerPlacement; placement?: DrawerPlacement;
/** /**
* *
* @default true * @default true
@ -98,6 +103,10 @@ export interface DrawerProps {
* *
*/ */
titleTooltip?: string; titleTooltip?: string;
/**
*
*/
zIndex?: number;
} }
export interface DrawerState extends DrawerProps { export interface DrawerState extends DrawerProps {

View File

@ -1,7 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { DrawerProps, ExtendedDrawerApi } from './drawer'; import type { DrawerProps, ExtendedDrawerApi } from './drawer';
import { provide, ref, useId, watch } from 'vue'; import { computed, provide, ref, useId, watch } from 'vue';
import { import {
useIsMobile, useIsMobile,
@ -23,6 +23,7 @@ import {
VbenLoading, VbenLoading,
VisuallyHidden, VisuallyHidden,
} from '@vben-core/shadcn-ui'; } from '@vben-core/shadcn-ui';
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
import { globalShareState } from '@vben-core/shared/global-state'; import { globalShareState } from '@vben-core/shared/global-state';
import { cn } from '@vben-core/shared/utils'; import { cn } from '@vben-core/shared/utils';
@ -31,7 +32,9 @@ interface Props extends DrawerProps {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
appendToMain: false,
drawerApi: undefined, drawerApi: undefined,
zIndex: 1000,
}); });
const components = globalShareState.getComponents(); const components = globalShareState.getComponents();
@ -46,6 +49,7 @@ const { isMobile } = useIsMobile();
const state = props.drawerApi?.useStore?.(); const state = props.drawerApi?.useStore?.();
const { const {
appendToMain,
cancelText, cancelText,
class: drawerClass, class: drawerClass,
closable, closable,
@ -67,6 +71,7 @@ const {
showConfirmButton, showConfirmButton,
title, title,
titleTooltip, titleTooltip,
zIndex,
} = usePriorityValues(props, state); } = usePriorityValues(props, state);
watch( watch(
@ -110,6 +115,10 @@ function handleFocusOutside(e: Event) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }
const getAppendTo = computed(() => {
return appendToMain.value ? `#${ELEMENT_ID_MAIN_CONTENT}` : undefined;
});
</script> </script>
<template> <template>
<Sheet <Sheet
@ -118,6 +127,7 @@ function handleFocusOutside(e: Event) {
@update:open="() => drawerApi?.close()" @update:open="() => drawerApi?.close()"
> >
<SheetContent <SheetContent
:append-to="getAppendTo"
:class=" :class="
cn('flex w-[520px] flex-col', drawerClass, { cn('flex w-[520px] flex-col', drawerClass, {
'!w-full': isMobile || placement === 'bottom' || placement === 'top', '!w-full': isMobile || placement === 'bottom' || placement === 'top',
@ -127,6 +137,7 @@ function handleFocusOutside(e: Event) {
:modal="modal" :modal="modal"
:open="state?.isOpen" :open="state?.isOpen"
:side="placement" :side="placement"
:z-index="zIndex"
@close-auto-focus="handleFocusOutside" @close-auto-focus="handleFocusOutside"
@escape-key-down="escapeKeyDown" @escape-key-down="escapeKeyDown"
@focus-outside="handleFocusOutside" @focus-outside="handleFocusOutside"

View File

@ -3,6 +3,11 @@ import type { ModalApi } from './modal-api';
import type { Component, Ref } from 'vue'; import type { Component, Ref } from 'vue';
export interface ModalProps { export interface ModalProps {
/**
*
* @default false
*/
appendToMain?: boolean;
/** /**
* *
* @default false * @default false
@ -12,7 +17,6 @@ export interface ModalProps {
* *
*/ */
cancelText?: string; cancelText?: string;
/** /**
* *
* @default false * @default false
@ -20,6 +24,7 @@ export interface ModalProps {
centered?: boolean; centered?: boolean;
class?: string; class?: string;
/** /**
* *
* @default true * @default true
@ -112,6 +117,10 @@ export interface ModalProps {
* *
*/ */
titleTooltip?: string; titleTooltip?: string;
/**
*
*/
zIndex?: number;
} }
export interface ModalState extends ModalProps { export interface ModalState extends ModalProps {

View File

@ -22,6 +22,7 @@ import {
VbenLoading, VbenLoading,
VisuallyHidden, VisuallyHidden,
} from '@vben-core/shadcn-ui'; } from '@vben-core/shadcn-ui';
import { ELEMENT_ID_MAIN_CONTENT } from '@vben-core/shared/constants';
import { globalShareState } from '@vben-core/shared/global-state'; import { globalShareState } from '@vben-core/shared/global-state';
import { cn } from '@vben-core/shared/utils'; import { cn } from '@vben-core/shared/utils';
@ -32,6 +33,7 @@ interface Props extends ModalProps {
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
appendToMain: false,
modalApi: undefined, modalApi: undefined,
}); });
@ -52,6 +54,7 @@ const { isMobile } = useIsMobile();
const state = props.modalApi?.useStore?.(); const state = props.modalApi?.useStore?.();
const { const {
appendToMain,
bordered, bordered,
cancelText, cancelText,
centered, centered,
@ -78,6 +81,7 @@ const {
showConfirmButton, showConfirmButton,
title, title,
titleTooltip, titleTooltip,
zIndex,
} = usePriorityValues(props, state); } = usePriorityValues(props, state);
const shouldFullscreen = computed( const shouldFullscreen = computed(
@ -161,6 +165,9 @@ function handleFocusOutside(e: Event) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} }
const getAppendTo = computed(() => {
return appendToMain.value ? `#${ELEMENT_ID_MAIN_CONTENT}` : undefined;
});
</script> </script>
<template> <template>
<Dialog <Dialog
@ -170,6 +177,7 @@ function handleFocusOutside(e: Event) {
> >
<DialogContent <DialogContent
ref="contentRef" ref="contentRef"
:append-to="getAppendTo"
:class=" :class="
cn( cn(
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-[var(--radius)]', 'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] w-[520px] flex-col p-0 sm:rounded-[var(--radius)]',
@ -187,6 +195,7 @@ function handleFocusOutside(e: Event) {
:modal="modal" :modal="modal"
:open="state?.isOpen" :open="state?.isOpen"
:show-close="closable" :show-close="closable"
:z-index="zIndex"
close-class="top-3" close-class="top-3"
@close-auto-focus="handleFocusOutside" @close-auto-focus="handleFocusOutside"
@closed="() => modalApi?.onClosed()" @closed="() => modalApi?.onClosed()"

View File

@ -20,14 +20,16 @@ import DialogOverlay from './DialogOverlay.vue';
const props = withDefaults( const props = withDefaults(
defineProps< defineProps<
{ {
appendTo?: HTMLElement | string;
class?: ClassType; class?: ClassType;
closeClass?: ClassType; closeClass?: ClassType;
modal?: boolean; modal?: boolean;
open?: boolean; open?: boolean;
showClose?: boolean; showClose?: boolean;
zIndex?: number;
} & DialogContentProps } & DialogContentProps
>(), >(),
{ showClose: true }, { appendTo: 'body', showClose: true, zIndex: 1000 },
); );
const emits = defineEmits< const emits = defineEmits<
{ close: []; closed: []; opened: [] } & DialogContentEmits { close: []; closed: []; opened: [] } & DialogContentEmits
@ -45,6 +47,18 @@ const delegatedProps = computed(() => {
return delegated; return delegated;
}); });
function isAppendToBody() {
return (
props.appendTo === 'body' ||
props.appendTo === document.body ||
!props.appendTo
);
}
const position = computed(() => {
return isAppendToBody() ? 'fixed' : 'absolute';
});
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);
@ -64,17 +78,22 @@ defineExpose({
</script> </script>
<template> <template>
<DialogPortal> <DialogPortal :to="appendTo">
<Transition name="fade"> <Transition name="fade">
<DialogOverlay v-if="open && modal" @click="() => emits('close')" /> <DialogOverlay
v-if="open && modal"
:style="{ zIndex, position }"
@click="() => emits('close')"
/>
</Transition> </Transition>
<DialogContent <DialogContent
ref="contentRef" ref="contentRef"
:style="{ zIndex, position }"
@animationend="onAnimationEnd" @animationend="onAnimationEnd"
v-bind="forwarded" v-bind="forwarded"
:class=" :class="
cn( cn(
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%] fixed z-[1000] w-full p-6 shadow-lg outline-none sm:rounded-xl', 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%] w-full p-6 shadow-lg outline-none sm:rounded-xl',
props.class, props.class,
) )
" "

View File

@ -7,8 +7,5 @@ useScrollLock();
const id = inject('DISMISSABLE_MODAL_ID'); const id = inject('DISMISSABLE_MODAL_ID');
</script> </script>
<template> <template>
<div <div :data-dismissable-modal="id" class="bg-overlay inset-0"></div>
:data-dismissable-modal="id"
class="bg-overlay fixed inset-0 z-[1000]"
></div>
</template> </template>

View File

@ -14,7 +14,10 @@ import {
useForwardPropsEmits, useForwardPropsEmits,
} from 'radix-vue'; } from 'radix-vue';
const props = defineProps<{ class?: any } & DialogContentProps>(); const props = withDefaults(
defineProps<{ class?: any; zIndex?: number } & DialogContentProps>(),
{ zIndex: 1000 },
);
const emits = defineEmits<DialogContentEmits>(); const emits = defineEmits<DialogContentEmits>();
const delegatedProps = computed(() => { const delegatedProps = computed(() => {
@ -29,7 +32,8 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
<template> <template>
<DialogPortal> <DialogPortal>
<DialogOverlay <DialogOverlay
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 border-border fixed inset-0 z-[1000] grid place-items-center overflow-y-auto border bg-black/80" :style="{ zIndex }"
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 border-border absolute inset-0 grid place-items-center overflow-y-auto border bg-black/80"
> >
<DialogContent <DialogContent
:class=" :class="
@ -38,6 +42,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
props.class, props.class,
) )
" "
:style="{ zIndex }"
v-bind="forwarded" v-bind="forwarded"
@pointer-down-outside=" @pointer-down-outside="
(event) => { (event) => {

View File

@ -15,17 +15,22 @@ import { type SheetVariants, sheetVariants } from './sheet';
import SheetOverlay from './SheetOverlay.vue'; import SheetOverlay from './SheetOverlay.vue';
interface SheetContentProps extends DialogContentProps { interface SheetContentProps extends DialogContentProps {
appendTo?: HTMLElement | string;
class?: any; class?: any;
modal?: boolean; modal?: boolean;
open?: boolean; open?: boolean;
side?: SheetVariants['side']; side?: SheetVariants['side'];
zIndex?: number;
} }
defineOptions({ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}); });
const props = defineProps<SheetContentProps>(); const props = withDefaults(defineProps<SheetContentProps>(), {
appendTo: 'body',
zIndex: 1000,
});
const emits = defineEmits<DialogContentEmits>(); const emits = defineEmits<DialogContentEmits>();
@ -41,16 +46,29 @@ const delegatedProps = computed(() => {
return delegated; return delegated;
}); });
function isAppendToBody() {
return (
props.appendTo === 'body' ||
props.appendTo === document.body ||
!props.appendTo
);
}
const position = computed(() => {
return isAppendToBody() ? 'fixed' : 'absolute';
});
const forwarded = useForwardPropsEmits(delegatedProps, emits); const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script> </script>
<template> <template>
<DialogPortal> <DialogPortal :to="appendTo">
<Transition name="fade"> <Transition name="fade">
<SheetOverlay v-if="open && modal" /> <SheetOverlay v-if="open && modal" :style="{ zIndex, position }" />
</Transition> </Transition>
<DialogContent <DialogContent
:class="cn(sheetVariants({ side }), 'z-[1000]', props.class)" :class="cn(sheetVariants({ side }), props.class)"
:style="{ zIndex, position }"
v-bind="{ ...forwarded, ...$attrs }" v-bind="{ ...forwarded, ...$attrs }"
> >
<slot></slot> <slot></slot>

View File

@ -7,8 +7,5 @@ useScrollLock();
const id = inject('DISMISSABLE_DRAWER_ID'); const id = inject('DISMISSABLE_DRAWER_ID');
</script> </script>
<template> <template>
<div <div :data-dismissable-drawer="id" class="bg-overlay inset-0"></div>
:data-dismissable-drawer="id"
class="bg-overlay fixed inset-0 z-[1000]"
></div>
</template> </template>

View File

@ -1,7 +1,7 @@
import { cva, type VariantProps } from 'class-variance-authority'; import { cva, type VariantProps } from 'class-variance-authority';
export const sheetVariants = cva( export const sheetVariants = cva(
'fixed z-[1000] bg-background shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 border-border', 'bg-background shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 border-border',
{ {
defaultVariants: { defaultVariants: {
side: 'right', side: 'right',
@ -12,7 +12,7 @@ export const sheetVariants = cva(
'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom', 'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left ', left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left ',
right: right:
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right', 'inset-y-0 right-0 w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right',
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top', top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
}, },
}, },

View File

@ -1,5 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, nextTick, onMounted, ref, useTemplateRef } from 'vue'; import {
computed,
nextTick,
onMounted,
ref,
type StyleValue,
useTemplateRef,
} from 'vue';
import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from '@vben-core/shared/constants'; import { CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT } from '@vben-core/shared/constants';
import { cn } from '@vben-core/shared/utils'; import { cn } from '@vben-core/shared/utils';
@ -29,13 +36,13 @@ const shouldAutoHeight = ref(false);
const headerRef = useTemplateRef<HTMLDivElement>('headerRef'); const headerRef = useTemplateRef<HTMLDivElement>('headerRef');
const footerRef = useTemplateRef<HTMLDivElement>('footerRef'); const footerRef = useTemplateRef<HTMLDivElement>('footerRef');
const contentStyle = computed(() => { const contentStyle = computed<StyleValue>(() => {
if (autoContentHeight) { if (autoContentHeight) {
return { return {
height: shouldAutoHeight.value height: shouldAutoHeight.value
? `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px)` ? `calc(var(${CSS_VARIABLE_LAYOUT_CONTENT_HEIGHT}) - ${headerHeight.value}px)`
: '0', : '0',
// 'overflow-y': shouldAutoHeight.value?'auto':'unset', overflowY: shouldAutoHeight.value ? 'auto' : 'unset',
}; };
} }
return {}; return {};

View File

@ -0,0 +1,21 @@
<script lang="ts" setup>
import { useVbenDrawer } from '@vben/common-ui';
import { message } from 'ant-design-vue';
const [Drawer, drawerApi] = useVbenDrawer({
onCancel() {
drawerApi.close();
},
onConfirm() {
message.info('onConfirm');
// drawerApi.close();
},
});
</script>
<template>
<Drawer append-to-main title="基础抽屉示例" title-tooltip="">
<template #extra> extra </template>
本抽屉指定在内容区域打开
</Drawer>
</template>

View File

@ -8,6 +8,7 @@ import AutoHeightDemo from './auto-height-demo.vue';
import BaseDemo from './base-demo.vue'; import BaseDemo from './base-demo.vue';
import DynamicDemo from './dynamic-demo.vue'; import DynamicDemo from './dynamic-demo.vue';
import FormDrawerDemo from './form-drawer-demo.vue'; import FormDrawerDemo from './form-drawer-demo.vue';
import inContentDemo from './in-content-demo.vue';
import SharedDataDemo from './shared-data-demo.vue'; import SharedDataDemo from './shared-data-demo.vue';
const [BaseDrawer, baseDrawerApi] = useVbenDrawer({ const [BaseDrawer, baseDrawerApi] = useVbenDrawer({
@ -16,6 +17,12 @@ const [BaseDrawer, baseDrawerApi] = useVbenDrawer({
// placement: 'left', // placement: 'left',
}); });
const [InContentDrawer, inContentDrawerApi] = useVbenDrawer({
//
connectedComponent: inContentDemo,
// placement: 'left',
});
const [AutoHeightDrawer, autoHeightDrawerApi] = useVbenDrawer({ const [AutoHeightDrawer, autoHeightDrawerApi] = useVbenDrawer({
connectedComponent: AutoHeightDemo, connectedComponent: AutoHeightDemo,
}); });
@ -37,6 +44,23 @@ function openBaseDrawer(placement: DrawerPlacement = 'right') {
baseDrawerApi.open(); baseDrawerApi.open();
} }
function openInContentDrawer(placement: DrawerPlacement = 'right') {
inContentDrawerApi.setState({ class: '', placement });
if (placement === 'top') {
// 200200
inContentDrawerApi.setState({ zIndex: 199 });
} else {
inContentDrawerApi.setState({ zIndex: undefined });
}
inContentDrawerApi.open();
}
function openMaxContentDrawer() {
// 便使Drawer
inContentDrawerApi.setState({ class: 'w-full', placement: 'right' });
inContentDrawerApi.open();
}
function openAutoHeightDrawer() { function openAutoHeightDrawer() {
autoHeightDrawerApi.open(); autoHeightDrawerApi.open();
} }
@ -69,6 +93,7 @@ function openFormDrawer() {
<template> <template>
<Page <Page
auto-content-height
description="抽屉组件通常用于在当前页面上显示一个覆盖层,用以展示重要信息或提供用户交互界面。" description="抽屉组件通常用于在当前页面上显示一个覆盖层,用以展示重要信息或提供用户交互界面。"
title="抽屉组件示例" title="抽屉组件示例"
> >
@ -76,6 +101,7 @@ function openFormDrawer() {
<DocButton path="/components/common-ui/vben-drawer" /> <DocButton path="/components/common-ui/vben-drawer" />
</template> </template>
<BaseDrawer /> <BaseDrawer />
<InContentDrawer />
<AutoHeightDrawer /> <AutoHeightDrawer />
<DynamicDrawer /> <DynamicDrawer />
<SharedDataDrawer /> <SharedDataDrawer />
@ -83,18 +109,55 @@ function openFormDrawer() {
<Card class="mb-4" title="基本使用"> <Card class="mb-4" title="基本使用">
<p class="mb-3">一个基础的抽屉示例</p> <p class="mb-3">一个基础的抽屉示例</p>
<Button type="primary" @click="openBaseDrawer('right')"></Button> <Button class="mb-2" type="primary" @click="openBaseDrawer('right')">
<Button class="ml-2" type="primary" @click="openBaseDrawer('bottom')"> 右侧打开
</Button>
<Button
class="mb-2 ml-2"
type="primary"
@click="openBaseDrawer('bottom')"
>
底部打开 底部打开
</Button> </Button>
<Button class="ml-2" type="primary" @click="openBaseDrawer('left')"> <Button class="mb-2 ml-2" type="primary" @click="openBaseDrawer('left')">
左侧打开 左侧打开
</Button> </Button>
<Button class="ml-2" type="primary" @click="openBaseDrawer('top')"> <Button class="mb-2 ml-2" type="primary" @click="openBaseDrawer('top')">
顶部打开 顶部打开
</Button> </Button>
</Card> </Card>
<Card class="mb-4" title="在内容区域打开">
<p class="mb-3">指定抽屉在内容区域打开不会覆盖顶部和左侧菜单等区域</p>
<Button class="mb-2" type="primary" @click="openInContentDrawer('right')">
右侧打开
</Button>
<Button
class="mb-2 ml-2"
type="primary"
@click="openInContentDrawer('bottom')"
>
底部打开
</Button>
<Button
class="mb-2 ml-2"
type="primary"
@click="openInContentDrawer('left')"
>
左侧打开
</Button>
<Button
class="mb-2 ml-2"
type="primary"
@click="openInContentDrawer('top')"
>
顶部打开
</Button>
<Button class="mb-2 ml-2" type="primary" @click="openMaxContentDrawer">
内容区域全屏打开
</Button>
</Card>
<Card class="mb-4" title="内容高度自适应滚动"> <Card class="mb-4" title="内容高度自适应滚动">
<p class="mb-3">可根据内容自动计算滚动高度</p> <p class="mb-3">可根据内容自动计算滚动高度</p>
<Button type="primary" @click="openAutoHeightDrawer"></Button> <Button type="primary" @click="openAutoHeightDrawer"></Button>

View File

@ -0,0 +1,25 @@
<script lang="ts" setup>
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
const [Modal, modalApi] = useVbenModal({
onCancel() {
modalApi.close();
},
onConfirm() {
message.info('onConfirm');
// modalApi.close();
},
});
</script>
<template>
<Modal
append-to-main
class="w-[600px]"
title="基础弹窗示例"
title-tooltip="标题提示内容"
>
此弹窗指定在内容区域打开
</Modal>
</template>

View File

@ -9,6 +9,7 @@ import BaseDemo from './base-demo.vue';
import DragDemo from './drag-demo.vue'; import DragDemo from './drag-demo.vue';
import DynamicDemo from './dynamic-demo.vue'; import DynamicDemo from './dynamic-demo.vue';
import FormModalDemo from './form-modal-demo.vue'; import FormModalDemo from './form-modal-demo.vue';
import InContentModalDemo from './in-content-demo.vue';
import SharedDataDemo from './shared-data-demo.vue'; import SharedDataDemo from './shared-data-demo.vue';
const [BaseModal, baseModalApi] = useVbenModal({ const [BaseModal, baseModalApi] = useVbenModal({
@ -16,6 +17,11 @@ const [BaseModal, baseModalApi] = useVbenModal({
connectedComponent: BaseDemo, connectedComponent: BaseDemo,
}); });
const [InContentModal, inContentModalApi] = useVbenModal({
//
connectedComponent: InContentModalDemo,
});
const [AutoHeightModal, autoHeightModalApi] = useVbenModal({ const [AutoHeightModal, autoHeightModalApi] = useVbenModal({
connectedComponent: AutoHeightDemo, connectedComponent: AutoHeightDemo,
}); });
@ -40,6 +46,10 @@ function openBaseModal() {
baseModalApi.open(); baseModalApi.open();
} }
function openInContentModal() {
inContentModalApi.open();
}
function openAutoHeightModal() { function openAutoHeightModal() {
autoHeightModalApi.open(); autoHeightModalApi.open();
} }
@ -76,6 +86,7 @@ function openFormModal() {
<template> <template>
<Page <Page
auto-content-height
description="弹窗组件常用于在不离开当前页面的情况下显示额外的信息、表单或操作提示更多api请查看组件文档。" description="弹窗组件常用于在不离开当前页面的情况下显示额外的信息、表单或操作提示更多api请查看组件文档。"
title="弹窗组件示例" title="弹窗组件示例"
> >
@ -83,6 +94,7 @@ function openFormModal() {
<DocButton path="/components/common-ui/vben-modal" /> <DocButton path="/components/common-ui/vben-modal" />
</template> </template>
<BaseModal /> <BaseModal />
<InContentModal />
<AutoHeightModal /> <AutoHeightModal />
<DragModal /> <DragModal />
<DynamicModal /> <DynamicModal />
@ -93,6 +105,11 @@ function openFormModal() {
<Button type="primary" @click="openBaseModal"></Button> <Button type="primary" @click="openBaseModal"></Button>
</Card> </Card>
<Card class="mb-4" title="指定容器">
<p class="mb-3">在内容区域打开弹窗的示例</p>
<Button type="primary" @click="openInContentModal"></Button>
</Card>
<Card class="mb-4" title="内容高度自适应"> <Card class="mb-4" title="内容高度自适应">
<p class="mb-3">可根据内容并自动调整高度</p> <p class="mb-3">可根据内容并自动调整高度</p>
<Button type="primary" @click="openAutoHeightModal"></Button> <Button type="primary" @click="openAutoHeightModal"></Button>

View File

@ -76,6 +76,12 @@ function changeLoading() {
<template #extra> <template #extra>
<DocButton path="/components/common-ui/vben-vxe-table" /> <DocButton path="/components/common-ui/vben-vxe-table" />
</template> </template>
<Modal title="弹窗测试">
<p>这是一个弹窗</p>
</Modal>
<Drawer title="抽屉测试">
<p>这是一个抽屉</p>
</Drawer>
<Grid table-title="" table-title-help=""> <Grid table-title="" table-title-help="">
<!-- <template #toolbar-actions> <!-- <template #toolbar-actions>
<Button class="mr-2" type="primary">左侧插槽</Button> <Button class="mr-2" type="primary">左侧插槽</Button>

View File

@ -1340,6 +1340,9 @@ importers:
'@vben-core/shadcn-ui': '@vben-core/shadcn-ui':
specifier: workspace:* specifier: workspace:*
version: link:../shadcn-ui version: link:../shadcn-ui
'@vben-core/shared':
specifier: workspace:*
version: link:../../base/shared
'@vben-core/typings': '@vben-core/typings':
specifier: workspace:* specifier: workspace:*
version: link:../../base/typings version: link:../../base/typings