vue2+ant-design-vue a-form-model component secondary encapsulation (form form component) FormModel form

1. Renderings

2. Parameter configuration

1. Code example

<t-antd-form
  :ref-obj.sync="formOpts.ref"
  :formOpts="formOpts"
  :widthSize="1"
  :labelCol="{ span:2}"
  :wrapperCol="{ span:22}"
  @handleEvent="handleEvent"
/>

2. Configuration parameters inherit all properties of FormModel

Parameter Description Type Default value
refObj form form validation rule method (you can refer to validate in the antd FormModel form method) obj
className Custom class name String
layout Change the layout of the form item label and input box (default: horizontal) /vertical String ‘horizontal’
widthSize Display per line Several input items (default two) Maximum value 4 Number 2
isTrim Whether to enable clearing of leading and trailing spaces globally (comp is a-input and type is not equal to ‘password’) Boolean true
formOpts Form configuration items Object {}
-listTypeInfo Drop-down selection data source (type:’select’ is valid) td>

Object {}
-fieldList form form list Array []
——isHideItem An item is not displayed Boolean false
——slotName Customize an input box of a form slot
——childSlotName Customize the slot of a certain drop-down selection subcomponent of the form (a-select-option) slot
——comp Form each component is an input box or drop-down selection, etc. (You can use third-party UI such as a-select/a-input or you can use custom components) String
——formItemBind Form each An attribute (inherits the Attributes of FormModelItem) Object {}
——bind Each attribute of the form (inherits the Attributes of the third-party UI, such as in a-input allowClear clearing function) default clearing and drop-down filtering Object {}
——isTrim Whether to clear the spaces before and after (comp is a-input and type is not equal to ‘password ‘) Boolean false
——type Form form each item type String
——widthSize The proportion of an item in the form form (if it occupies a whole row, set 1) Number 2
——width The actual proportion of a certain item in the form form Width String 100%
——arrLabel type=select-arr, the Chinese displayed in each drop-down String ‘label’
——arrKey When type=select-arr, each drop-down display shows the Chinese number transmitted to the background String ‘value’
——label For each item title String
——labelRender Customize a certain item title function
——value Parameters passed to the background for each form form item String
——rules Form validation rules for each input box Object/Array
——list Drop-down selection data source (only valid for type:’select’) String
——event Event mark of each form item (handleEvent event) String
——eventHandle Inherit the event of comp component (return Two parameters, the first comes with it, the second formOpts) Object
——isSelfCom Whether to use its own encapsulated components (TAntdSelect, etc. – including drop-down box) Boolean false
-formData Form submission data (corresponding to the value of each item in fieldList) Object
-labelCol label Width ({ span:2}) Object {span:2}
-wrapperCol Input box width Object {span:22}
-rules Rule (can be configured according to AntdUI FormModel–corresponding to the value of formData) Object/Array
-operatorList Operation button list Array

3. events inherit all events of FormModel

Event name Description Return value
handleEvent A single query condition triggers an event Type/query condition input value in fieldList/event value in fieldList

4. Methods

Event name Description Parameters
resetFields Reset form
clearValidate Clear Validate Check

5. Some properties provided by Ant-Design-Vue FormModel/FormModelItem can be used directly without any other configuration

3. Source code

