1
1
import type { Recordable } from '@vben-core/typings' ;
2
- import type { Component } from 'vue' ;
2
+ import type { Component , VNode } from 'vue' ;
3
3
4
- import type { AlertProps , BeforeCloseScope } from './alert' ;
4
+ import type { AlertProps , BeforeCloseScope , PromptProps } from './alert' ;
5
5
6
6
import { useSimpleLocale } from '@vben-core/composables' ;
7
7
import { Input } from '@vben-core/shadcn-ui' ;
8
8
import { isFunction , isString } from '@vben-core/shared/utils' ;
9
- import { h , ref , render } from 'vue' ;
9
+ import { h , nextTick , ref , render } from 'vue' ;
10
10
11
11
import Alert from './alert.vue' ;
12
12
@@ -128,40 +128,58 @@ export function vbenConfirm(
128
128
}
129
129
130
130
export async function vbenPrompt < T = any > (
131
- options : {
132
- beforeClose ?: ( scope : {
133
- isConfirm : boolean ;
134
- value : T | undefined ;
135
- } ) => boolean | Promise < boolean | undefined > | undefined ;
136
- component ?: Component ;
137
- componentProps ?: Recordable < any > ;
138
- defaultValue ?: T ;
139
- modelPropName ?: string ;
140
- } & Omit < AlertProps , 'beforeClose' > ,
131
+ options : PromptProps < T > ,
141
132
) : Promise < T | undefined > {
142
133
const {
143
134
component : _component ,
144
135
componentProps : _componentProps ,
136
+ componentSlots,
145
137
content,
146
138
defaultValue,
147
139
modelPropName : _modelPropName ,
148
140
...delegated
149
141
} = options ;
150
- const contents : Component [ ] = [ ] ;
142
+
151
143
const modelValue = ref < T | undefined > ( defaultValue ) ;
144
+ const inputComponentRef = ref < null | VNode > ( null ) ;
145
+ const staticContents : Component [ ] = [ ] ;
146
+
152
147
if ( isString ( content ) ) {
153
- contents . push ( h ( 'span' , content ) ) ;
154
- } else {
155
- contents . push ( content ) ;
148
+ staticContents . push ( h ( 'span' , content ) ) ;
149
+ } else if ( content ) {
150
+ staticContents . push ( content as Component ) ;
156
151
}
157
- const componentProps = _componentProps || { } ;
152
+
158
153
const modelPropName = _modelPropName || 'modelValue' ;
159
- componentProps [ modelPropName ] = modelValue . value ;
160
- componentProps [ `onUpdate:${ modelPropName } ` ] = ( val : any ) => {
161
- modelValue . value = val ;
154
+ const componentProps = { ..._componentProps } ;
155
+
156
+ // 每次渲染时都会重新计算的内容函数
157
+ const contentRenderer = ( ) => {
158
+ const currentProps = { ...componentProps } ;
159
+
160
+ // 设置当前值
161
+ currentProps [ modelPropName ] = modelValue . value ;
162
+
163
+ // 设置更新处理函数
164
+ currentProps [ `onUpdate:${ modelPropName } ` ] = ( val : T ) => {
165
+ modelValue . value = val ;
166
+ } ;
167
+
168
+ // 创建输入组件
169
+ inputComponentRef . value = h (
170
+ _component || Input ,
171
+ currentProps ,
172
+ componentSlots ,
173
+ ) ;
174
+
175
+ // 返回包含静态内容和输入组件的数组
176
+ return h (
177
+ 'div' ,
178
+ { class : 'flex flex-col gap-2' } ,
179
+ { default : ( ) => [ ...staticContents , inputComponentRef . value ] } ,
180
+ ) ;
162
181
} ;
163
- const componentRef = h ( _component || Input , componentProps ) ;
164
- contents . push ( componentRef ) ;
182
+
165
183
const props : AlertProps & Recordable < any > = {
166
184
...delegated ,
167
185
async beforeClose ( scope : BeforeCloseScope ) {
@@ -172,23 +190,46 @@ export async function vbenPrompt<T = any>(
172
190
} ) ;
173
191
}
174
192
} ,
175
- content : h (
176
- 'div' ,
177
- { class : 'flex flex-col gap-2' } ,
178
- { default : ( ) => contents } ,
179
- ) ,
180
- onOpened ( ) {
181
- // 组件挂载完成后,自动聚焦到输入组件
182
- if (
183
- componentRef . component ?. exposed &&
184
- isFunction ( componentRef . component . exposed . focus )
185
- ) {
186
- componentRef . component . exposed . focus ( ) ;
187
- } else if ( componentRef . el && isFunction ( componentRef . el . focus ) ) {
188
- componentRef . el . focus ( ) ;
193
+ // 使用函数形式,每次渲染都会重新计算内容
194
+ content : contentRenderer ,
195
+ contentMasking : true ,
196
+ async onOpened ( ) {
197
+ await nextTick ( ) ;
198
+ const componentRef : null | VNode = inputComponentRef . value ;
199
+ if ( componentRef ) {
200
+ if (
201
+ componentRef . component ?. exposed &&
202
+ isFunction ( componentRef . component . exposed . focus )
203
+ ) {
204
+ componentRef . component . exposed . focus ( ) ;
205
+ } else {
206
+ if ( componentRef . el ) {
207
+ if (
208
+ isFunction ( componentRef . el . focus ) &&
209
+ [ 'BUTTON' , 'INPUT' , 'SELECT' , 'TEXTAREA' ] . includes (
210
+ componentRef . el . tagName ,
211
+ )
212
+ ) {
213
+ componentRef . el . focus ( ) ;
214
+ } else if ( isFunction ( componentRef . el . querySelector ) ) {
215
+ const focusableElement = componentRef . el . querySelector (
216
+ 'input, select, textarea, button' ,
217
+ ) ;
218
+ if ( focusableElement && isFunction ( focusableElement . focus ) ) {
219
+ focusableElement . focus ( ) ;
220
+ }
221
+ } else if (
222
+ componentRef . el . nextElementSibling &&
223
+ isFunction ( componentRef . el . nextElementSibling . focus )
224
+ ) {
225
+ componentRef . el . nextElementSibling . focus ( ) ;
226
+ }
227
+ }
228
+ }
189
229
}
190
230
} ,
191
231
} ;
232
+
192
233
await vbenConfirm ( props ) ;
193
234
return modelValue . value ;
194
235
}
0 commit comments