BasicForm.vue 9.6 KB
Newer Older
陈文彬 已提交
1
<template>
2
  <Form
V
Vben 已提交
3
    v-bind="getBindValue"
4 5 6 7 8
    :class="getFormClass"
    ref="formElRef"
    :model="formModel"
    @keypress.enter="handleEnterPress"
  >
V
Vben 已提交
9
    <Row v-bind="getRow">
V
vben 已提交
10
      <slot name="formHeader"></slot>
陈文彬 已提交
11 12
      <template v-for="schema in getSchema" :key="schema.field">
        <FormItem
13
          :tableAction="tableAction"
14
          :formActionType="formActionType"
陈文彬 已提交
15 16
          :schema="schema"
          :formProps="getProps"
17
          :allDefaultValues="defaultValueRef"
陈文彬 已提交
18
          :formModel="formModel"
V
vben 已提交
19
          :setFormModel="setFormModel"
陈文彬 已提交
20
        >
陈文彬 已提交
21
          <template #[item]="data" v-for="item in Object.keys($slots)">
无木 已提交
22
            <slot :name="item" v-bind="data || {}"></slot>
陈文彬 已提交
23 24 25
          </template>
        </FormItem>
      </template>
V
vben 已提交
26

27
      <FormAction v-bind="getFormActionBindProps" @toggle-advanced="handleToggleAdvanced">
V
vben 已提交
28 29 30 31
        <template
          #[item]="data"
          v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']"
        >
32
          <slot :name="item" v-bind="data || {}"></slot>
V
vben 已提交
33 34
        </template>
      </FormAction>
V
vben 已提交
35
      <slot name="formFooter"></slot>
陈文彬 已提交
36 37 38 39 40
    </Row>
  </Form>
</template>
<script lang="ts">
  import type { FormActionType, FormProps, FormSchema } from './types/form';
41
  import type { AdvanceState } from './types/hooks';
V
Vben 已提交
42
  import type { Ref } from 'vue';
V
vben 已提交
43

44
  import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
陈文彬 已提交
45
  import { Form, Row } from 'ant-design-vue';
V
Vben 已提交
46
  import FormItem from './components/FormItem.vue';
V
vben 已提交
47
  import FormAction from './components/FormAction.vue';
陈文彬 已提交
48 49

  import { dateItemType } from './helper';
V
vben 已提交
50 51
  import { dateUtil } from '/@/utils/dateUtil';

V
vben 已提交
52
  // import { cloneDeep } from 'lodash-es';
53 54
  import { deepMerge } from '/@/utils';

陈文彬 已提交
55
  import { useFormValues } from './hooks/useFormValues';
56
  import useAdvanced from './hooks/useAdvanced';
V
vben 已提交
57 58
  import { useFormEvents } from './hooks/useFormEvents';
  import { createFormContext } from './hooks/useFormContext';
V
vben 已提交
59
  import { useAutoFocus } from './hooks/useAutoFocus';
V
vben 已提交
60
  import { useModalContext } from '/@/components/Modal';
61
  import { useDebounceFn } from '@vueuse/core';
V
vben 已提交
62 63

  import { basicProps } from './props';
V
vben 已提交
64
  import { useDesign } from '/@/hooks/web/useDesign';
65
  import { cloneDeep } from 'lodash-es';
V
vben 已提交
66