<template>
  <FormModel
    ref="form"
    class="t_antd_form"
    :class="className"
    :model="formOpts.formData"
    :rules="formOpts.rules"
    :layout="formOpts.layout||'horizontal'"
    v-bind="formAttr"
    v-on="$listeners"
  >
    <template v-for="(item, index) in fieldList">
      <FormModelItem
        v-if="!item.isHideItem"
        :key="index"
        :prop="item.value"
        :label="item.label"
        :class="[item.className]"
        :rules="item.rules"
        :style="getChildWidth(item)"
        v-bind="{...item.formItemBind}"
      >
        <!-- Custom label -->
        <template #label v-if="item.labelRender">
          <render-comp :createElementFunc="item.labelRender" />
        </template>
        <!-- Custom input box slot -->
        <template v-if="item.slotName">
          <slot :name="item.slotName"></slot>
        </template>
        <!-- Text display value -->
        <template v-if="item.textShow">
          <span>{<!-- -->{item.textValue||formOpts.formData[item.value]}}</span>
        </template>
        <template v-if="item.isSelfCom">
          <component
            :is="item.comp"
            v-model="formOpts.formData[item.value]"
            :placeholder="item.placeholder||getPlaceholder(item)"
            v-bind="{allowClear:true,showSearch:true,...item.bind}"
            :style="{width: item.width||'100%'}"
            v-on="cEvent(item)"
          />
        </template>
        <component
          v-if="!item.slotName & amp; & amp;!item.textShow & amp; & amp;!item.isSelfCom"
          :is="item.comp"
          v-model="formOpts.formData[item.value]"
          :type="item.type||item.bind.type"
          :mode="item.comp.includes('picker')?(item.type||item.bind.type):''"
          :placeholder="item.placeholder||getPlaceholder(item)"
          @change="handleEvent(item.event, formOpts.formData[item.value],item)"
          v-bind="{allowClear:true,showSearch:true,...item.bind}"
          :style="{width: item.width||'100%'}"
          v-on="cEvent(item)"
        >
          <template #addonBefore v-if="item.addonBefore">{<!-- -->{ item.addonBefore }}</template>
          <template #addonAfter v-if="item.addonAfter">{<!-- -->{ item.addonAfter }}</template>
          <template v-if="item.childSlotName">
            <slot :name="item.childSlotName"></slot>
          </template>
          <component
            v-else
            :is="compChildName(item)"
            v-for="(value, key, index) in selectListType(item)"
            :key="index"
            :disabled="value.disabled"
            :label="compChildLabel(item,value)"
            :value="compChildValue(item,value,key)"
          >{<!-- -->{compChildShowLabel(item,value)}}</component>
        </component>
      </FormModelItem>
    </template>
    <!-- button -->
    <div class="footer_btn">
      <template v-if="formOpts.btnSlotName">
        <slot :name="formOpts.btnSlotName"></slot>
      </template>
      <template v-if="!formOpts.btnSlotName & amp; & amp;formOpts.operatorList & amp; & amp;formOpts.operatorList.length>0">
        <Button
          v-for="(val,index) in formOpts.operatorList"
          :key="index"
          @click="val.fun(val)"
          :type="val.type||'primary'"
          :icon="val.icon"
          :size="val.size || 'default'"
          :disabled="val.disabled"
        >{<!-- -->{ val.label }}</Button>
      </template>
    </div>
  </FormModel>
