Browse Source
feat: add `useAlertContext` for Alert component (#5947)
* 新增Alert的子组件中获取弹窗上下文的能力
pull/5948/head
Netfan
10 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with
104 additions and
25 deletions
-
docs/src/components/common-ui/vben-alert.md
-
docs/src/demos/vben-alert/prompt/index.vue
-
packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts
-
packages/@core/ui-kit/popup-ui/src/alert/alert.ts
-
packages/@core/ui-kit/popup-ui/src/alert/alert.vue
-
packages/@core/ui-kit/popup-ui/src/alert/index.ts
-
packages/@core/ui-kit/shadcn-ui/src/components/render-content/render-content.vue
|
|
|
@ -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: { |
|
|
|
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) => { |
|
|
|
|
|
|
|
@ -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 { |
|
|
|
|