陈文彬 已提交
67 68 69 70
  export default defineComponent({
    name: 'BasicForm',
    components: { FormItem, Form, Row, FormAction },
    props: basicProps,
C
chengj 已提交
71
    emits: ['advanced-change', 'reset', 'submit', 'register', 'field-value-change'],
V
Vben 已提交
72
    setup(props, { emit, attrs }) {
V
vben 已提交
73
      const formModel = reactive<Recordable>({});
V
vben 已提交
74
      const modalFn = useModalContext();
75 76

      const advanceState = reactive<AdvanceState>({
陈文彬 已提交
77 78 79 80 81
        isAdvanced: true,
        hideAdvanceBtn: false,
        isLoad: false,
        actionSpan: 6,
      });
82

V
vben 已提交
83
      const defaultValueRef = ref<Recordable>({});
V
vben 已提交
84
      const isInitedDefaultRef = ref(false);
陈文彬 已提交
85
      const propsRef = ref<Partial<FormProps>>({});
V
vben 已提交
86
      const schemaRef = ref<Nullable<FormSchema[]>>(null);
87
      const formElRef = ref<Nullable<FormActionType>>(null);
陈文彬 已提交
88

V
vben 已提交
89 90
      const { prefixCls } = useDesign('basic-form');

V
vben 已提交
91
      // Get the basic configuration of the form
92 93 94
      const getProps = computed((): FormProps => {
        return { ...props, ...unref(propsRef) } as FormProps;
      });
95

V
vben 已提交
96 97 98 99 100 101 102 103 104
      const getFormClass = computed(() => {
        return [
          prefixCls,
          {
            [`${prefixCls}--compact`]: unref(getProps).compact,
          },
        ];
      });

105
      // Get uniform row style and Row configuration for the entire form
106
      const getRow = computed((): Recordable => {
107 108 109 110 111
        const { baseRowStyle = {}, rowProps } = unref(getProps);
        return {
          style: baseRowStyle,
          ...rowProps,
        };
112
      });
陈文彬 已提交
113

V
Vben 已提交
114
      const getBindValue = computed(
V
vben 已提交
115
        () => ({ ...attrs, ...props, ...unref(getProps) } as Recordable),
V
Vben 已提交
116 117
      );

陈文彬 已提交
118 119 120 121
      const getSchema = computed((): FormSchema[] => {
        const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
        for (const schema of schemas) {
          const { defaultValue, component } = schema;
V
vben 已提交
122
          // handle date type
123
          if (defaultValue && dateItemType.includes(component)) {
V
vben 已提交
124
            if (!Array.isArray(defaultValue)) {
V
vben 已提交
125
              schema.defaultValue = dateUtil(defaultValue);
V
vben 已提交
126
            } else {
V
vben 已提交
127
              const def: any[] = [];
V
vben 已提交
128
              defaultValue.forEach((item) => {
V
vben 已提交
129
                def.push(dateUtil(item));
V
vben 已提交
130 131 132
              });
              schema.defaultValue = def;
            }
陈文彬 已提交
133 134
          }
        }
135
        if (unref(getProps).showAdvancedButton) {
136 137 138
          return cloneDeep(
            schemas.filter((schema) => schema.component !== 'Divider') as FormSchema[],
          );
139
        } else {
140
          return cloneDeep(schemas as FormSchema[]);
141
        }
陈文彬 已提交
142 143
      });

V
vben 已提交
144
      const { handleToggleAdvanced } = useAdvanced({
145 146 147 148 149 150
        advanceState,
        emit,
        getProps,
        getSchema,
        formModel,
        defaultValueRef,
陈文彬 已提交
151
      });
V
vben 已提交
152

153
      const { handleFormValues, initDefault } = useFormValues({
154
        getProps,
155 156 157 158
        defaultValueRef,
        getSchema,
        formModel,
      });
159

V
vben 已提交
160 161
      useAutoFocus({
        getSchema,
162
        getProps,
V
vben 已提交
163 164 165 166
        isInitedDefault: isInitedDefaultRef,
        formElRef: formElRef as Ref<FormActionType>,
      });

167
      const {
V
vben 已提交
168
        handleSubmit,
169 170 171 172 173 174
        setFieldsValue,
        clearValidate,
        validate,
        validateFields,
        getFieldsValue,
        updateSchema,
Z
zuihou 已提交
175
        resetSchema,
176
        appendSchemaByField,
J
JinMao 已提交
177
        removeSchemaByField,
178
        resetFields,
V
vben 已提交
179 180
        scrollToField,
      } = useFormEvents({
181 182 183 184 185
        emit,
        getProps,
        formModel,
        getSchema,
        defaultValueRef,
V
vben 已提交
186 187
        formElRef: formElRef as Ref<FormActionType>,
        schemaRef: schemaRef as Ref<FormSchema[]>,
188
        handleFormValues,
V
vben 已提交
189 190 191 192 193
      });

      createFormContext({
        resetAction: resetFields,
        submitAction: handleSubmit,
194
      });
陈文彬 已提交
195

V
vben 已提交
196
      watch(
V
vben 已提交
197
        () => unref(getProps).model,
V
vben 已提交
198
        () => {
V
vben 已提交
199 200 201
          const { model } = unref(getProps);
          if (!model) return;
          setFieldsValue(model);
V
vben 已提交
202 203 204
        },
        {
          immediate: true,
V
vben 已提交
205
        },
V
vben 已提交
206
      );
207

无木 已提交
208 209 210 211
      watch(
        () => unref(getProps).schemas,
        (schemas) => {
          resetSchema(schemas ?? []);
V
vben 已提交
212
        },
无木 已提交
213 214
      );

V
vben 已提交
215
      watch(
V
vben 已提交
216
        () => getSchema.value,
V
vben 已提交
217
        (schema) => {
V
vben 已提交
218 219 220 221
          nextTick(() => {
            //  Solve the problem of modal adaptive height calculation when the form is placed in the modal
            modalFn?.redoModalHeight?.();
          });
V
vben 已提交
222
          if (unref(isInitedDefaultRef)) {
V
vben 已提交
223
            return;
V
vben 已提交
224
          }
V
vben 已提交
225
          if (schema?.length) {
V
vben 已提交
226 227 228
            initDefault();
            isInitedDefaultRef.value = true;
          }
V
vben 已提交
229
        },
V
vben 已提交
230 231
      );

232 233 234 235 236 237 238 239
      watch(
        () => formModel,
        useDebounceFn(() => {
          unref(getProps).submitOnChange && handleSubmit();
        }, 300),
        { deep: true },
      );

V
vben 已提交
240 241 242 243 244 245
      async function setProps(formProps: Partial<FormProps>): Promise<void> {
        propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
      }

      function setFormModel(key: string, value: any) {
        formModel[key] = value;
246 247
        const { validateTrigger } = unref(getBindValue);
        if (!validateTrigger || validateTrigger === 'change') {
248
          validateFields([key]).catch((_) => {});
249
        }
C
chengj 已提交
250
        emit('field-value-change', key, value);
陈文彬 已提交
251 252
      }

253 254 255 256 257 258 259 260 261 262 263
      function handleEnterPress(e: KeyboardEvent) {
        const { autoSubmitOnEnter } = unref(getProps);
        if (!autoSubmitOnEnter) return;
        if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
          const target: HTMLElement = e.target as HTMLElement;
          if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
            handleSubmit();
          }
        }
      }

264
      const formActionType: Partial<FormActionType> = {
陈文彬 已提交
265 266 267 268
        getFieldsValue,
        setFieldsValue,
        resetFields,
        updateSchema,
Z
zuihou 已提交
269
        resetSchema,
陈文彬 已提交
270
        setProps,
J
JinMao 已提交
271
        removeSchemaByField,
陈文彬 已提交
272 273
        appendSchemaByField,
        clearValidate,
V
vben 已提交
274 275 276 277
        validateFields,
        validate,
        submit: handleSubmit,
        scrollToField: scrollToField,
陈文彬 已提交
278
      };
279

陈文彬 已提交
280
      onMounted(() => {
281
        initDefault();
282
        emit('register', formActionType);
陈文彬 已提交
283
      });
284

陈文彬 已提交
285
      return {
V
Vben 已提交
286
        getBindValue,
陈文彬 已提交
287
        handleToggleAdvanced,
288
        handleEnterPress,
陈文彬 已提交
289
        formModel,
290
        defaultValueRef,
陈文彬 已提交
291
        advanceState,
292
        getRow,
陈文彬 已提交
293 294 295
        getProps,
        formElRef,
        getSchema,
296
        formActionType: formActionType as any,
V
vben 已提交
297
        setFormModel,
V
vben 已提交
298
        getFormClass,
299
        getFormActionBindProps: computed(
V
vben 已提交
300
          (): Recordable => ({ ...getProps.value, ...advanceState }),
301
        ),
302
        ...formActionType,
陈文彬 已提交
303 304 305 306
      };
    },
  });
</script>
V
vben 已提交
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
<style lang="less">
  @prefix-cls: ~'@{namespace}-basic-form';

  .@{prefix-cls} {
    .ant-form-item {
      &-label label::after {
        margin: 0 6px 0 2px;
      }

      &-with-help {
        margin-bottom: 0;
      }

      &:not(.ant-form-item-with-help) {
        margin-bottom: 20px;
      }

      &.suffix-item {
        .ant-form-item-children {
          display: flex;
        }

329 330 331 332
        .ant-form-item-control {
          margin-top: 4px;
        }

V
vben 已提交
333
        .suffix {
334
          display: inline-flex;
V
vben 已提交
335
          padding-left: 6px;
336 337 338
          margin-top: 1px;
          line-height: 1;
          align-items: center;
V
vben 已提交
339 340 341 342 343 344 345 346 347 348
        }
      }
    }

    .ant-form-explain {
      font-size: 14px;
    }

    &--compact {
      .ant-form-item {
V
vben 已提交
349
        margin-bottom: 8px !important;
V
vben 已提交
350 351 352 353
      }
    }
  }
</style>