Skip to content

Commit bf7496f

Browse files
authored
feat: add useAlertContext for Alert component (#5947)
* 新增Alert的子组件中获取弹窗上下文的能力
1 parent 9700150 commit bf7496f

File tree

7 files changed

+104
-25
lines changed

7 files changed

+104
-25
lines changed

docs/src/components/common-ui/vben-alert.md

+19
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ Alert提供的功能与Modal类似,但只适用于简单应用场景。例如
1212

1313
:::
1414

15+
::: tip 注意
16+
17+
Alert提供的快捷方法alert、confirm、prompt动态创建的弹窗在已打开的情况下,不支持HMR(热更新),代码变更后需要关闭这些弹窗后重新打开。
18+
19+
:::
20+
1521
::: tip README
1622

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

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

41+
## useAlertContext
42+
43+
当弹窗的content、footer、icon使用自定义组件时,在这些组件中可以使用 `useAlertContext` 获取当前弹窗的上下文对象,用来主动控制弹窗。
44+
45+
::: tip 注意 `useAlertContext`只能用在setup或者函数式组件中。:::
46+
47+
### Methods
48+
49+
| 方法 | 描述 | 类型 | 版本要求 |
50+
| --------- | ------------------ | -------- | -------- |
51+
| doConfirm | 调用弹窗的确认操作 | ()=>void | >5.5.4 |
52+
| doCancel | 调用弹窗的取消操作 | ()=>void | >5.5.4 |
53+
3554
## 类型说明
3655

3756
```ts

docs/src/demos/vben-alert/prompt/index.vue

+24-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<script lang="ts" setup>
22
import { h } from 'vue';
33
4-
import { alert, prompt, VbenButton } from '@vben/common-ui';
4+
import { alert, prompt, useAlertContext, VbenButton } from '@vben/common-ui';
55
66
import { Input, RadioGroup, Select } from 'ant-design-vue';
77
import { BadgeJapaneseYen } from 'lucide-vue-next';
@@ -20,16 +20,30 @@ function showPrompt() {
2020
2121
function showSlotsPrompt() {
2222
prompt({
23-
component: Input,
24-
componentProps: {
25-
placeholder: '请输入',
26-
prefix: '充值金额',
27-
type: 'number',
28-
},
29-
componentSlots: {
30-
addonAfter: () => h(BadgeJapaneseYen),
23+
component: () => {
24+
// 获取弹窗上下文。注意:只能在setup或者函数式组件中调用
25+
const { doConfirm } = useAlertContext();
26+
return h(
27+
Input,
28+
{
29+
onKeydown(e: KeyboardEvent) {
30+
if (e.key === 'Enter') {
31+
e.preventDefault();
32+
// 调用弹窗提供的确认方法
33+
doConfirm();
34+
}
35+
},
36+
placeholder: '请输入',
37+
prefix: '充值金额:',
38+
type: 'number',
39+
},
40+
{
41+
addonAfter: () => h(BadgeJapaneseYen),
42+
},
43+
);
3144
},
32-
content: '此弹窗演示了如何使用componentSlots传递自定义插槽',
45+
content:
46+
'此弹窗演示了如何使用自定义插槽,并且可以使用useAlertContext获取到弹窗的上下文。\n在输入框中按下回车键会触发确认操作。',
3347
icon: 'question',
3448
modelPropName: 'value',
3549
}).then((val) => {

packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { AlertProps, BeforeCloseScope, PromptProps } from './alert';
77
import { h, nextTick, ref, render } from 'vue';
88

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

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

149-
if (isString(content)) {
150-
staticContents.push(h('span', content));
151-
} else if (content) {
152-
staticContents.push(content as Component);
153-
}
149+
staticContents.push(h(VbenRenderContent, { content, renderBr: true }));
154150

155151
const modelPropName = _modelPropName || 'modelValue';
156152
const componentProps = { ..._componentProps };

packages/@core/ui-kit/popup-ui/src/alert/alert.ts

+27
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { Component, VNode, VNodeArrayChildren } from 'vue';
22

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

5+
import { createContext } from '@vben-core/shadcn-ui';
6+
57
export type IconType = 'error' | 'info' | 'question' | 'success' | 'warning';
68

79
export type BeforeCloseScope = {
@@ -70,3 +72,28 @@ export type PromptProps<T = any> = {
7072
/** 输入组件的值属性名 */
7173
modelPropName?: string;
7274
} & Omit<AlertProps, 'beforeClose'>;
75+
76+
/**
77+
* Alert上下文
78+
*/
79+
export type AlertContext = {
80+
/** 执行取消操作 */
81+
doCancel: () => void;
82+
/** 执行确认操作 */
83+
doConfirm: () => void;
84+
};
85+
86+
export const [injectAlertContext, provideAlertContext] =
87+
createContext<AlertContext>('VbenAlertContext');
88+
89+
/**
90+
* 获取Alert上下文
91+
* @returns AlertContext
92+
*/
93+
export function useAlertContext() {
94+
const context = injectAlertContext();
95+
if (!context) {
96+
throw new Error('useAlertContext must be used within an AlertProvider');
97+
}
98+
return context;
99+
}

packages/@core/ui-kit/popup-ui/src/alert/alert.vue

+20-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
import { globalShareState } from '@vben-core/shared/global-state';
2929
import { cn } from '@vben-core/shared/utils';
3030
31+
import { provideAlertContext } from './alert';
32+
3133
const props = withDefaults(defineProps<AlertProps>(), {
3234
bordered: true,
3335
buttonAlign: 'end',
@@ -87,6 +89,23 @@ const getIconRender = computed(() => {
8789
}
8890
return iconRender;
8991
});
92+
93+
function doCancel() {
94+
isConfirm.value = false;
95+
handleOpenChange(false);
96+
}
97+
98+
function doConfirm() {
99+
isConfirm.value = true;
100+
handleOpenChange(false);
101+
emits('confirm');
102+
}
103+
104+
provideAlertContext({
105+
doCancel,
106+
doConfirm,
107+
});
108+
90109
function handleConfirm() {
91110
isConfirm.value = true;
92111
emits('confirm');
@@ -152,7 +171,7 @@ async function handleOpenChange(val: boolean) {
152171
</div>
153172
</AlertDialogTitle>
154173
<AlertDialogDescription>
155-
<div class="m-4 mb-6 min-h-[30px]">
174+
<div class="m-4 min-h-[30px]">
156175
<VbenRenderContent :content="content" render-br />
157176
</div>
158177
<VbenLoading v-if="loading && contentMasking" :spinning="loading" />

packages/@core/ui-kit/popup-ui/src/alert/index.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
export * from './alert';
2-
1+
export type {
2+
AlertProps,
3+
BeforeCloseScope,
4+
IconType,
5+
PromptProps,
6+
} from './alert';
7+
export { useAlertContext } from './alert';
38
export { default as Alert } from './alert.vue';
49
export {
510
vbenAlert as alert,

packages/@core/ui-kit/shadcn-ui/src/components/render-content/render-content.vue

+5-6
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,11 @@ export default defineComponent({
3131
if (props.renderBr && isString(props.content)) {
3232
const lines = props.content.split('\n');
3333
const result = [];
34-
for (let i = 0; i < lines.length; i++) {
35-
const line = lines[i];
36-
result.push(h('span', { key: i }, line));
37-
if (i < lines.length - 1) {
38-
result.push(h('br'));
39-
}
34+
for (const [i, line] of lines.entries()) {
35+
result.push(h('p', { key: i }, line));
36+
// if (i < lines.length - 1) {
37+
// result.push(h('br'));
38+
// }
4039
}
4140
return result;
4241
} else {

0 commit comments

Comments
 (0)