提交 | 用户 | age
|
84c9d7
|
1 |
import type { ComputedRef, Ref } from 'vue'; |
0bb76a
|
2 |
import type { FormProps, FormSchemaInner as FormSchema, FormActionType } from '../types/form'; |
2407b3
|
3 |
import type { NamePath } from 'ant-design-vue/lib/form/interface'; |
6b594a
|
4 |
import { unref, toRaw, nextTick } from 'vue'; |
88e77d
|
5 |
import { isArray, isFunction, isObject, isString, isNil } from '@/utils/is'; |
bab28a
|
6 |
import { deepMerge } from '@/utils'; |
cca7f5
|
7 |
import { |
E |
8 |
dateItemType, |
|
9 |
defaultValueComponents, |
|
10 |
isIncludeSimpleComponents, |
|
11 |
uploadItemType, |
|
12 |
} from '../helper'; |
bab28a
|
13 |
import { dateUtil } from '@/utils/dateUtil'; |
7538c5
|
14 |
import { cloneDeep, has, uniqBy, get, set } from 'lodash-es'; |
bab28a
|
15 |
import { error } from '@/utils/log'; |
cca7f5
|
16 |
import { ComponentProps } from '../types'; |
84c9d7
|
17 |
|
V |
18 |
interface UseFormActionContext { |
|
19 |
emit: EmitType; |
|
20 |
getProps: ComputedRef<FormProps>; |
|
21 |
getSchema: ComputedRef<FormSchema[]>; |
4ff1c4
|
22 |
formModel: Recordable; |
V |
23 |
defaultValueRef: Ref<Recordable>; |
2407b3
|
24 |
formElRef: Ref<FormActionType>; |
84c9d7
|
25 |
schemaRef: Ref<FormSchema[]>; |
V |
26 |
handleFormValues: Fn; |
1b3083
|
27 |
} |
baf406
|
28 |
/** |
C |
29 |
* @description: Is it upload |
cca7f5
|
30 |
*/ |
baf406
|
31 |
export function itemIsUploadComponent(key: keyof ComponentProps) { |
C |
32 |
return uploadItemType.includes(key); |
|
33 |
} |
7538c5
|
34 |
function tryConstructArray(field: string, values: Recordable = {}): any[] | undefined { |
E |
35 |
const pattern = /^\[(.+)\]$/; |
|
36 |
if (pattern.test(field)) { |
|
37 |
const match = field.match(pattern); |
|
38 |
if (match && match[1]) { |
|
39 |
const keys = match[1].split(','); |
|
40 |
if (!keys.length) { |
|
41 |
return undefined; |
|
42 |
} |
|
43 |
const result = []; |
|
44 |
keys.forEach((k, index) => { |
|
45 |
set(result, index, values[k.trim()]); |
|
46 |
}); |
|
47 |
return result.filter(Boolean).length ? result : undefined; |
|
48 |
} |
|
49 |
} |
|
50 |
} |
4ff1c4
|
51 |
export function useFormEvents({ |
84c9d7
|
52 |
emit, |
V |
53 |
getProps, |
|
54 |
formModel, |
|
55 |
getSchema, |
|
56 |
defaultValueRef, |
|
57 |
formElRef, |
|
58 |
schemaRef, |
|
59 |
handleFormValues, |
|
60 |
}: UseFormActionContext) { |
4ff1c4
|
61 |
async function resetFields(): Promise<void> { |
84c9d7
|
62 |
const { resetFunc, submitOnReset } = unref(getProps); |
V |
63 |
resetFunc && isFunction(resetFunc) && (await resetFunc()); |
4ff1c4
|
64 |
|
84c9d7
|
65 |
const formEl = unref(formElRef); |
V |
66 |
if (!formEl) return; |
4ff1c4
|
67 |
|
84c9d7
|
68 |
Object.keys(formModel).forEach((key) => { |
2423ae
|
69 |
const schema = unref(getSchema).find((item) => item.field === key); |
a065de
|
70 |
const defaultValueObj = schema?.defaultValueObj; |
L |
71 |
const fieldKeys = Object.keys(defaultValueObj || {}); |
|
72 |
if (fieldKeys.length) { |
05030e
|
73 |
fieldKeys.forEach((field) => { |
a065de
|
74 |
formModel[field] = defaultValueObj![field]; |
L |
75 |
}); |
|
76 |
} |
5425dc
|
77 |
formModel[key] = getDefaultValue(schema, defaultValueRef, key); |
84c9d7
|
78 |
}); |
6b594a
|
79 |
nextTick(() => clearValidate()); |
L |
80 |
|
84c9d7
|
81 |
emit('reset', toRaw(formModel)); |
V |
82 |
submitOnReset && handleSubmit(); |
|
83 |
} |
a065de
|
84 |
// 获取表单fields |
L |
85 |
const getAllFields = () => |
|
86 |
unref(getSchema) |
|
87 |
.map((item) => [...(item.fields || []), item.field]) |
|
88 |
.flat(1) |
|
89 |
.filter(Boolean); |
84c9d7
|
90 |
/** |
4ff1c4
|
91 |
* @description: Set form value |
84c9d7
|
92 |
*/ |
ac1a36
|
93 |
async function setFieldsValue(values: Recordable): Promise<void> { |
8f9008
|
94 |
if (Object.keys(values).length === 0) { |
IW |
95 |
return; |
|
96 |
} |
|
97 |
|
a065de
|
98 |
const fields = getAllFields(); |
1db72c
|
99 |
|
V |
100 |
const validKeys: string[] = []; |
1b3083
|
101 |
fields.forEach((key) => { |
ac1a36
|
102 |
const schema = unref(getSchema).find((item) => item.field === key); |
ef5285
|
103 |
const value = get(values, key); |
f5cd3a
|
104 |
const hasKey = has(values, key); |
4c67d8
|
105 |
const { componentProps } = schema || {}; |
V |
106 |
let _props = componentProps as any; |
|
107 |
if (typeof componentProps === 'function') { |
f62043
|
108 |
_props = _props({ |
Z |
109 |
formModel: unref(formModel), |
|
110 |
formActionType, |
|
111 |
}); |
4c67d8
|
112 |
} |
1b3083
|
113 |
|
22052f
|
114 |
let constructValue; |
09f795
|
115 |
const setDateFieldValue = (v) => { |
NN |
116 |
return v ? (_props?.valueFormat ? v : dateUtil(v)) : null; |
|
117 |
}; |
1b3083
|
118 |
|
22052f
|
119 |
// Adapt date component |
Z |
120 |
if (itemIsDateComponent(schema?.component)) { |
|
121 |
constructValue = tryConstructArray(key, values); |
|
122 |
if (constructValue) { |
7538c5
|
123 |
const fieldValue = constructValue || value; |
1b3083
|
124 |
if (Array.isArray(fieldValue)) { |
e689ee
|
125 |
const arr: any[] = []; |
1b3083
|
126 |
for (const ele of fieldValue) { |
09f795
|
127 |
arr.push(setDateFieldValue(ele)); |
84c9d7
|
128 |
} |
4c67d8
|
129 |
unref(formModel)[key] = arr; |
7538c5
|
130 |
validKeys.push(key); |
84c9d7
|
131 |
} else { |
09f795
|
132 |
unref(formModel)[key] = setDateFieldValue(fieldValue); |
7538c5
|
133 |
validKeys.push(key); |
84c9d7
|
134 |
} |
4c67d8
|
135 |
} |
7538c5
|
136 |
} |
cca7f5
|
137 |
// Adapt upload component |
E |
138 |
if (itemIsUploadComponent(schema?.component)) { |
|
139 |
constructValue = get(value, key); |
|
140 |
const fieldValue = constructValue || value; |
|
141 |
if (fieldValue) { |
|
142 |
if (isArray(fieldValue)) { |
|
143 |
unref(formModel)[key] = fieldValue; |
|
144 |
} else if (typeof fieldValue == 'string') { |
|
145 |
unref(formModel)[key] = [fieldValue]; |
|
146 |
} |
|
147 |
} |
|
148 |
validKeys.push(key); |
baf406
|
149 |
return; |
cca7f5
|
150 |
} |
22052f
|
151 |
// Adapt common component |
7538c5
|
152 |
if (hasKey) { |
E |
153 |
constructValue = get(value, key); |
|
154 |
const fieldValue = constructValue || value; |
|
155 |
unref(formModel)[key] = fieldValue; |
4c67d8
|
156 |
if (_props?.onChange) { |
1b3083
|
157 |
_props?.onChange(fieldValue); |
84c9d7
|
158 |
} |
1db72c
|
159 |
validKeys.push(key); |
d09e99
|
160 |
} else { |
22052f
|
161 |
// key not exist |
88e77d
|
162 |
// refer:https://github.com/vbenjs/vue-vben-admin/issues/3795 |
84c9d7
|
163 |
} |
V |
164 |
}); |
8d185b
|
165 |
validateFields(validKeys).catch((_) => {}); |
84c9d7
|
166 |
} |
7b21e9
|
167 |
|
88e77d
|
168 |
/** |
E |
169 |
* @description: Set form default value |
|
170 |
*/ |
22052f
|
171 |
function resetDefaultField(nameList?: NamePath[]) { |
Z |
172 |
if (!Array.isArray(nameList)) { |
|
173 |
return; |
88e77d
|
174 |
} |
E |
175 |
if (Array.isArray(nameList) && nameList.length === 0) { |
|
176 |
return; |
|
177 |
} |
|
178 |
const validKeys: string[] = []; |
22052f
|
179 |
const keys = Object.keys(unref(formModel)); |
Z |
180 |
if (!keys) { |
|
181 |
return; |
88e77d
|
182 |
} |
22052f
|
183 |
nameList.forEach((key: any) => { |
Z |
184 |
if (keys.includes(key)) { |
88e77d
|
185 |
validKeys.push(key); |
E |
186 |
unref(formModel)[key] = cloneDeep(unref(get(defaultValueRef.value, key))); |
|
187 |
} |
|
188 |
}); |
|
189 |
validateFields(validKeys).catch((_) => {}); |
|
190 |
} |
22052f
|
191 |
|
84c9d7
|
192 |
/** |
46e087
|
193 |
* @description: Delete based on field name |
84c9d7
|
194 |
*/ |
768fad
|
195 |
async function removeSchemaByField(fields: string | string[]): Promise<void> { |
84c9d7
|
196 |
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); |
38f507
|
197 |
if (!fields) { |
V |
198 |
return; |
|
199 |
} |
4ff1c4
|
200 |
|
22052f
|
201 |
let fieldList = (isString(fields) ? [fields] : fields) as string[]; |
84c9d7
|
202 |
if (isString(fields)) { |
22052f
|
203 |
fieldList = [fields as string]; |
84c9d7
|
204 |
} |
V |
205 |
for (const field of fieldList) { |
9882e8
|
206 |
_removeSchemaByField(field, schemaList); |
84c9d7
|
207 |
} |
4ff1c4
|
208 |
schemaRef.value = schemaList; |
84c9d7
|
209 |
} |
V |
210 |
|
|
211 |
/** |
46e087
|
212 |
* @description: Delete based on field name |
84c9d7
|
213 |
*/ |
9882e8
|
214 |
function _removeSchemaByField(field: string, schemaList: FormSchema[]): void { |
84c9d7
|
215 |
if (isString(field)) { |
V |
216 |
const index = schemaList.findIndex((schema) => schema.field === field); |
|
217 |
if (index !== -1) { |
38f507
|
218 |
delete formModel[field]; |
84c9d7
|
219 |
schemaList.splice(index, 1); |
V |
220 |
} |
|
221 |
} |
|
222 |
} |
|
223 |
|
|
224 |
/** |
46e087
|
225 |
* @description: Insert after a certain field, if not insert the last |
84c9d7
|
226 |
*/ |
098621
|
227 |
async function appendSchemaByField( |
V |
228 |
schema: FormSchema | FormSchema[], |
|
229 |
prefixField?: string, |
|
230 |
first = false, |
|
231 |
) { |
84c9d7
|
232 |
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); |
3b0b8d
|
233 |
const addSchemaIds: string[] = Array.isArray(schema) |
Z |
234 |
? schema.map((item) => item.field) |
|
235 |
: [schema.field]; |
|
236 |
if (schemaList.find((item) => addSchemaIds.includes(item.field))) { |
|
237 |
error('There are schemas that have already been added'); |
|
238 |
return; |
|
239 |
} |
84c9d7
|
240 |
const index = schemaList.findIndex((schema) => schema.field === prefixField); |
098621
|
241 |
const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]); |
4ff1c4
|
242 |
if (!prefixField || index === -1 || first) { |
098621
|
243 |
first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList); |
7b26c5
|
244 |
} else if (index !== -1) { |
098621
|
245 |
schemaList.splice(index + 1, 0, ..._schemaList); |
84c9d7
|
246 |
} |
4ff1c4
|
247 |
schemaRef.value = schemaList; |
7b26c5
|
248 |
_setDefaultValue(schema); |
84c9d7
|
249 |
} |
V |
250 |
|
c639e4
|
251 |
async function resetSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { |
Z |
252 |
let updateData: Partial<FormSchema>[] = []; |
|
253 |
if (isObject(data)) { |
|
254 |
updateData.push(data as FormSchema); |
|
255 |
} |
|
256 |
if (isArray(data)) { |
|
257 |
updateData = [...data]; |
|
258 |
} |
|
259 |
|
47a448
|
260 |
const hasField = updateData.every( |
cd71e6
|
261 |
(item) => |
林 |
262 |
isIncludeSimpleComponents(item.component) || (Reflect.has(item, 'field') && item.field), |
47a448
|
263 |
); |
c639e4
|
264 |
|
Z |
265 |
if (!hasField) { |
|
266 |
error( |
56a966
|
267 |
'All children of the form Schema array that need to be updated must contain the `field` field', |
c639e4
|
268 |
); |
Z |
269 |
return; |
|
270 |
} |
|
271 |
schemaRef.value = updateData as FormSchema[]; |
|
272 |
} |
|
273 |
|
4ff1c4
|
274 |
async function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { |
84c9d7
|
275 |
let updateData: Partial<FormSchema>[] = []; |
V |
276 |
if (isObject(data)) { |
|
277 |
updateData.push(data as FormSchema); |
|
278 |
} |
|
279 |
if (isArray(data)) { |
|
280 |
updateData = [...data]; |
|
281 |
} |
4ff1c4
|
282 |
|
47a448
|
283 |
const hasField = updateData.every( |
cd71e6
|
284 |
(item) => |
林 |
285 |
isIncludeSimpleComponents(item.component) || (Reflect.has(item, 'field') && item.field), |
47a448
|
286 |
); |
4ff1c4
|
287 |
|
84c9d7
|
288 |
if (!hasField) { |
4ff1c4
|
289 |
error( |
56a966
|
290 |
'All children of the form Schema array that need to be updated must contain the `field` field', |
4ff1c4
|
291 |
); |
116a1f
|
292 |
return; |
84c9d7
|
293 |
} |
V |
294 |
const schema: FormSchema[] = []; |
3de22b
|
295 |
const updatedSchema: FormSchema[] = []; |
165743
|
296 |
unref(getSchema).forEach((val) => { |
3de22b
|
297 |
const updatedItem = updateData.find((item) => val.field === item.field); |
Z |
298 |
|
|
299 |
if (updatedItem) { |
|
300 |
const newSchema = deepMerge(val, updatedItem); |
|
301 |
updatedSchema.push(newSchema as FormSchema); |
165743
|
302 |
schema.push(newSchema as FormSchema); |
CZ |
303 |
} else { |
|
304 |
schema.push(val); |
|
305 |
} |
84c9d7
|
306 |
}); |
3de22b
|
307 |
_setDefaultValue(updatedSchema); |
aaa30f
|
308 |
|
be3a3e
|
309 |
schemaRef.value = uniqBy(schema, 'field'); |
84c9d7
|
310 |
} |
V |
311 |
|
aaa30f
|
312 |
function _setDefaultValue(data: FormSchema | FormSchema[]) { |
L |
313 |
let schemas: FormSchema[] = []; |
|
314 |
if (isObject(data)) { |
|
315 |
schemas.push(data as FormSchema); |
|
316 |
} |
|
317 |
if (isArray(data)) { |
|
318 |
schemas = [...data]; |
|
319 |
} |
|
320 |
|
|
321 |
const obj: Recordable = {}; |
3cc72d
|
322 |
const currentFieldsValue = getFieldsValue(); |
aaa30f
|
323 |
schemas.forEach((item) => { |
L |
324 |
if ( |
cd71e6
|
325 |
!isIncludeSimpleComponents(item.component) && |
aaa30f
|
326 |
Reflect.has(item, 'field') && |
L |
327 |
item.field && |
f91d77
|
328 |
!isNil(item.defaultValue) && |
72ef3d
|
329 |
(!(item.field in currentFieldsValue) || isNil(currentFieldsValue[item.field])) |
aaa30f
|
330 |
) { |
L |
331 |
obj[item.field] = item.defaultValue; |
|
332 |
} |
|
333 |
}); |
|
334 |
setFieldsValue(obj); |
|
335 |
} |
|
336 |
|
4ff1c4
|
337 |
function getFieldsValue(): Recordable { |
84c9d7
|
338 |
const formEl = unref(formElRef); |
4ff1c4
|
339 |
if (!formEl) return {}; |
84c9d7
|
340 |
return handleFormValues(toRaw(unref(formModel))); |
V |
341 |
} |
|
342 |
|
|
343 |
/** |
46e087
|
344 |
* @description: Is it time |
84c9d7
|
345 |
*/ |
7538c5
|
346 |
function itemIsDateComponent(key: string) { |
E |
347 |
return dateItemType.includes(key); |
84c9d7
|
348 |
} |
V |
349 |
|
4ff1c4
|
350 |
async function validateFields(nameList?: NamePath[] | undefined) { |
71c3fe
|
351 |
const values = await unref(formElRef)?.validateFields(nameList); |
a3b9ff
|
352 |
return handleFormValues(values); |
84c9d7
|
353 |
} |
43a45b
|
354 |
|
f62043
|
355 |
async function setProps(formProps: Partial<FormProps>): Promise<void> { |
Z |
356 |
await unref(formElRef)?.setProps(formProps); |
|
357 |
} |
|
358 |
|
a065de
|
359 |
async function validate(nameList?: NamePath[] | false | undefined) { |
L |
360 |
let _nameList: any; |
|
361 |
if (nameList === undefined) { |
|
362 |
_nameList = getAllFields(); |
|
363 |
} else { |
|
364 |
_nameList = nameList === Array.isArray(nameList) ? nameList : undefined; |
|
365 |
} |
a3b9ff
|
366 |
const values = await unref(formElRef)?.validate(_nameList); |
1 |
367 |
return handleFormValues(values); |
84c9d7
|
368 |
} |
V |
369 |
|
4ff1c4
|
370 |
async function clearValidate(name?: string | string[]) { |
V |
371 |
await unref(formElRef)?.clearValidate(name); |
|
372 |
} |
|
373 |
|
|
374 |
async function scrollToField(name: NamePath, options?: ScrollOptions | undefined) { |
|
375 |
await unref(formElRef)?.scrollToField(name, options); |
84c9d7
|
376 |
} |
V |
377 |
|
|
378 |
/** |
46e087
|
379 |
* @description: Form submission |
84c9d7
|
380 |
*/ |
V |
381 |
async function handleSubmit(e?: Event): Promise<void> { |
|
382 |
e && e.preventDefault(); |
|
383 |
const { submitFunc } = unref(getProps); |
|
384 |
if (submitFunc && isFunction(submitFunc)) { |
|
385 |
await submitFunc(); |
|
386 |
return; |
|
387 |
} |
|
388 |
const formEl = unref(formElRef); |
|
389 |
if (!formEl) return; |
|
390 |
try { |
94bf85
|
391 |
const values = await validate(); |
a3b9ff
|
392 |
emit('submit', values); |
a248e2
|
393 |
} catch (error: any) { |
72dbe5
|
394 |
if (error?.outOfDate === false && error?.errorFields) { |
L |
395 |
return; |
|
396 |
} |
9a21b8
|
397 |
throw new Error(error); |
LY |
398 |
} |
84c9d7
|
399 |
} |
V |
400 |
|
f62043
|
401 |
const formActionType: Partial<FormActionType> = { |
Z |
402 |
getFieldsValue, |
|
403 |
setFieldsValue, |
|
404 |
resetFields, |
|
405 |
updateSchema, |
|
406 |
resetSchema, |
|
407 |
setProps, |
|
408 |
removeSchemaByField, |
|
409 |
appendSchemaByField, |
|
410 |
clearValidate, |
|
411 |
validateFields, |
|
412 |
validate, |
|
413 |
submit: handleSubmit, |
|
414 |
scrollToField: scrollToField, |
|
415 |
}; |
|
416 |
|
84c9d7
|
417 |
return { |
V |
418 |
handleSubmit, |
|
419 |
clearValidate, |
|
420 |
validate, |
|
421 |
validateFields, |
|
422 |
getFieldsValue, |
|
423 |
updateSchema, |
c639e4
|
424 |
resetSchema, |
84c9d7
|
425 |
appendSchemaByField, |
768fad
|
426 |
removeSchemaByField, |
84c9d7
|
427 |
resetFields, |
V |
428 |
setFieldsValue, |
4ff1c4
|
429 |
scrollToField, |
22052f
|
430 |
resetDefaultField, |
84c9d7
|
431 |
}; |
7b21e9
|
432 |
} |
5425dc
|
433 |
|
IW |
434 |
function getDefaultValue( |
|
435 |
schema: FormSchema | undefined, |
|
436 |
defaultValueRef: UseFormActionContext['defaultValueRef'], |
|
437 |
key: string, |
|
438 |
) { |
|
439 |
let defaultValue = cloneDeep(defaultValueRef.value[key]); |
|
440 |
const isInput = checkIsInput(schema); |
|
441 |
if (isInput) { |
22052f
|
442 |
return !isNil(defaultValue) ? defaultValue : undefined; |
5425dc
|
443 |
} |
IW |
444 |
if (!defaultValue && schema && checkIsRangeSlider(schema)) { |
|
445 |
defaultValue = [0, 0]; |
|
446 |
} |
a0d4b1
|
447 |
if (!defaultValue && schema && schema.component === 'ApiTree') { |
IW |
448 |
defaultValue = []; |
|
449 |
} |
5425dc
|
450 |
return defaultValue; |
IW |
451 |
} |
|
452 |
|
|
453 |
function checkIsRangeSlider(schema: FormSchema) { |
e25af8
|
454 |
if (schema.component === 'Slider' && schema.componentProps && 'range' in schema.componentProps) { |
5425dc
|
455 |
return true; |
IW |
456 |
} |
|
457 |
} |
|
458 |
|
|
459 |
function checkIsInput(schema?: FormSchema) { |
|
460 |
return schema?.component && defaultValueComponents.includes(schema.component); |
|
461 |
} |