oooplz
2023-03-29 1b30834eb36b7e1730b01ce6af602444937d00ef
提交 | 用户 | age
84c9d7 1 import type { ComputedRef, Ref } from 'vue';
2407b3 2 import type { FormProps, FormSchema, FormActionType } from '../types/form';
V 3 import type { NamePath } from 'ant-design-vue/lib/form/interface';
6b594a 4 import { unref, toRaw, nextTick } from 'vue';
0bb3b7 5 import {
V 6   isArray,
7   isFunction,
8   isObject,
9   isString,
10   isDef,
11   isNullOrUnDef,
12   isEmpty,
13 } from '/@/utils/is';
be3a3e 14 import { deepMerge } from '/@/utils';
2423ae 15 import { dateItemType, handleInputNumberValue, defaultValueComponents } from '../helper';
3509eb 16 import { dateUtil } from '/@/utils/dateUtil';
1b3083 17 import { cloneDeep, set, uniqBy } from 'lodash-es';
4ff1c4 18 import { error } from '/@/utils/log';
84c9d7 19
V 20 interface UseFormActionContext {
21   emit: EmitType;
22   getProps: ComputedRef<FormProps>;
23   getSchema: ComputedRef<FormSchema[]>;
4ff1c4 24   formModel: Recordable;
V 25   defaultValueRef: Ref<Recordable>;
2407b3 26   formElRef: Ref<FormActionType>;
84c9d7 27   schemaRef: Ref<FormSchema[]>;
V 28   handleFormValues: Fn;
29 }
1b3083 30
O 31 function tryConstructArray(field: string, values: Recordable = {}): any[] | undefined {
32   const pattern = /^\[(.+)\]$/;
33   if (pattern.test(field)) {
34     const match = field.match(pattern);
35     if (match && match[1]) {
36       const keys = match[1].split(',');
37       if (!keys.length) {
38         return undefined;
39       }
40
41       const result = [];
42       keys.forEach((k, index) => {
43         set(result, index, values[k.trim()]);
44       });
45
46       return result.length ? result : undefined;
47     }
48   }
49 }
50
51 function tryConstructObject(field: string, values: Recordable = {}): Recordable | undefined {
52   const pattern = /^\{(.+)\}$/;
53   if (pattern.test(field)) {
54     const match = field.match(pattern);
55     if (match && match[1]) {
56       const keys = match[1].split(',');
57       if (!keys.length) {
58         return;
59       }
60
61       const result = {};
62       keys.forEach((k) => {
63         set(result, k.trim(), values[k.trim()]);
64       });
65
66       return Object.values(result).filter(Boolean).length ? result : undefined;
67     }
68   }
69 }
70
4ff1c4 71 export function useFormEvents({
84c9d7 72   emit,
V 73   getProps,
74   formModel,
75   getSchema,
76   defaultValueRef,
77   formElRef,
78   schemaRef,
79   handleFormValues,
80 }: UseFormActionContext) {
4ff1c4 81   async function resetFields(): Promise<void> {
84c9d7 82     const { resetFunc, submitOnReset } = unref(getProps);
V 83     resetFunc && isFunction(resetFunc) && (await resetFunc());
4ff1c4 84
84c9d7 85     const formEl = unref(formElRef);
V 86     if (!formEl) return;
4ff1c4 87
84c9d7 88     Object.keys(formModel).forEach((key) => {
2423ae 89       const schema = unref(getSchema).find((item) => item.field === key);
W 90       const isInput = schema?.component && defaultValueComponents.includes(schema.component);
d21578 91       const defaultValue = cloneDeep(defaultValueRef.value[key]);
A 92       formModel[key] = isInput ? defaultValue || '' : defaultValue;
84c9d7 93     });
6b594a 94     nextTick(() => clearValidate());
L 95
84c9d7 96     emit('reset', toRaw(formModel));
V 97     submitOnReset && handleSubmit();
98   }
99
100   /**
4ff1c4 101    * @description: Set form value
84c9d7 102    */
ac1a36 103   async function setFieldsValue(values: Recordable): Promise<void> {
84c9d7 104     const fields = unref(getSchema)
V 105       .map((item) => item.field)
106       .filter(Boolean);
1db72c 107
d09e99 108     // key 支持 a.b.c 的嵌套写法
J 109     const delimiter = '.';
edede2 110     const nestKeyArray = fields.filter((item) => String(item).indexOf(delimiter) >= 0);
d09e99 111
1db72c 112     const validKeys: string[] = [];
1b3083 113     fields.forEach((key) => {
ac1a36 114       const schema = unref(getSchema).find((item) => item.field === key);
V 115       let value = values[key];
116
8f76ef 117       const hasKey = Reflect.has(values, key);
V 118
ac1a36 119       value = handleInputNumberValue(schema?.component, value);
4c67d8 120       const { componentProps } = schema || {};
V 121       let _props = componentProps as any;
122       if (typeof componentProps === 'function') {
123         _props = _props({ formModel: unref(formModel) });
124       }
1b3083 125
O 126       const constructValue = tryConstructArray(key, values) || tryConstructObject(key, values);
127
4ff1c4 128       // 0| '' is allow
1b3083 129       if (hasKey || !!constructValue) {
O 130         const fieldValue = constructValue || value;
46e087 131         // time type
84c9d7 132         if (itemIsDateType(key)) {
1b3083 133           if (Array.isArray(fieldValue)) {
e689ee 134             const arr: any[] = [];
1b3083 135             for (const ele of fieldValue) {
e689ee 136               arr.push(ele ? dateUtil(ele) : null);
84c9d7 137             }
4c67d8 138             unref(formModel)[key] = arr;
84c9d7 139           } else {
1b3083 140             unref(formModel)[key] = fieldValue ? (_props?.valueFormat ? fieldValue : dateUtil(fieldValue)) : null;
84c9d7 141           }
V 142         } else {
1b3083 143           unref(formModel)[key] = fieldValue;
4c67d8 144         }
V 145         if (_props?.onChange) {
1b3083 146           _props?.onChange(fieldValue);
84c9d7 147         }
1db72c 148         validKeys.push(key);
d09e99 149       } else {
J 150         nestKeyArray.forEach((nestKey: string) => {
151           try {
87ee7c 152             const value = nestKey.split('.').reduce((out, item) => out[item], values);
d09e99 153             if (isDef(value)) {
4c67d8 154               unref(formModel)[nestKey] = unref(value);
d09e99 155               validKeys.push(nestKey);
J 156             }
157           } catch (e) {
158             // key not exist
159             if (isDef(defaultValueRef.value[nestKey])) {
4c67d8 160               unref(formModel)[nestKey] = cloneDeep(unref(defaultValueRef.value[nestKey]));
d09e99 161             }
J 162           }
163         });
84c9d7 164       }
V 165     });
8d185b 166     validateFields(validKeys).catch((_) => {});
84c9d7 167   }
V 168   /**
46e087 169    * @description: Delete based on field name
84c9d7 170    */
768fad 171   async function removeSchemaByField(fields: string | string[]): Promise<void> {
84c9d7 172     const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
38f507 173     if (!fields) {
V 174       return;
175     }
4ff1c4 176
V 177     let fieldList: string[] = isString(fields) ? [fields] : fields;
84c9d7 178     if (isString(fields)) {
V 179       fieldList = [fields];
180     }
181     for (const field of fieldList) {
9aa2cf 182       _removeSchemaByFeild(field, schemaList);
84c9d7 183     }
4ff1c4 184     schemaRef.value = schemaList;
84c9d7 185   }
V 186
187   /**
46e087 188    * @description: Delete based on field name
84c9d7 189    */
9aa2cf 190   function _removeSchemaByFeild(field: string, schemaList: FormSchema[]): void {
84c9d7 191     if (isString(field)) {
V 192       const index = schemaList.findIndex((schema) => schema.field === field);
193       if (index !== -1) {
38f507 194         delete formModel[field];
84c9d7 195         schemaList.splice(index, 1);
V 196       }
197     }
198   }
199
200   /**
46e087 201    * @description: Insert after a certain field, if not insert the last
84c9d7 202    */
098621 203   async function appendSchemaByField(
V 204     schema: FormSchema | FormSchema[],
205     prefixField?: string,
206     first = false,
207   ) {
84c9d7 208     const schemaList: FormSchema[] = cloneDeep(unref(getSchema));
V 209
210     const index = schemaList.findIndex((schema) => schema.field === prefixField);
098621 211     const _schemaList = isObject(schema) ? [schema as FormSchema] : (schema as FormSchema[]);
4ff1c4 212     if (!prefixField || index === -1 || first) {
098621 213       first ? schemaList.unshift(..._schemaList) : schemaList.push(..._schemaList);
4ff1c4 214       schemaRef.value = schemaList;
aaa30f 215       _setDefaultValue(schema);
84c9d7 216       return;
V 217     }
218     if (index !== -1) {
098621 219       schemaList.splice(index + 1, 0, ..._schemaList);
84c9d7 220     }
aaa30f 221     _setDefaultValue(schema);
L 222
4ff1c4 223     schemaRef.value = schemaList;
84c9d7 224   }
V 225
c639e4 226   async function resetSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
Z 227     let updateData: Partial<FormSchema>[] = [];
228     if (isObject(data)) {
229       updateData.push(data as FormSchema);
230     }
231     if (isArray(data)) {
232       updateData = [...data];
233     }
234
47a448 235     const hasField = updateData.every(
56a966 236       (item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
47a448 237     );
c639e4 238
Z 239     if (!hasField) {
240       error(
56a966 241         'All children of the form Schema array that need to be updated must contain the `field` field',
c639e4 242       );
Z 243       return;
244     }
245     schemaRef.value = updateData as FormSchema[];
246   }
247
4ff1c4 248   async function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) {
84c9d7 249     let updateData: Partial<FormSchema>[] = [];
V 250     if (isObject(data)) {
251       updateData.push(data as FormSchema);
252     }
253     if (isArray(data)) {
254       updateData = [...data];
255     }
4ff1c4 256
47a448 257     const hasField = updateData.every(
56a966 258       (item) => item.component === 'Divider' || (Reflect.has(item, 'field') && item.field),
47a448 259     );
4ff1c4 260
84c9d7 261     if (!hasField) {
4ff1c4 262       error(
56a966 263         'All children of the form Schema array that need to be updated must contain the `field` field',
4ff1c4 264       );
116a1f 265       return;
84c9d7 266     }
V 267     const schema: FormSchema[] = [];
165743 268     unref(getSchema).forEach((val) => {
CZ 269       let _val;
270       updateData.forEach((item) => {
84c9d7 271         if (val.field === item.field) {
165743 272           _val = item;
84c9d7 273         }
V 274       });
165743 275       if (_val !== undefined && val.field === _val.field) {
CZ 276         const newSchema = deepMerge(val, _val);
277         schema.push(newSchema as FormSchema);
278       } else {
279         schema.push(val);
280       }
84c9d7 281     });
aaa30f 282     _setDefaultValue(schema);
L 283
be3a3e 284     schemaRef.value = uniqBy(schema, 'field');
84c9d7 285   }
V 286
aaa30f 287   function _setDefaultValue(data: FormSchema | FormSchema[]) {
L 288     let schemas: FormSchema[] = [];
289     if (isObject(data)) {
290       schemas.push(data as FormSchema);
291     }
292     if (isArray(data)) {
293       schemas = [...data];
294     }
295
296     const obj: Recordable = {};
3cc72d 297     const currentFieldsValue = getFieldsValue();
aaa30f 298     schemas.forEach((item) => {
L 299       if (
300         item.component != 'Divider' &&
301         Reflect.has(item, 'field') &&
302         item.field &&
3cc72d 303         !isNullOrUnDef(item.defaultValue) &&
0bb3b7 304         (!(item.field in currentFieldsValue) ||
V 305           isNullOrUnDef(currentFieldsValue[item.field]) ||
306           isEmpty(currentFieldsValue[item.field]))
aaa30f 307       ) {
L 308         obj[item.field] = item.defaultValue;
309       }
310     });
311     setFieldsValue(obj);
312   }
313
4ff1c4 314   function getFieldsValue(): Recordable {
84c9d7 315     const formEl = unref(formElRef);
4ff1c4 316     if (!formEl) return {};
84c9d7 317     return handleFormValues(toRaw(unref(formModel)));
V 318   }
319
320   /**
46e087 321    * @description: Is it time
84c9d7 322    */
V 323   function itemIsDateType(key: string) {
324     return unref(getSchema).some((item) => {
4ff1c4 325       return item.field === key ? dateItemType.includes(item.component) : false;
84c9d7 326     });
V 327   }
328
4ff1c4 329   async function validateFields(nameList?: NamePath[] | undefined) {
a305e5 330     return unref(formElRef)?.validateFields(nameList);
84c9d7 331   }
43a45b 332
4ff1c4 333   async function validate(nameList?: NamePath[] | undefined) {
a305e5 334     return await unref(formElRef)?.validate(nameList);
84c9d7 335   }
V 336
4ff1c4 337   async function clearValidate(name?: string | string[]) {
V 338     await unref(formElRef)?.clearValidate(name);
339   }
340
341   async function scrollToField(name: NamePath, options?: ScrollOptions | undefined) {
342     await unref(formElRef)?.scrollToField(name, options);
84c9d7 343   }
V 344
345   /**
46e087 346    * @description: Form submission
84c9d7 347    */
V 348   async function handleSubmit(e?: Event): Promise<void> {
349     e && e.preventDefault();
350     const { submitFunc } = unref(getProps);
351     if (submitFunc && isFunction(submitFunc)) {
352       await submitFunc();
353       return;
354     }
355     const formEl = unref(formElRef);
356     if (!formEl) return;
357     try {
94bf85 358       const values = await validate();
84c9d7 359       const res = handleFormValues(values);
V 360       emit('submit', res);
a248e2 361     } catch (error: any) {
72dbe5 362       if (error?.outOfDate === false && error?.errorFields) {
L 363         return;
364       }
9a21b8 365       throw new Error(error);
LY 366     }
84c9d7 367   }
V 368
369   return {
370     handleSubmit,
371     clearValidate,
372     validate,
373     validateFields,
374     getFieldsValue,
375     updateSchema,
c639e4 376     resetSchema,
84c9d7 377     appendSchemaByField,
768fad 378     removeSchemaByField,
84c9d7 379     resetFields,
V 380     setFieldsValue,
4ff1c4 381     scrollToField,
84c9d7 382   };