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"
/>

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

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

FormModel_54">3. events继承FormModel的所有事件

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

4. Methods

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

FormModelFormModelItem__68">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}"
      >
        <!-- 自定义label -->
        <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再次封装基础组件文档


http://www.niftyadmin.cn/n/5140972.html

相关文章

STM32-电源管理(实现低功耗)

电源管理 STM32 HAL库对电源管理提供了完善的函数和命令。 工作模式&#xff08;高功耗->低功耗&#xff09;&#xff1a;运行、睡眠、停止、待机。 若备份域电源正常供电&#xff0c;备份域内的RTC都可以正常运行&#xff0c;备份域内的寄存器的数据会被保存&#xff0c;不…

C#学习系列之构造函数

C#学习系列之构造函数 啰嗦派生类及构造函数使用总结 啰嗦 基础学习 派生类及构造函数 调用顺序&#xff1a;实例成员初始化->基类构造函数->派生类构造函数。 隐式调用和显式调用&#xff1a;一般在后加**&#xff1a;xxx&#xff08;&#xff09;** 调用当前类的其他…

UE5——网络——RPC

RPC&#xff08;这个是官方文档的资料&#xff09; 要将一个函数声明为 RPC&#xff0c;您只需将 Server、Client 或 NetMulticast 关键字添加到 UFUNCTION 声明。 例如&#xff0c;若要将某个函数声明为一个要在服务器上调用、但需要在客户端上执行的 RPC&#xff0c;您可以…

干货来袭 只需套用模板就能制作的电子相册的网站

随着科技的不断发展&#xff0c;电子相册已经成为了我们生活中不可或缺的一部分。但是&#xff0c;制作电子相册的过程却往往让人感到头疼。那么&#xff0c;有没有一种简单易用的方法来制作电子相册呢&#xff1f;今天&#xff0c;就给大家介绍一款只需套用模板就能制作的电子…

关于Goby反制上线CS中的各种问题

前言 Goby作为新一代网络安全技术&#xff0c;通过为目标建立完整的资产数据库&#xff0c;实现快速的安全应急&#xff0c;日常为广大师傅提供了便捷的渗透体验。最近有观察到有关于某些蜜罐出现了Goby反制的指纹&#xff0c;顿时就起了兴趣进行研究Goby的反制&#xff0c;期…

实现右键出现菜单选项功能

文章目录 需求分析需求 实现鼠标右键显示菜单的功能 分析 分析该需求,流程如下 写一个 div 作为右键弹出的菜单选项——> 监听鼠标右键事件——> 得到坐标位置——> 在该位置对写好的 菜单选项 进行展示——> 选择完毕后关闭菜单——> 鼠标左键其他位置 点…

C++二叉搜索树模拟实现

目录 一、二叉搜索树的概念 二、二叉搜索树的结构 三、二叉搜索树的操作&#xff08;非递归&#xff09; 1.插入 2.查找 3.删除 4.遍历 四、二叉搜索树的操作&#xff08;递归&#xff09; 1.递归插入 2.递归查找 3.递归删除 4.递归遍历 五、二叉搜索树的默认成员函…

Xshell复制粘贴(Ctrl+C,Ctrl+V)配置

文章目录 Xshell复制粘贴&#xff08;CtrlC&#xff0c;CtrlV&#xff09;配置一、复制二、粘贴 Xshell复制粘贴&#xff08;CtrlC&#xff0c;CtrlV&#xff09;配置 在使用Windows一般使用ctrlc&#xff0c;ctrv实现粘贴复制&#xff0c;通过Xshell连接Linux时&#xff0c;可…