Skip to content

feat: add useAlertContext for Alert component #5947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/src/components/common-ui/vben-alert.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Alert提供的功能与Modal类似,但只适用于简单应用场景。例如

:::

::: tip 注意

Alert提供的快捷方法alert、confirm、prompt动态创建的弹窗在已打开的情况下,不支持HMR(热更新),代码变更后需要关闭这些弹窗后重新打开。

:::

::: tip README

下方示例代码中的,存在一些主题色未适配、样式缺失的问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
Expand All @@ -32,6 +38,19 @@ Alert提供的功能与Modal类似,但只适用于简单应用场景。例如

<DemoPreview dir="demos/vben-alert/prompt" />

## useAlertContext

当弹窗的content、footer、icon使用自定义组件时,在这些组件中可以使用 `useAlertContext` 获取当前弹窗的上下文对象,用来主动控制弹窗。

::: tip 注意 `useAlertContext`只能用在setup或者函数式组件中。:::

### Methods

| 方法 | 描述 | 类型 | 版本要求 |
| --------- | ------------------ | -------- | -------- |
| doConfirm | 调用弹窗的确认操作 | ()=>void | >5.5.4 |
| doCancel | 调用弹窗的取消操作 | ()=>void | >5.5.4 |

## 类型说明

```ts
Expand Down
34 changes: 24 additions & 10 deletions docs/src/demos/vben-alert/prompt/index.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { h } from 'vue';

import { alert, prompt, VbenButton } from '@vben/common-ui';
import { alert, prompt, useAlertContext, VbenButton } from '@vben/common-ui';

import { Input, RadioGroup, Select } from 'ant-design-vue';
import { BadgeJapaneseYen } from 'lucide-vue-next';
Expand All @@ -20,16 +20,30 @@ function showPrompt() {

function showSlotsPrompt() {
prompt({
component: Input,
componentProps: {
placeholder: '请输入',
prefix: '充值金额',
type: 'number',
},
componentSlots: {
addonAfter: () => h(BadgeJapaneseYen),
component: () => {
// 获取弹窗上下文。注意:只能在setup或者函数式组件中调用
const { doConfirm } = useAlertContext();
return h(
Input,
{
onKeydown(e: KeyboardEvent) {
if (e.key === 'Enter') {
e.preventDefault();
// 调用弹窗提供的确认方法
doConfirm();
}
},
placeholder: '请输入',
prefix: '充值金额:',
type: 'number',
},
{
addonAfter: () => h(BadgeJapaneseYen),
},
);
},
content: '此弹窗演示了如何使用componentSlots传递自定义插槽',
content:
'此弹窗演示了如何使用自定义插槽,并且可以使用useAlertContext获取到弹窗的上下文。\n在输入框中按下回车键会触发确认操作。',
icon: 'question',
modelPropName: 'value',
}).then((val) => {
Expand Down
8 changes: 2 additions & 6 deletions packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { AlertProps, BeforeCloseScope, PromptProps } from './alert';
import { h, nextTick, ref, render } from 'vue';

import { useSimpleLocale } from '@vben-core/composables';
import { Input } from '@vben-core/shadcn-ui';
import { Input, VbenRenderContent } from '@vben-core/shadcn-ui';
import { isFunction, isString } from '@vben-core/shared/utils';

import Alert from './alert.vue';
Expand Down Expand Up @@ -146,11 +146,7 @@ export async function vbenPrompt<T = any>(
const inputComponentRef = ref<null | VNode>(null);
const staticContents: Component[] = [];

if (isString(content)) {
staticContents.push(h('span', content));
} else if (content) {
staticContents.push(content as Component);
}
staticContents.push(h(VbenRenderContent, { content, renderBr: true }));

const modelPropName = _modelPropName || 'modelValue';
const componentProps = { ..._componentProps };
Expand Down
27 changes: 27 additions & 0 deletions packages/@core/ui-kit/popup-ui/src/alert/alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type { Component, VNode, VNodeArrayChildren } from 'vue';

import type { Recordable } from '@vben-core/typings';

import { createContext } from '@vben-core/shadcn-ui';

export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';

export type BeforeCloseScope = {
Expand Down Expand Up @@ -70,3 +72,28 @@ export type PromptProps<T = any> = {
/** 输入组件的值属性名 */
modelPropName?: string;
} & Omit<AlertProps, 'beforeClose'>;

/**
* Alert上下文
*/
export type AlertContext = {
/** 执行取消操作 */
doCancel: () => void;
/** 执行确认操作 */
doConfirm: () => void;
};

export const [injectAlertContext, provideAlertContext] =
createContext<AlertContext>('VbenAlertContext');

/**
* 获取Alert上下文
* @returns AlertContext
*/
export function useAlertContext() {
const context = injectAlertContext();
if (!context) {
throw new Error('useAlertContext must be used within an AlertProvider');
}
return context;
}
21 changes: 20 additions & 1 deletion packages/@core/ui-kit/popup-ui/src/alert/alert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
import { globalShareState } from '@vben-core/shared/global-state';
import { cn } from '@vben-core/shared/utils';

import { provideAlertContext } from './alert';

const props = withDefaults(defineProps<AlertProps>(), {
bordered: true,
buttonAlign: 'end',
Expand Down Expand Up @@ -87,6 +89,23 @@ const getIconRender = computed(() => {
}
return iconRender;
});

function doCancel() {
isConfirm.value = false;
handleOpenChange(false);
}

function doConfirm() {
isConfirm.value = true;
handleOpenChange(false);
emits('confirm');
}

provideAlertContext({
doCancel,
doConfirm,
});

function handleConfirm() {
isConfirm.value = true;
emits('confirm');
Expand Down Expand Up @@ -152,7 +171,7 @@ async function handleOpenChange(val: boolean) {
</div>
</AlertDialogTitle>
<AlertDialogDescription>
<div class="m-4 mb-6 min-h-[30px]">
<div class="m-4 min-h-[30px]">
<VbenRenderContent :content="content" render-br />
</div>
<VbenLoading v-if="loading && contentMasking" :spinning="loading" />
Expand Down
9 changes: 7 additions & 2 deletions packages/@core/ui-kit/popup-ui/src/alert/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export * from './alert';

export type {
AlertProps,
BeforeCloseScope,
IconType,
PromptProps,
} from './alert';
export { useAlertContext } from './alert';
export { default as Alert } from './alert.vue';
export {
vbenAlert as alert,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ export default defineComponent({
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'));
}
for (const [i, line] of lines.entries()) {
result.push(h('p', { key: i }, line));
// if (i < lines.length - 1) {
// result.push(h('br'));
// }
}
return result;
} else {
Expand Down