diff --git a/docs/src/components/common-ui/vben-alert.md b/docs/src/components/common-ui/vben-alert.md index 6a477e31..61caac6d 100644 --- a/docs/src/components/common-ui/vben-alert.md +++ b/docs/src/components/common-ui/vben-alert.md @@ -43,6 +43,9 @@ export type BeforeCloseScope = { isConfirm: boolean; }; +/** + * alert 属性 + */ export type AlertProps = { /** 关闭前的回调,如果返回false,则终止关闭 */ beforeClose?: ( @@ -50,6 +53,8 @@ export type AlertProps = { ) => boolean | Promise | undefined; /** 边框 */ bordered?: boolean; + /** 按钮对齐方式 */ + buttonAlign?: 'center' | 'end' | 'start'; /** 取消按钮的标题 */ cancelText?: string; /** 是否居中显示 */ @@ -62,6 +67,8 @@ export type AlertProps = { content: Component | string; /** 弹窗内容的额外样式 */ contentClass?: string; + /** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/ + contentMasking?: boolean; /** 弹窗的图标(在标题的前面) */ icon?: Component | IconType; /** 是否显示取消按钮 */ @@ -70,6 +77,25 @@ export type AlertProps = { title?: string; }; +/** prompt 属性 */ +export type PromptProps = { + /** 关闭前的回调,如果返回false,则终止关闭 */ + beforeClose?: (scope: { + isConfirm: boolean; + value: T | undefined; + }) => boolean | Promise | undefined; + /** 用于接受用户输入的组件 */ + component?: Component; + /** 输入组件的属性 */ + componentProps?: Recordable; + /** 输入组件的插槽 */ + componentSlots?: Recordable; + /** 默认值 */ + defaultValue?: T; + /** 输入组件的值属性名 */ + modelPropName?: string; +} & Omit; + /** * 函数签名 * alert和confirm的函数签名相同。 diff --git a/docs/src/demos/vben-alert/alert/index.vue b/docs/src/demos/vben-alert/alert/index.vue index 103ce64f..9ba18a4d 100644 --- a/docs/src/demos/vben-alert/alert/index.vue +++ b/docs/src/demos/vben-alert/alert/index.vue @@ -3,7 +3,7 @@ import { h } from 'vue'; import { alert, VbenButton } from '@vben/common-ui'; -import { Empty } from 'ant-design-vue'; +import { Result } from 'ant-design-vue'; function showAlert() { alert('This is an alert message'); @@ -18,7 +18,12 @@ function showIconAlert() { function showCustomAlert() { alert({ - content: h(Empty, { description: '什么都没有' }), + buttonAlign: 'center', + content: h(Result, { + status: 'success', + subTitle: '已成功创建订单。订单ID:2017182818828182881', + title: '操作成功', + }), }); } diff --git a/docs/src/demos/vben-alert/prompt/index.vue b/docs/src/demos/vben-alert/prompt/index.vue index 423124e7..c9cf5c3e 100644 --- a/docs/src/demos/vben-alert/prompt/index.vue +++ b/docs/src/demos/vben-alert/prompt/index.vue @@ -1,7 +1,10 @@ diff --git a/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts b/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts index d022d1cd..20e4254c 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts +++ b/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts @@ -1,10 +1,10 @@ -import type { Component } from 'vue'; +import type { Component, VNode } from 'vue'; import type { Recordable } from '@vben-core/typings'; -import type { AlertProps, BeforeCloseScope } from './alert'; +import type { AlertProps, BeforeCloseScope, PromptProps } from './alert'; -import { h, ref, render } from 'vue'; +import { h, nextTick, ref, render } from 'vue'; import { useSimpleLocale } from '@vben-core/composables'; import { Input } from '@vben-core/shadcn-ui'; @@ -130,40 +130,58 @@ export function vbenConfirm( } export async function vbenPrompt( - options: Omit & { - beforeClose?: (scope: { - isConfirm: boolean; - value: T | undefined; - }) => boolean | Promise | undefined; - component?: Component; - componentProps?: Recordable; - defaultValue?: T; - modelPropName?: string; - }, + options: PromptProps, ): Promise { const { component: _component, componentProps: _componentProps, + componentSlots, content, defaultValue, modelPropName: _modelPropName, ...delegated } = options; - const contents: Component[] = []; + const modelValue = ref(defaultValue); + const inputComponentRef = ref(null); + const staticContents: Component[] = []; + if (isString(content)) { - contents.push(h('span', content)); - } else { - contents.push(content); + staticContents.push(h('span', content)); + } else if (content) { + staticContents.push(content as Component); } - const componentProps = _componentProps || {}; + const modelPropName = _modelPropName || 'modelValue'; - componentProps[modelPropName] = modelValue.value; - componentProps[`onUpdate:${modelPropName}`] = (val: any) => { - modelValue.value = val; + const componentProps = { ..._componentProps }; + + // 每次渲染时都会重新计算的内容函数 + const contentRenderer = () => { + const currentProps = { ...componentProps }; + + // 设置当前值 + currentProps[modelPropName] = modelValue.value; + + // 设置更新处理函数 + currentProps[`onUpdate:${modelPropName}`] = (val: T) => { + modelValue.value = val; + }; + + // 创建输入组件 + inputComponentRef.value = h( + _component || Input, + currentProps, + componentSlots, + ); + + // 返回包含静态内容和输入组件的数组 + return h( + 'div', + { class: 'flex flex-col gap-2' }, + { default: () => [...staticContents, inputComponentRef.value] }, + ); }; - const componentRef = h(_component || Input, componentProps); - contents.push(componentRef); + const props: AlertProps & Recordable = { ...delegated, async beforeClose(scope: BeforeCloseScope) { @@ -174,23 +192,46 @@ export async function vbenPrompt( }); } }, - 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(); + // 使用函数形式,每次渲染都会重新计算内容 + content: contentRenderer, + contentMasking: true, + async onOpened() { + await nextTick(); + const componentRef: null | VNode = inputComponentRef.value; + if (componentRef) { + if ( + componentRef.component?.exposed && + isFunction(componentRef.component.exposed.focus) + ) { + componentRef.component.exposed.focus(); + } else { + if (componentRef.el) { + if ( + isFunction(componentRef.el.focus) && + ['BUTTON', 'INPUT', 'SELECT', 'TEXTAREA'].includes( + componentRef.el.tagName, + ) + ) { + componentRef.el.focus(); + } else if (isFunction(componentRef.el.querySelector)) { + const focusableElement = componentRef.el.querySelector( + 'input, select, textarea, button', + ); + if (focusableElement && isFunction(focusableElement.focus)) { + focusableElement.focus(); + } + } else if ( + componentRef.el.nextElementSibling && + isFunction(componentRef.el.nextElementSibling.focus) + ) { + componentRef.el.nextElementSibling.focus(); + } + } + } } }, }; + await vbenConfirm(props); return modelValue.value; } diff --git a/packages/@core/ui-kit/popup-ui/src/alert/alert.ts b/packages/@core/ui-kit/popup-ui/src/alert/alert.ts index 6a574daa..49d65ed5 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/alert.ts +++ b/packages/@core/ui-kit/popup-ui/src/alert/alert.ts @@ -1,4 +1,6 @@ -import type { Component } from 'vue'; +import type { Component, VNode, VNodeArrayChildren } from 'vue'; + +import type { Recordable } from '@vben-core/typings'; export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning'; @@ -13,6 +15,11 @@ export type AlertProps = { ) => boolean | Promise | undefined; /** 边框 */ bordered?: boolean; + /** + * 按钮对齐方式 + * @default 'end' + */ + buttonAlign?: 'center' | 'end' | 'start'; /** 取消按钮的标题 */ cancelText?: string; /** 是否居中显示 */ @@ -25,6 +32,8 @@ export type AlertProps = { content: Component | string; /** 弹窗内容的额外样式 */ contentClass?: string; + /** 执行beforeClose回调期间,在内容区域显示一个loading遮罩*/ + contentMasking?: boolean; /** 弹窗的图标(在标题的前面) */ icon?: Component | IconType; /** 是否显示取消按钮 */ @@ -32,3 +41,26 @@ export type AlertProps = { /** 弹窗标题 */ title?: string; }; + +/** Prompt属性 */ +export type PromptProps = { + /** 关闭前的回调,如果返回false,则终止关闭 */ + beforeClose?: (scope: { + isConfirm: boolean; + value: T | undefined; + }) => boolean | Promise | undefined; + /** 用于接受用户输入的组件 */ + component?: Component; + /** 输入组件的属性 */ + componentProps?: Recordable; + /** 输入组件的插槽 */ + componentSlots?: + | (() => any) + | Recordable + | VNode + | VNodeArrayChildren; + /** 默认值 */ + defaultValue?: T; + /** 输入组件的值属性名 */ + modelPropName?: string; +} & Omit; diff --git a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue index 2cd334b7..8c7c4541 100644 --- a/packages/@core/ui-kit/popup-ui/src/alert/alert.vue +++ b/packages/@core/ui-kit/popup-ui/src/alert/alert.vue @@ -30,6 +30,7 @@ import { cn } from '@vben-core/shared/utils'; const props = withDefaults(defineProps(), { bordered: true, + buttonAlign: 'end', centered: true, containerClass: 'w-[520px]', }); @@ -154,9 +155,9 @@ async function handleOpenChange(val: boolean) {
- + -
+