feat: add preset alert, confirm, prompt components that can be simple called (#5843)

* feat: add preset alert, confirm, prompt components that can be simple called

* fix: type define
pull/78/MERGE
Netfan 2025-04-01 15:10:18 +08:00 committed by GitHub
parent 0e3abc2b53
commit 44138f578f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 941 additions and 4 deletions

View File

@ -168,6 +168,10 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] {
link: 'common-ui/vben-api-component',
text: 'ApiComponent Api组件包装器',
},
{
link: 'common-ui/vben-alert',
text: 'Alert 轻量提示框',
},
{
link: 'common-ui/vben-modal',
text: 'Modal 模态框',

View File

@ -0,0 +1,101 @@
---
outline: deep
---
# Vben Alert 轻量提示框
框架提供的一些用于轻量提示的弹窗仅使用js代码即可快速动态创建提示而不需要在template写任何代码。
::: info 应用场景
Alert提供的功能与Modal类似但只适用于简单应用场景。例如临时性、动态地弹出模态确认框、输入框等。如果对弹窗有更复杂的需求请使用VbenModal
:::
::: tip README
下方示例代码中的,存在一些主题色未适配、样式缺失的问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
:::
## 基础用法
使用 `alert` 创建只有一个确认按钮的提示框。
<DemoPreview dir="demos/vben-alert/alert" />
使用 `confirm` 创建有确认和取消按钮的提示框。
<DemoPreview dir="demos/vben-alert/confirm" />
使用 `prompt` 创建有确认和取消按钮、接受用户输入的提示框。
<DemoPreview dir="demos/vben-alert/prompt" />
## 类型说明
```ts
/** 预置的图标类型 */
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
export type AlertProps = {
/** 关闭前的回调如果返回false则终止关闭 */
beforeClose?: () => boolean | Promise<boolean | undefined> | undefined;
/** 边框 */
bordered?: boolean;
/** 取消按钮的标题 */
cancelText?: string;
/** 是否居中显示 */
centered?: boolean;
/** 确认按钮的标题 */
confirmText?: string;
/** 弹窗容器的额外样式 */
containerClass?: string;
/** 弹窗提示内容 */
content: Component | string;
/** 弹窗内容的额外样式 */
contentClass?: string;
/** 弹窗的图标(在标题的前面) */
icon?: Component | IconType;
/** 是否显示取消按钮 */
showCancel?: boolean;
/** 弹窗标题 */
title?: string;
};
/**
* 函数签名
* alert和confirm的函数签名相同。
* confirm默认会显示取消按钮而alert默认只有一个按钮
* */
export function alert(options: AlertProps): Promise<void>;
export function alert(
message: string,
options?: Partial<AlertProps>,
): Promise<void>;
export function alert(
message: string,
title?: string,
options?: Partial<AlertProps>,
): Promise<void>;
/**
* 弹出输入框的函数签名。
* 参数beforeClose会传入用户当前输入的值
* component指定接受用户输入的组件默认为Input
* componentProps 为输入组件设置的属性数据
* defaultValue 默认的值
* modelPropName 输入组件的值属性名称。默认为modelValue
*/
export async function prompt<T = any>(
options: Omit<AlertProps, 'beforeClose'> & {
beforeClose?: (
val: T,
) => boolean | Promise<boolean | undefined> | undefined;
component?: Component;
componentProps?: Recordable<any>;
defaultValue?: T;
modelPropName?: string;
},
): Promise<T | undefined>;
```

View File

@ -0,0 +1,31 @@
<script lang="ts" setup>
import { h } from 'vue';
import { alert, VbenButton } from '@vben/common-ui';
import { Empty } from 'ant-design-vue';
function showAlert() {
alert('This is an alert message');
}
function showIconAlert() {
alert({
content: 'This is an alert message with icon',
icon: 'success',
});
}
function showCustomAlert() {
alert({
content: h(Empty, { description: '什么都没有' }),
});
}
</script>
<template>
<div class="flex gap-4">
<VbenButton @click="showAlert">Alert</VbenButton>
<VbenButton @click="showIconAlert">Alert With Icon</VbenButton>
<VbenButton @click="showCustomAlert">Alert With Custom Content</VbenButton>
</div>
</template>

View File

@ -0,0 +1,39 @@
<script lang="ts" setup>
import { alert, confirm, VbenButton } from '@vben/common-ui';
function showConfirm() {
confirm('This is an alert message')
.then(() => {
alert('Confirmed');
})
.catch(() => {
alert('Canceled');
});
}
function showIconConfirm() {
confirm({
content: 'This is an alert message with icon',
icon: 'success',
});
}
function showAsyncConfirm() {
confirm({
beforeClose() {
return new Promise((resolve) => setTimeout(resolve, 2000));
},
content: 'This is an alert message with async confirm',
icon: 'success',
}).then(() => {
alert('Confirmed');
});
}
</script>
<template>
<div class="flex gap-4">
<VbenButton @click="showConfirm">Confirm</VbenButton>
<VbenButton @click="showIconConfirm">Confirm With Icon</VbenButton>
<VbenButton @click="showAsyncConfirm">Async Confirm</VbenButton>
</div>
</template>

View File

@ -0,0 +1,41 @@
<script lang="ts" setup>
import { alert, prompt, VbenButton } from '@vben/common-ui';
import { VbenSelect } from '@vben-core/shadcn-ui';
function showPrompt() {
prompt({
content: '请输入一些东西',
})
.then((val) => {
alert(`已收到你的输入:${val}`);
})
.catch(() => {
alert('Canceled');
});
}
function showSelectPrompt() {
prompt({
component: VbenSelect,
componentProps: {
options: [
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
{ label: 'Option 3', value: 'option3' },
],
placeholder: '请选择',
},
content: 'This is an alert message with icon',
icon: 'question',
}).then((val) => {
alert(`你选择的是${val}`);
});
}
</script>
<template>
<div class="flex gap-4">
<VbenButton @click="showPrompt">Prompt</VbenButton>
<VbenButton @click="showSelectPrompt">Confirm With Select</VbenButton>
</div>
</template>

View File

@ -15,8 +15,10 @@ export {
ChevronsLeft,
ChevronsRight,
Circle,
CircleAlert,
CircleCheckBig,
CircleHelp,
CircleX,
Copy,
CornerDownLeft,
Ellipsis,

View File

@ -6,6 +6,7 @@ export const messages: Record<Locale, Record<string, string>> = {
collapse: 'Collapse',
confirm: 'Confirm',
expand: 'Expand',
prompt: 'Prompt',
reset: 'Reset',
submit: 'Submit',
},
@ -14,6 +15,7 @@ export const messages: Record<Locale, Record<string, string>> = {
collapse: '收起',
confirm: '确认',
expand: '展开',
prompt: '提示',
reset: '重置',
submit: '提交',
},

View File

@ -0,0 +1,203 @@
import type { Component } from 'vue';
import type { Recordable } from '@vben-core/typings';
import type { AlertProps } from './alert';
import { h, ref, render } from 'vue';
import { useSimpleLocale } from '@vben-core/composables';
import { Input } from '@vben-core/shadcn-ui';
import { isFunction, isString } from '@vben-core/shared/utils';
import Alert from './alert.vue';
const alerts = ref<Array<{ container: HTMLElement; instance: Component }>>([]);
const { $t } = useSimpleLocale();
export function vbenAlert(options: AlertProps): Promise<void>;
export function vbenAlert(
message: string,
options?: Partial<AlertProps>,
): Promise<void>;
export function vbenAlert(
message: string,
title?: string,
options?: Partial<AlertProps>,
): Promise<void>;
export function vbenAlert(
arg0: AlertProps | string,
arg1?: Partial<AlertProps> | string,
arg2?: Partial<AlertProps>,
): Promise<void> {
return new Promise((resolve, reject) => {
const options: AlertProps = isString(arg0)
? {
content: arg0,
}
: { ...arg0 };
if (arg1) {
if (isString(arg1)) {
options.title = arg1;
} else if (!isString(arg1)) {
// 如果第二个参数是对象,则合并到选项中
Object.assign(options, arg1);
}
}
if (arg2 && !isString(arg2)) {
Object.assign(options, arg2);
}
// 创建容器元素
const container = document.createElement('div');
document.body.append(container);
// 创建一个引用,用于在回调中访问实例
const alertRef = { container, instance: null as any };
const props: AlertProps & Recordable<any> = {
onClosed: (isConfirm: boolean) => {
// 移除组件实例以及创建的所有dom恢复页面到打开前的状态
// 从alerts数组中移除该实例
alerts.value = alerts.value.filter((item) => item !== alertRef);
// 从DOM中移除容器
render(null, container);
if (container.parentNode) {
container.remove();
}
// 解析 Promise传递用户操作结果
if (isConfirm) {
resolve();
} else {
reject(new Error('dialog cancelled'));
}
},
...options,
open: true,
title: options.title ?? $t.value('prompt'),
};
// 创建Alert组件的VNode
const vnode = h(Alert, props);
// 渲染组件到容器
render(vnode, container);
// 保存组件实例引用
alertRef.instance = vnode.component?.proxy as Component;
// 将实例和容器添加到alerts数组中
alerts.value.push(alertRef);
});
}
export function vbenConfirm(options: AlertProps): Promise<void>;
export function vbenConfirm(
message: string,
options?: Partial<AlertProps>,
): Promise<void>;
export function vbenConfirm(
message: string,
title?: string,
options?: Partial<AlertProps>,
): Promise<void>;
export function vbenConfirm(
arg0: AlertProps | string,
arg1?: Partial<AlertProps> | string,
arg2?: Partial<AlertProps>,
): Promise<void> {
const defaultProps: Partial<AlertProps> = {
showCancel: true,
};
if (!arg1) {
return isString(arg0)
? vbenAlert(arg0, defaultProps)
: vbenAlert({ ...defaultProps, ...arg0 });
} else if (!arg2) {
return isString(arg1)
? vbenAlert(arg0 as string, arg1, defaultProps)
: vbenAlert(arg0 as string, { ...defaultProps, ...arg1 });
}
return vbenAlert(arg0 as string, arg1 as string, {
...defaultProps,
...arg2,
});
}
export async function vbenPrompt<T = any>(
options: Omit<AlertProps, 'beforeClose'> & {
beforeClose?: (
val: T,
) => boolean | Promise<boolean | undefined> | undefined;
component?: Component;
componentProps?: Recordable<any>;
defaultValue?: T;
modelPropName?: string;
},
): Promise<T | undefined> {
const {
component: _component,
componentProps: _componentProps,
content,
defaultValue,
modelPropName: _modelPropName,
...delegated
} = options;
const contents: Component[] = [];
const modelValue = ref<T | undefined>(defaultValue);
if (isString(content)) {
contents.push(h('span', content));
} else {
contents.push(content);
}
const componentProps = _componentProps || {};
const modelPropName = _modelPropName || 'modelValue';
componentProps[modelPropName] = modelValue.value;
componentProps[`onUpdate:${modelPropName}`] = (val: any) => {
modelValue.value = val;
};
const componentRef = h(_component || Input, componentProps);
contents.push(componentRef);
const props: AlertProps & Recordable<any> = {
...delegated,
async beforeClose() {
if (delegated.beforeClose) {
return await delegated.beforeClose(modelValue.value);
}
},
content: h(
'div',
{ class: 'flex flex-col gap-2' },
{ default: () => contents },
),
onOpened() {
// 组件挂载完成后,自动聚焦到输入组件
if (
componentRef.component?.exposed &&
isFunction(componentRef.component.exposed.focus)
) {
componentRef.component.exposed.focus();
} else if (componentRef.el && isFunction(componentRef.el.focus)) {
componentRef.el.focus();
}
},
};
await vbenConfirm(props);
return modelValue.value;
}
export function clearAllAlerts() {
alerts.value.forEach((alert) => {
// 从DOM中移除容器
render(null, alert.container);
if (alert.container.parentNode) {
alert.container.remove();
}
});
alerts.value = [];
}

View File

@ -0,0 +1,28 @@
import type { Component } from 'vue';
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
export type AlertProps = {
/** 关闭前的回调如果返回false则终止关闭 */
beforeClose?: () => boolean | Promise<boolean | undefined> | undefined;
/** 边框 */
bordered?: boolean;
/** 取消按钮的标题 */
cancelText?: string;
/** 是否居中显示 */
centered?: boolean;
/** 确认按钮的标题 */
confirmText?: string;
/** 弹窗容器的额外样式 */
containerClass?: string;
/** 弹窗提示内容 */
content: Component | string;
/** 弹窗内容的额外样式 */
contentClass?: string;
/** 弹窗的图标(在标题的前面) */
icon?: Component | IconType;
/** 是否显示取消按钮 */
showCancel?: boolean;
/** 弹窗标题 */
title?: string;
};

View File

@ -0,0 +1,181 @@
<script lang="ts" setup>
import type { Component } from 'vue';
import type { AlertProps } from './alert';
import { computed, h, nextTick, ref, watch } from 'vue';
import { useSimpleLocale } from '@vben-core/composables';
import {
CircleAlert,
CircleCheckBig,
CircleHelp,
CircleX,
Info,
X,
} from '@vben-core/icons';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
VbenButton,
VbenLoading,
VbenRenderContent,
} from '@vben-core/shadcn-ui';
import { globalShareState } from '@vben-core/shared/global-state';
import { cn } from '@vben-core/shared/utils';
const props = withDefaults(defineProps<AlertProps>(), {
bordered: true,
centered: true,
containerClass: 'w-[520px]',
});
const emits = defineEmits(['closed', 'confirm', 'opened']);
const open = defineModel<boolean>('open', { default: false });
const { $t } = useSimpleLocale();
const components = globalShareState.getComponents();
const isConfirm = ref(false);
watch(open, async (val) => {
await nextTick();
if (val) {
isConfirm.value = false;
} else {
emits('closed', isConfirm.value);
}
});
const getIconRender = computed(() => {
let iconRender: Component | null = null;
if (props.icon) {
if (typeof props.icon === 'string') {
switch (props.icon) {
case 'error': {
iconRender = h(CircleX, {
style: { color: 'hsl(var(--destructive))' },
});
break;
}
case 'info': {
iconRender = h(Info, { style: { color: 'hsl(var(--info))' } });
break;
}
case 'question': {
iconRender = CircleHelp;
break;
}
case 'success': {
iconRender = h(CircleCheckBig, {
style: { color: 'hsl(var(--success))' },
});
break;
}
case 'warning': {
iconRender = h(CircleAlert, {
style: { color: 'hsl(var(--warning))' },
});
break;
}
default: {
iconRender = null;
break;
}
}
}
} else {
iconRender = props.icon ?? null;
}
return iconRender;
});
function handleConfirm() {
isConfirm.value = true;
emits('confirm');
}
function handleCancel() {
open.value = false;
}
const loading = ref(false);
async function handleOpenChange(val: boolean) {
if (!val && props.beforeClose) {
loading.value = true;
try {
const res = await props.beforeClose();
if (res !== false) {
open.value = false;
}
} finally {
loading.value = false;
}
} else {
open.value = val;
}
}
</script>
<template>
<AlertDialog :open="open" @update:open="handleOpenChange">
<AlertDialogContent
:open="open"
:centered="centered"
@opened="emits('opened')"
:class="
cn(
containerClass,
'left-0 right-0 top-[10vh] mx-auto flex max-h-[80%] flex-col p-0 duration-300 sm:rounded-[var(--radius)] md:w-[520px] md:max-w-[80%]',
{
'border-border border': bordered,
'shadow-3xl': !bordered,
'top-1/2 !-translate-y-1/2': centered,
},
)
"
>
<div :class="cn('relative flex-1 overflow-y-auto p-3', contentClass)">
<AlertDialogTitle v-if="title">
<div class="flex items-center">
<component :is="getIconRender" class="mr-2" />
<span class="flex-auto">{{ $t(title) }}</span>
<AlertDialogCancel v-if="showCancel">
<VbenButton
variant="ghost"
size="icon"
class="rounded-full"
:disabled="loading"
>
<X class="text-muted-foreground size-4" />
</VbenButton>
</AlertDialogCancel>
</div>
</AlertDialogTitle>
<AlertDialogDescription>
<div class="m-4 mb-6 min-h-[30px]">
<VbenRenderContent :content="content" render-br />
</div>
<VbenLoading v-if="loading" :spinning="loading" />
</AlertDialogDescription>
<div class="flex justify-end gap-x-2">
<AlertDialogCancel
v-if="showCancel"
@click="handleCancel"
:disabled="loading"
>
<component
:is="components.DefaultButton || VbenButton"
variant="ghost"
>
{{ cancelText || $t('cancel') }}
</component>
</AlertDialogCancel>
<AlertDialogAction @click="handleConfirm">
<component
:is="components.PrimaryButton || VbenButton"
:loading="loading"
>
{{ confirmText || $t('confirm') }}
</component>
</AlertDialogAction>
</div>
</div>
</AlertDialogContent>
</AlertDialog>
</template>

View File

@ -0,0 +1,9 @@
export * from './alert';
export { default as Alert } from './alert.vue';
export {
vbenAlert as alert,
clearAllAlerts,
vbenConfirm as confirm,
vbenPrompt as prompt,
} from './AlertBuilder';

View File

@ -1,2 +1,3 @@
export * from './alert';
export * from './drawer';
export * from './modal';

View File

@ -3,7 +3,7 @@ import type { Component, PropType } from 'vue';
import { defineComponent, h } from 'vue';
import { isFunction, isObject } from '@vben-core/shared/utils';
import { isFunction, isObject, isString } from '@vben-core/shared/utils';
export default defineComponent({
name: 'RenderContent',
@ -14,6 +14,10 @@ export default defineComponent({
| undefined,
type: [Object, String, Function],
},
renderBr: {
default: false,
type: Boolean,
},
},
setup(props, { attrs, slots }) {
return () => {
@ -24,7 +28,20 @@ export default defineComponent({
(isObject(props.content) || isFunction(props.content)) &&
props.content !== null;
if (!isComponent) {
return props.content;
if (props.renderBr && isString(props.content)) {
const lines = props.content.split('\n');
const result = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
result.push(h('span', { key: i }, line));
if (i < lines.length - 1) {
result.push(h('br'));
}
}
return result;
} else {
return props.content;
}
}
return h(props.content as never, {
...attrs,

View File

@ -0,0 +1,16 @@
<script setup lang="ts">
import type { AlertDialogEmits, AlertDialogProps } from 'radix-vue';
import { AlertDialogRoot, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<AlertDialogProps>();
const emits = defineEmits<AlertDialogEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<AlertDialogRoot v-bind="forwarded">
<slot></slot>
</AlertDialogRoot>
</template>

View File

@ -0,0 +1,13 @@
<script setup lang="ts">
import type { AlertDialogActionProps } from 'radix-vue';
import { AlertDialogAction } from 'radix-vue';
const props = defineProps<AlertDialogActionProps>();
</script>
<template>
<AlertDialogAction v-bind="props">
<slot></slot>
</AlertDialogAction>
</template>

View File

@ -0,0 +1,13 @@
<script setup lang="ts">
import type { AlertDialogCancelProps } from 'radix-vue';
import { AlertDialogCancel } from 'radix-vue';
const props = defineProps<AlertDialogCancelProps>();
</script>
<template>
<AlertDialogCancel v-bind="props">
<slot></slot>
</AlertDialogCancel>
</template>

View File

@ -0,0 +1,91 @@
<script setup lang="ts">
import type {
AlertDialogContentEmits,
AlertDialogContentProps,
} from 'radix-vue';
import type { ClassType } from '@vben-core/typings';
import { computed, ref } from 'vue';
import { cn } from '@vben-core/shared/utils';
import {
AlertDialogContent,
AlertDialogPortal,
useForwardPropsEmits,
} from 'radix-vue';
import AlertDialogOverlay from './AlertDialogOverlay.vue';
const props = withDefaults(
defineProps<
AlertDialogContentProps & {
centered?: boolean;
class?: ClassType;
modal?: boolean;
open?: boolean;
overlayBlur?: number;
zIndex?: number;
}
>(),
{ modal: true },
);
const emits = defineEmits<
AlertDialogContentEmits & { close: []; closed: []; opened: [] }
>();
const delegatedProps = computed(() => {
const { class: _, modal: _modal, open: _open, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
const contentRef = ref<InstanceType<typeof AlertDialogContent> | null>(null);
function onAnimationEnd(event: AnimationEvent) {
// contentRef opened/closed
if (event.target === contentRef.value?.$el) {
if (props.open) {
emits('opened');
} else {
emits('closed');
}
}
}
defineExpose({
getContentRef: () => contentRef.value,
});
</script>
<template>
<AlertDialogPortal>
<Transition name="fade">
<AlertDialogOverlay
v-if="open && modal"
:style="{
...(zIndex ? { zIndex } : {}),
position: 'fixed',
backdropFilter:
overlayBlur && overlayBlur > 0 ? `blur(${overlayBlur}px)` : 'none',
}"
@click="() => emits('close')"
/>
</Transition>
<AlertDialogContent
ref="contentRef"
:style="{ ...(zIndex ? { zIndex } : {}), position: 'fixed' }"
@animationend="onAnimationEnd"
v-bind="forwarded"
:class="
cn(
'z-popup 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,
)
"
>
<slot></slot>
</AlertDialogContent>
</AlertDialogPortal>
</template>

View File

@ -0,0 +1,28 @@
<script lang="ts" setup>
import type { AlertDialogDescriptionProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { AlertDialogDescription, useForwardProps } from 'radix-vue';
const props = defineProps<AlertDialogDescriptionProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<AlertDialogDescription
v-bind="forwardedProps"
:class="cn('text-muted-foreground text-sm', props.class)"
>
<slot></slot>
</AlertDialogDescription>
</template>

View File

@ -0,0 +1,8 @@
<script setup lang="ts">
import { useScrollLock } from '@vben-core/composables';
useScrollLock();
</script>
<template>
<div class="bg-overlay z-popup inset-0"></div>
</template>

View File

@ -0,0 +1,30 @@
<script setup lang="ts">
import type { AlertDialogTitleProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { AlertDialogTitle, useForwardProps } from 'radix-vue';
const props = defineProps<AlertDialogTitleProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<AlertDialogTitle
v-bind="forwardedProps"
:class="
cn('text-lg font-semibold leading-none tracking-tight', props.class)
"
>
<slot></slot>
</AlertDialogTitle>
</template>

View File

@ -0,0 +1,6 @@
export { default as AlertDialog } from './AlertDialog.vue';
export { default as AlertDialogAction } from './AlertDialogAction.vue';
export { default as AlertDialogCancel } from './AlertDialogCancel.vue';
export { default as AlertDialogContent } from './AlertDialogContent.vue';
export { default as AlertDialogDescription } from './AlertDialogDescription.vue';
export { default as AlertDialogTitle } from './AlertDialogTitle.vue';

View File

@ -1,4 +1,5 @@
export * from './accordion';
export * from './alert-dialog';
export * from './avatar';
export * from './badge';
export * from './breadcrumb';

View File

@ -1,7 +1,16 @@
<script lang="ts" setup>
import { Page, useVbenModal } from '@vben/common-ui';
import { onBeforeUnmount } from 'vue';
import { Button, Card, Flex } from 'ant-design-vue';
import {
alert,
clearAllAlerts,
confirm,
Page,
prompt,
useVbenModal,
} from '@vben/common-ui';
import { Button, Card, Flex, message } from 'ant-design-vue';
import DocButton from '../doc-button.vue';
import AutoHeightDemo from './auto-height-demo.vue';
@ -103,6 +112,61 @@ function openFormModal() {
})
.open();
}
function openAlert() {
alert({
content: '这是一个弹窗',
icon: 'success',
}).then(() => {
message.info('用户关闭了弹窗');
});
}
onBeforeUnmount(() => {
//
clearAllAlerts();
});
function openConfirm() {
confirm({
beforeClose() {
//
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, 1000);
});
},
content: '这是一个确认弹窗',
icon: 'question',
})
.then(() => {
message.success('用户确认了操作');
})
.catch(() => {
message.error('用户取消了操作');
});
}
async function openPrompt() {
prompt<string>({
async beforeClose(val) {
if (val === '芝士') {
message.error('不能吃芝士');
return false;
}
},
componentProps: { placeholder: '不能吃芝士...' },
content: '中午吃了什么?',
icon: 'question',
})
.then((res) => {
message.success(`用户输入了:${res}`);
})
.catch(() => {
message.error('用户取消了输入');
});
}
</script>
<template>
@ -195,6 +259,14 @@ function openFormModal() {
<Button type="primary" @click="openBlurModal"></Button>
</template>
</Card>
<Card class="w-[300px]" title="轻量提示弹窗">
<p>通过快捷方法创建动态提示弹窗适合一些轻量的提示和确认输入等</p>
<template #actions>
<Button type="primary" @click="openAlert">Alert</Button>
<Button type="primary" @click="openConfirm">Confirm</Button>
<Button type="primary" @click="openPrompt">Prompt</Button>
</template>
</Card>
</Flex>
</Page>
</template>