feat: add useAlertContext
for Alert component (#5947)
* 新增Alert的子组件中获取弹窗上下文的能力
This commit is contained in:
parent
9700150653
commit
bf7496f0d5
@ -12,6 +12,12 @@ Alert提供的功能与Modal类似,但只适用于简单应用场景。例如
|
||||
|
||||
:::
|
||||
|
||||
::: tip 注意
|
||||
|
||||
Alert提供的快捷方法alert、confirm、prompt动态创建的弹窗在已打开的情况下,不支持HMR(热更新),代码变更后需要关闭这些弹窗后重新打开。
|
||||
|
||||
:::
|
||||
|
||||
::: tip README
|
||||
|
||||
下方示例代码中的,存在一些主题色未适配、样式缺失的问题,这些问题只在文档内会出现,实际使用并不会有这些问题,可忽略,不必纠结。
|
||||
@ -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
|
||||
|
@ -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';
|
||||
@ -20,16 +20,30 @@ function showPrompt() {
|
||||
|
||||
function showSlotsPrompt() {
|
||||
prompt({
|
||||
component: Input,
|
||||
componentProps: {
|
||||
component: () => {
|
||||
// 获取弹窗上下文。注意:只能在setup或者函数式组件中调用
|
||||
const { doConfirm } = useAlertContext();
|
||||
return h(
|
||||
Input,
|
||||
{
|
||||
onKeydown(e: KeyboardEvent) {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
// 调用弹窗提供的确认方法
|
||||
doConfirm();
|
||||
}
|
||||
},
|
||||
placeholder: '请输入',
|
||||
prefix: '充值金额',
|
||||
prefix: '充值金额:',
|
||||
type: 'number',
|
||||
},
|
||||
componentSlots: {
|
||||
{
|
||||
addonAfter: () => h(BadgeJapaneseYen),
|
||||
},
|
||||
content: '此弹窗演示了如何使用componentSlots传递自定义插槽',
|
||||
);
|
||||
},
|
||||
content:
|
||||
'此弹窗演示了如何使用自定义插槽,并且可以使用useAlertContext获取到弹窗的上下文。\n在输入框中按下回车键会触发确认操作。',
|
||||
icon: 'question',
|
||||
modelPropName: 'value',
|
||||
}).then((val) => {
|
||||
|
@ -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';
|
||||
@ -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 };
|
||||
|
@ -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 = {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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',
|
||||
@ -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');
|
||||
@ -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" />
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user