clddup
2024-07-19 baf406e7e271ac90faa3aec31ceb44715331d9d0
提交 | 用户 | 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 }