index.vue 4.1 KB
<!--
 * @Description: 表单渲染器,根据json生成表单
-->
<template>
  <div class="v-form-container">
    <Form class="v-form-model" ref="eFormModel" :model="formModel" v-bind="formModelProps">
      <Row>
        <FormRender
          v-for="(schema, index) of noHiddenList"
          :key="index"
          :schema="schema"
          :formConfig="formConfig"
          :formData="formModelNew"
          @change="handleChange"
          :setFormModel="setFormModel"
          @submit="handleSubmit"
          @reset="resetFields"
        >
          <template v-if="schema && schema.componentProps" #[`schema.componentProps!.slotName`]>
            <slot
              :name="schema.componentProps!.slotName"
              v-bind="{ formModel: formModel, field: schema.field, schema }"
            ></slot>
          </template>
        </FormRender>
      </Row>
    </Form>
  </div>
</template>
<script lang="ts">
  import { computed, defineComponent, PropType, provide, ref, unref } from 'vue';
  import FormRender from './components/FormRender.vue';
  import { IFormConfig, AForm } from '../../typings/v-form-component';
  import { Form, Row, Col } from 'ant-design-vue';
  import { useFormInstanceMethods } from '../../hooks/useFormInstanceMethods';
  import { IProps, IVFormMethods, useVFormMethods } from '../../hooks/useVFormMethods';
  import { useVModel } from '@vueuse/core';
  import { omit } from 'lodash-es';

  export default defineComponent({
    name: 'VFormCreate',
    components: {
      FormRender,
      Form,
      Row,
    },
    props: {
      fApi: {
        type: Object,
      },
      formModel: {
        type: Object,
        default: () => ({}),
      },
      formConfig: {
        type: Object as PropType<IFormConfig>,
        required: true,
      },
    },
    emits: ['submit', 'change', 'update:fApi', 'update:formModel'],
    setup(props, context) {
      const wrapperComp = props.formConfig.layout == 'vertical' ? Col : Row;
      const { emit } = context;
      const eFormModel = ref<AForm | null>(null);

      const formModelNew = computed({
        get: () => props.formModel,
        set: (value) => emit('update:formModel', value),
      });

      const noHiddenList = computed(() => {
        return (
          props.formConfig.schemas &&
          props.formConfig.schemas.filter((item) => item.hidden !== true)
        );
      });

      const fApi = useVModel(props, 'fApi', emit);

      const { submit, validate, clearValidate, resetFields, validateField } =
        useFormInstanceMethods(props, formModelNew, context, eFormModel);

      const { linkOn, ...methods } = useVFormMethods(
        { formConfig: props.formConfig, formData: props.formModel } as unknown as IProps,
        context,
        eFormModel,
        {
          submit,
          validate,
          validateField,
          resetFields,
          clearValidate,
        },
      );

      fApi.value = methods;

      const handleChange = (_event) => {
        const { schema, value } = _event;
        const { field } = unref(schema);

        linkOn[field!]?.forEach((formItem) => {
          formItem.update?.(value, formItem, fApi.value as IVFormMethods);
        });
      };
      /**
       * 获取表单属性
       */
      const formModelProps = computed(
        () => omit(props.formConfig, ['disabled', 'labelWidth', 'schemas']) as Recordable,
      );

      const handleSubmit = () => {
        submit();
      };

      provide('formModel', formModelNew);
      const setFormModel = (key, value) => {
        formModelNew.value[key] = value;
      };

      provide<(key: String, value: any) => void>('setFormModelMethod', setFormModel);

      // 把祖先组件的方法项注入到子组件中,子组件可通过inject获取
      return {
        eFormModel,
        submit,
        validate,
        validateField,
        resetFields,
        clearValidate,
        handleChange,
        formModelProps,
        handleSubmit,
        setFormModel,
        formModelNew,
        wrapperComp,
        noHiddenList,
      };
    },
  });
</script>

<style lang="less" scoped>
  .v-form-model {
    overflow: hidden;
  }
</style>