vben
2021-11-10 3fcfac1f37c2aeabbb2af4897ada6ba8c225c667
提交 | 用户 | age
2f6253 1 <template>
9b2d41 2   <Form
4f20d4 3     v-bind="getBindValue"
9b2d41 4     :class="getFormClass"
N 5     ref="formElRef"
6     :model="formModel"
7     @keypress.enter="handleEnterPress"
8   >
4f20d4 9     <Row v-bind="getRow">
9edc28 10       <slot name="formHeader"></slot>
2f6253 11       <template v-for="schema in getSchema" :key="schema.field">
12         <FormItem
5832ee 13           :tableAction="tableAction"
1d4561 14           :formActionType="formActionType"
2f6253 15           :schema="schema"
16           :formProps="getProps"
1c075a 17           :allDefaultValues="defaultValueRef"
2f6253 18           :formModel="formModel"
4ff1c4 19           :setFormModel="setFormModel"
2f6253 20         >
faf3f4 21           <template #[item]="data" v-for="item in Object.keys($slots)">
b1f317 22             <slot :name="item" v-bind="data || {}"></slot>
2f6253 23           </template>
24         </FormItem>
25       </template>
498278 26
e15b4f 27       <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
de5bf7 28         <template
V 29           #[item]="data"
30           v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
31         >
47a448 32           <slot :name="item" v-bind="data || {}"></slot>
de5bf7 33         </template>
V 34       </FormAction>
9edc28 35       <slot name="formFooter"></slot>
2f6253 36     </Row>
37   </Form>
38 </template>
39 <script lang="ts">
40   import type { FormActionType, FormProps, FormSchema } from './types/form';
84c9d7 41   import type { AdvanceState } from './types/hooks';
4f20d4 42   import type { Ref } from 'vue';
2f6253 43
3ff70b 44   import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
2f6253 45   import { Form, Row } from 'ant-design-vue';
1418dc 46   import FormItem from './components/FormItem.vue';
4ff1c4 47   import FormAction from './components/FormAction.vue';
2f6253 48
49   import { dateItemType } from './helper';
3509eb 50   import { dateUtil } from '/@/utils/dateUtil';
V 51
a305e5 52   // import { cloneDeep } from 'lodash-es';
84b830 53   import { deepMerge } from '/@/utils';
V 54
2f6253 55   import { useFormValues } from './hooks/useFormValues';
84c9d7 56   import useAdvanced from './hooks/useAdvanced';
4ff1c4 57   import { useFormEvents } from './hooks/useFormEvents';
V 58   import { createFormContext } from './hooks/useFormContext';
ac1a36 59   import { useAutoFocus } from './hooks/useAutoFocus';
2882d6 60   import { useModalContext } from '/@/components/Modal';
4ff1c4 61
V 62   import { basicProps } from './props';
ac1a36 63   import { useDesign } from '/@/hooks/web/useDesign';
785732 64
2f6253 65   export default defineComponent({
66     name: 'BasicForm',
67     components: { FormItem, Form, Row, FormAction },
68     props: basicProps,
69     emits: ['advanced-change', 'reset', 'submit', 'register'],
4f20d4 70     setup(props, { emit, attrs }) {
4ff1c4 71       const formModel = reactive<Recordable>({});
2882d6 72       const modalFn = useModalContext();
84c9d7 73
V 74       const advanceState = reactive<AdvanceState>({
2f6253 75         isAdvanced: true,
76         hideAdvanceBtn: false,
77         isLoad: false,
78         actionSpan: 6,
79       });
84c9d7 80
4ff1c4 81       const defaultValueRef = ref<Recordable>({});
46e087 82       const isInitedDefaultRef = ref(false);
2f6253 83       const propsRef = ref<Partial<FormProps>>({});
0b6110 84       const schemaRef = ref<Nullable<FormSchema[]>>(null);
84b830 85       const formElRef = ref<Nullable<FormActionType>>(null);
b9d3d6 86
ac1a36 87       const { prefixCls } = useDesign('basic-form');
V 88
4ff1c4 89       // Get the basic configuration of the form
9b2d41 90       const getProps = computed((): FormProps => {
N 91         return { ...props, ...unref(propsRef) } as FormProps;
92       });
ac1a36 93
V 94       const getFormClass = computed(() => {
95         return [
96           prefixCls,
97           {
98             [`${prefixCls}--compact`]: unref(getProps).compact,
99           },
100         ];
101       });
bb1b26 102
785732 103       // Get uniform row style and Row configuration for the entire form
e15b4f 104       const getRow = computed((): Recordable => {
785732 105         const { baseRowStyle = {}, rowProps } = unref(getProps);
L 106         return {
107           style: baseRowStyle,
108           ...rowProps,
109         };
9b2d41 110       });
4f20d4 111
V 112       const getBindValue = computed(
56a966 113         () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
4f20d4 114       );
2f6253 115
116       const getSchema = computed((): FormSchema[] => {
117         const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
118         for (const schema of schemas) {
119           const { defaultValue, component } = schema;
498278 120           // handle date type
bb1b26 121           if (defaultValue && dateItemType.includes(component)) {
0b6110 122             if (!Array.isArray(defaultValue)) {
3509eb 123               schema.defaultValue = dateUtil(defaultValue);
0b6110 124             } else {
3fcfac 125               const def: any[] = [];
0b6110 126               defaultValue.forEach((item) => {
3509eb 127                 def.push(dateUtil(item));
0b6110 128               });
V 129               schema.defaultValue = def;
130             }
2f6253 131           }
132         }
47a448 133         if (unref(getProps).showAdvancedButton) {
134           return schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[];
135         } else {
136           return schemas as FormSchema[];
137         }
2f6253 138       });
139
4ff1c4 140       const { handleToggleAdvanced } = useAdvanced({
84c9d7 141         advanceState,
V 142         emit,
143         getProps,
144         getSchema,
145         formModel,
146         defaultValueRef,
2f6253 147       });
4ff1c4 148
84c9d7 149       const { handleFormValues, initDefault } = useFormValues({
3ff70b 150         getProps,
84c9d7 151         defaultValueRef,
V 152         getSchema,
153         formModel,
ac1a36 154       });
V 155
156       useAutoFocus({
157         getSchema,
3ff70b 158         getProps,
ac1a36 159         isInitedDefault: isInitedDefaultRef,
V 160         formElRef: formElRef as Ref<FormActionType>,
84c9d7 161       });
1c075a 162
84c9d7 163       const {
4ff1c4 164         handleSubmit,
84c9d7 165         setFieldsValue,
V 166         clearValidate,
167         validate,
168         validateFields,
169         getFieldsValue,
170         updateSchema,
c639e4 171         resetSchema,
84c9d7 172         appendSchemaByField,
V 173         removeSchemaByFiled,
174         resetFields,
4ff1c4 175         scrollToField,
V 176       } = useFormEvents({
84c9d7 177         emit,
V 178         getProps,
179         formModel,
180         getSchema,
181         defaultValueRef,
0b6110 182         formElRef: formElRef as Ref<FormActionType>,
V 183         schemaRef: schemaRef as Ref<FormSchema[]>,
84c9d7 184         handleFormValues,
4ff1c4 185       });
V 186
187       createFormContext({
188         resetAction: resetFields,
189         submitAction: handleSubmit,
84c9d7 190       });
1c075a 191
1db72c 192       watch(
4ff1c4 193         () => unref(getProps).model,
1db72c 194         () => {
4ff1c4 195           const { model } = unref(getProps);
V 196           if (!model) return;
197           setFieldsValue(model);
1db72c 198         },
V 199         {
200           immediate: true,
56a966 201         },
1db72c 202       );
1c075a 203
2882d6 204       watch(
808328 205         () => unref(getProps).schemas,
206         (schemas) => {
207           resetSchema(schemas ?? []);
56a966 208         },
808328 209       );
210
211       watch(
0b6110 212         () => getSchema.value,
46e087 213         (schema) => {
2882d6 214           nextTick(() => {
V 215             //  Solve the problem of modal adaptive height calculation when the form is placed in the modal
216             modalFn?.redoModalHeight?.();
217           });
46e087 218           if (unref(isInitedDefaultRef)) {
2882d6 219             return;
46e087 220           }
4ff1c4 221           if (schema?.length) {
46e087 222             initDefault();
V 223             isInitedDefaultRef.value = true;
224           }
56a966 225         },
0b6110 226       );
V 227
4ff1c4 228       async function setProps(formProps: Partial<FormProps>): Promise<void> {
V 229         propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
230       }
231
232       function setFormModel(key: string, value: any) {
233         formModel[key] = value;
f84401 234         const { validateTrigger } = unref(getBindValue);
235         if (!validateTrigger || validateTrigger === 'change') {
571f28 236           validateFields([key]).catch((_) => {});
f84401 237         }
2f6253 238       }
239
9b2d41 240       function handleEnterPress(e: KeyboardEvent) {
N 241         const { autoSubmitOnEnter } = unref(getProps);
242         if (!autoSubmitOnEnter) return;
243         if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
244           const target: HTMLElement = e.target as HTMLElement;
245           if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
246             handleSubmit();
247           }
248         }
249       }
250
1d4561 251       const formActionType: Partial<FormActionType> = {
2f6253 252         getFieldsValue,
253         setFieldsValue,
254         resetFields,
255         updateSchema,
c639e4 256         resetSchema,
2f6253 257         setProps,
258         removeSchemaByFiled,
259         appendSchemaByField,
260         clearValidate,
4ff1c4 261         validateFields,
V 262         validate,
263         submit: handleSubmit,
264         scrollToField: scrollToField,
2f6253 265       };
1c075a 266
2f6253 267       onMounted(() => {
1c075a 268         initDefault();
1d4561 269         emit('register', formActionType);
2f6253 270       });
1c075a 271
2f6253 272       return {
4f20d4 273         getBindValue,
2f6253 274         handleToggleAdvanced,
9b2d41 275         handleEnterPress,
2f6253 276         formModel,
1c075a 277         defaultValueRef,
2f6253 278         advanceState,
785732 279         getRow,
2f6253 280         getProps,
281         formElRef,
282         getSchema,
e15b4f 283         formActionType: formActionType as any,
4ff1c4 284         setFormModel,
ac1a36 285         getFormClass,
e15b4f 286         getFormActionBindProps: computed(
56a966 287           (): Recordable => ({ ...getProps.value, ...advanceState }),
e15b4f 288         ),
1d4561 289         ...formActionType,
2f6253 290       };
291     },
292   });
293 </script>
ac1a36 294 <style lang="less">
V 295   @prefix-cls: ~'@{namespace}-basic-form';
296
297   .@{prefix-cls} {
298     .ant-form-item {
299       &-label label::after {
300         margin: 0 6px 0 2px;
301       }
302
303       &-with-help {
304         margin-bottom: 0;
305       }
306
307       &:not(.ant-form-item-with-help) {
308         margin-bottom: 20px;
309       }
310
311       &.suffix-item {
312         .ant-form-item-children {
313           display: flex;
314         }
315
67a7a7 316         .ant-form-item-control {
V 317           margin-top: 4px;
318         }
319
ac1a36 320         .suffix {
67a7a7 321           display: inline-flex;
ac1a36 322           padding-left: 6px;
67a7a7 323           margin-top: 1px;
V 324           line-height: 1;
325           align-items: center;
ac1a36 326         }
V 327       }
328     }
329
330     .ant-form-explain {
331       font-size: 14px;
332     }
333
334     &--compact {
335       .ant-form-item {
08df19 336         margin-bottom: 8px !important;
ac1a36 337       }
V 338     }
339   }
340 </style>