</template>
<script>
import RenderComp from './render-comp.vue'
import {<!-- --> FormModel, Button } from 'ant-design-vue'
export default {<!-- -->
  name: 'TAntdForm',
  components: {<!-- -->
    RenderComp,
    FormModel,
    FormModelItem: FormModel.Item,
    Button
  },
  props: {<!-- -->
    /** Form configuration item description
     * formData object form submission data
     * rules object verification rules
     * fieldList Array form rendering data
     * operatorList Array operation button list
     * listTypeInfo object drop-down option data
     */
    formOpts: {<!-- -->
      type: Object,
      default: () => ({<!-- -->})
    },
    // Custom class name
    className: {<!-- -->
      type: String
    },
    // Display several input items in one line; maximum value 4
    widthSize: {<!-- -->
      type: Number,
      default: 2,
      validator: (value) => {<!-- -->
        return value <= 4
      }
    },
    //Whether clearing spaces before and after is enabled globally
    isTrim: {<!-- -->
      type: Boolean,
      default: true
    },
    //ref
    refObj: {<!-- -->
      type: Object
    }
  },
  data() {<!-- -->
    return {<!-- -->
      colSize: this.widthSize,
      fieldList: this.formOpts.fieldList
    }
  },
  computed: {<!-- -->
    formAttr() {<!-- -->
      let attr = {<!-- -->}
      this.formOpts.layout === 'vertical'
        ? attr = {<!-- -->
          ...this.$attrs
        } : attr = {<!-- -->
          labelCol: {<!-- --> span: 2 },
          wrapperCol: {<!-- --> span: 22 },
          ...this.$attrs
        }
      return attr
    },
    cEvent() {<!-- -->
      return ({<!-- --> eventHandle }, type) => {<!-- -->
        let event = {<!-- --> ...eventHandle }
        let changeEvent = {<!-- -->}
        Object.keys(event).forEach(v => {<!-- -->
          changeEvent[v] = (e, ids) => {<!-- -->
            if (type === 't-antd-select-table') {<!-- -->
              event[v] & amp; & amp; event[v](e, ids, arguments)
            } else {<!-- -->
              if ((typeof e === 'number' & amp; & amp; e === 0) || e) {<!-- -->
                event[v] & amp; & amp; event[v](e, this.formOpts, arguments)
              } else {<!-- -->
                event[v] & amp; & amp; event[v](this.formOpts, arguments)
              }
            }
          }
        })
        return {<!-- --> ...changeEvent }
      }
    },
    selectListType() {<!-- -->
      return ({<!-- --> list }) => {<!-- -->
        if (this.formOpts.listTypeInfo) {<!-- -->
          return this.formOpts.listTypeInfo[list]
        } else {<!-- -->
          return []
        }
      }
    },
    // Subcomponent name
    compChildName() {<!-- -->
      return ({<!-- --> type }) => {<!-- -->
        switch (type) {<!-- -->
          case 'checkbox':
            return 'a-checkbox'
          case 'radio':
            return 'a-radio'
          case 'select-arr':
          case 'select-obj':
            return 'a-select-option'
        }
      }
    },
    //Sub-subcomponent label
    compChildLabel() {<!-- -->
      return ({<!-- --> type, arrLabel }, value) => {<!-- -->
        switch (type) {<!-- -->
          case 'radio':
          case 'checkbox':
            return value.value
          case 'select-arr':
            return value[arrLabel || 'label']
          case 'select-obj':
            return value
        }
      }
    },
    // child subcomponent value
    compChildValue() {<!-- -->
      return ({<!-- --> type, arrKey }, value, key) => {<!-- -->
        switch (type) {<!-- -->
          case 'radio':
          case 'checkbox':
            return value.value
          case 'select-arr':
            return value[arrKey || 'value']
          case 'select-obj':
            return key
        }
      }
    },
    // Sub-subcomponent text display
    compChildShowLabel() {<!-- -->
      return ({<!-- --> type, arrLabel }, value) => {<!-- -->
        switch (type) {<!-- -->
          case 'radio':
          case 'checkbox':
            return value.label
          case 'select-arr':
            return value[arrLabel || 'label']
          case 'select-obj':
            return value
        }
      }
    }
  },
  watch: {<!-- -->
    'formOpts.formData': {<!-- -->
      handler(val) {<!-- -->
        // Return the form instance to the parent
        this.$emit('update:refObj', this.$refs.form)
      },
      deep: true // deep monitoring
    },
    widthSize(val) {<!-- -->
      if (val > 4) {<!-- -->
        this.$message.warning('widthSize value cannot be greater than 4!')
        this.colSize = 4
      } else {<!-- -->
        this.colSize = val
      }
    }
  },
  mounted() {<!-- -->
    // Return the form instance to the parent
    this.$emit('update:refObj', this.$refs.form)
  },

  methods: {<!-- -->
    //Layout of label and input box
    getChildWidth(item) {<!-- -->
      if (this.formOpts.layout === 'vertical') {<!-- -->
        return `flex: 0 1 calc((${<!-- -->100 / (item.widthSize || this.colSize)}% - 10px));margin-right:10px;`
      } else {<!-- -->
        return `flex: 0 1 ${<!-- -->100 / (item.widthSize || this.colSize)}%;`
      }
    },
    // Get the display of placeholder
    getPlaceholder(row) {<!-- -->
      let placeholder
      if (typeof row.comp === 'string' & amp; & amp; row.comp) {<!-- -->
        if (row.comp.includes('input')) {<!-- -->
          placeholder = row.label ? `Please enter ${<!-- -->row.label}` : `Please enter`
        } else if (row.comp.includes('select') || row.comp.includes('cascader')) {<!-- -->
          placeholder = row.label ? `Please select ${<!-- -->row.label}` : `Please select`
        } else if (!row.comp.includes('t-antd-date-picker')) {<!-- -->
          placeholder = row.label
        }
      } else {<!-- -->
        placeholder = row.label
      }
      return placeholder
    },
    //Bound related events
    handleEvent(type, val, item) {<!-- -->
      // console.log('component', type, val, item)
      //Remove leading and trailing spaces
      if (this.isTrim & amp; & amp; !item.isTrim & amp; & amp; item.comp.includes('input') & amp; & amp; item.type !== 'password' & amp; & amp; item.type !== 'inputNumber') {<!-- -->
        this.formOpts.formData[item.value] = this.formOpts.formData[item.value].trim()
      }
      this.$emit('handleEvent', type, val)
    },
    validate() {<!-- -->
      // selfValidate() {<!-- -->
      return new Promise((resolve, reject) => {<!-- -->
        this.$refs.form.validate(valid => {<!-- -->
          if (valid) {<!-- -->
            resolve({<!-- -->
              valid,
              formData: this.formOpts.formData
            })
          } else {<!-- -->
            // eslint-disable-next-line prefer-promise-reject-errors
            reject({<!-- -->
              valid,
              formData: null
            })
          }
        })
      })
    },
    //Reset form
    resetFields() {<!-- -->
      return this.$refs.form.resetFields()
    },
    // Clear verification
    clearValidate() {<!-- -->
      return this.$refs.form.clearValidate()
    }
  }
}
</script>
<style lang="scss">
.t_antd_form {<!-- -->
  display: flex;
  flex-wrap: wrap;
  .ant-select,
  .ant-calendar-picker {<!-- -->
    width: 100%;
  }
  .footer_btn {<!-- -->
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 5px;
    width: 100%;
    .ant-btn {<!-- -->
      margin-left: 10px;
    }
    .ant-btn:first-child {<!-- -->
      margin-left: 0;
    }
  }
}
</style>


4. Component address

gitHub component address

gitee code cloud component address

5. Related articles

Encapsulate basic component documents again based on ElementUi

Encapsulate basic component documents again based on ant-design-vue

vue3 + ts re-encapsulates basic component documents based on Element-plus