vue2+ant-design-vue a-form-model组件二次封装(form表单组件)FormModel 表单

一、效果图

二、参数配置

1、代码示例

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

2. 配置参数继承FormModel的所有属性

参数 说明 类型 默认值
refObj form 表单校验规则方法 (可以参考 antd FormModel 表单方法中的 validate) obj -
className 自定义类名 String -
layout 改变表单项 label 与输入框的布局方式(默认:horizontal) /vertical String ‘horizontal’
widthSize 每行显示几个输入项(默认两项) 最大值 4 Number 2
isTrim 全局是否开启清除前后空格(comp 为 a-input 且 type 不等于’password’) Boolean true
formOpts 表单配置项 Object {}
—listTypeInfo 下拉选择数据源(type:'select’有效) Object {}
—fieldList form 表单每项 list Array []
------isHideItem 某一项不显示 Boolean false
------slotName 自定义表单某一项输入框 slot -
------childSlotName 自定义表单某一下拉选择项子组件插槽(a-select-option) slot -
------comp form 表单每一项组件是输入框还是下拉选择等(可使用第三方 UI 如 a-select/a-input 也可以使用自定义组件) String -
------formItemBind 表单每一项属性(继承FormModelItem的 Attributes) Object {}
------bind 表单每一项属性(继承第三方 UI 的 Attributes,如 a-input 中的 allowClear 清空功能)默认清空及下拉过滤 Object {}
------isTrim 是否不清除前后空格(comp 为 a-input 且 type 不等于’password’) Boolean false
------type form 表单每一项类型 String -
------widthSize form 表单某一项所占比例(如果占一整行则设置 1) Number 2
------width form 表单某一项所占实际宽度 String 100%
------arrLabel type=select-arr 时,每个下拉显示的中文 String ‘label’
------arrKey type=select-arr 时,每个下拉显示的中文传后台的数字 String ‘value’
------label form 表单每一项 title String -
------labelRender 自定义某一项 title function -
------value form 表单每一项传给后台的参数 String -
------rules 每一项输入框的表单校验规则 Object/Array -
------list 下拉选择数据源(仅仅对 type:'select’有效) String -
------event 表单每一项事件标志(handleEvent 事件) String -
------eventHandle 继承 comp 组件的事件(返回两个参数,第一个自己自带,第二个 formOpts) Object -
------isSelfCom 是否使用自己封装的组件(TAntdSelect等—含有下拉框) Boolean false
—formData 表单提交数据(对应 fieldList 每一项的 value 值) Object -
—labelCol label 宽度({ span:2}) Object {span:2}
—wrapperCol 输入框 宽度 Object {span:22}
—rules 规则(可依据 AntdUI FormModel 配置————对应 formData 的值) Object/Array -
—operatorList 操作按钮 list Array -

3. events继承FormModel的所有事件

事件名 说明 返回值
handleEvent 单个查询条件触发事件 fieldList 中 type/查询条件输入的值/fieldList 中 event 值

4. Methods

事件名 说明 参数
resetFields 重置表单 -
clearValidate 清空校验 -

5. 关于 Ant-Design-Vue FormModel/FormModelItem 提供的一些属性可直接使用,无需其他配置

三、源码

<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}"
      >
        
        <template #label v-if="item.labelRender">
          <render-comp :createElementFunc="item.labelRender" />
        template>
        
        <template v-if="item.slotName">
          <slot :name="item.slotName">slot>
        template>
        
        <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&&!item.textShow&&!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>
    
    <div class="footer_btn">
      <template v-if="formOpts.btnSlotName">
        <slot :name="formOpts.btnSlotName">slot>
      template>
      <template v-if="!formOpts.btnSlotName&&formOpts.operatorList&&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: {
    /** 表单配置项说明
     * formData object 表单提交数据
     * rules object 验证规则
     * fieldList Array 表单渲染数据
     * operatorList Array 操作按钮list
     * listTypeInfo object 下拉选项数据
     */
    formOpts: {
      type: Object,
      default: () => ({})
    },
    // 自定义类名
    className: {
      type: String
    },
    // 一行显示几个输入项;最大值4
    widthSize: {
      type: Number,
      default: 2,
      validator: (value) => {
        return value <= 4
      }
    },
    // 全局是否开启清除前后空格
    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] && event[v](e, ids, arguments)
            } else {
              if ((typeof e === 'number' && e === 0) || e) {
                event[v] && event[v](e, this.formOpts, arguments)
              } else {
                event[v] && event[v](this.formOpts, arguments)
              }
            }
          }
        })
        return { ...changeEvent }
      }
    },
    selectListType() {
      return ({ list }) => {
        if (this.formOpts.listTypeInfo) {
          return this.formOpts.listTypeInfo[list]
        } else {
          return []
        }
      }
    },
    // 子组件名称
    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'
        }
      }
    },
    // 子子组件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
        }
      }
    },
    // 子子组件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
        }
      }
    },
    // 子子组件文字展示
    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) {
        // 将form实例返回到父级
        this.$emit('update:refObj', this.$refs.form)
      },
      deep: true // 深度监听
    },
    widthSize(val) {
      if (val > 4) {
        this.$message.warning('widthSize值不能大于4!')
        this.colSize = 4
      } else {
        this.colSize = val
      }
    }
  },
  mounted() {
    // 将form实例返回到父级
    this.$emit('update:refObj', this.$refs.form)
  },

  methods: {
    // label与输入框的布局方式
    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)}%;`
      }
    },
    // 得到placeholder的显示
    getPlaceholder(row) {
      let placeholder
      if (typeof row.comp === 'string' && row.comp) {
        if (row.comp.includes('input')) {
          placeholder = row.label ? `请输入${row.label}` : `请输入`
        } else if (row.comp.includes('select') || row.comp.includes('cascader')) {
          placeholder = row.label ? `请选择${row.label}` : `请选择`
        } else if (!row.comp.includes('t-antd-date-picker')) {
          placeholder = row.label
        }
      } else {
        placeholder = row.label
      }
      return placeholder
    },
    // 绑定的相关事件
    handleEvent(type, val, item) {
      // console.log('组件', type, val, item)
      // 去除前后空格
      if (this.isTrim && !item.isTrim && item.comp.includes('input') && item.type !== 'password' && 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
            })
          }
        })
      })
    },
    // 重置表单
    resetFields() {
      return this.$refs.form.resetFields()
    },
    // 清空校验
    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>


四、组件地址

gitHub组件地址

gitee码云组件地址

五、相关文章

基于ElementUi再次封装基础组件文档


基于ant-design-vue再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档

你可能感兴趣的:(vue.js,ant-design-vue,FormModel,a-form-model,表单组件,基础组件,组件封装)