基于Ant Design Vue封装一个表单控件

开源代码

https://github.com/naturefwvue/nf-vue3-ant

有缺点本来是写在最后的,但是博文写的似乎有点太长了,估计大家没时间往下看,于是就把有缺点写在前面了,不喜欢可以先跳过。

缺点

灵活性肯定是没有了,封装的还是有些过度,灵活度大大降低,没有使用slot,想加点啥目前是不可能的,等以后需要了再说,毕竟这个项目才刚刚开始。
(为啥缺点只有一条?那不那啥吗,基于ant design vue封装的,他们都那么强大了,还能有啥缺点?封装后除了失去灵活性还能差啥?)

优点

  1. 简洁,代码很少,做好meta就可以了,另外meta也不需要手写,有个小工具可以辅助创建。
  2. 风格统一,代码就是这样,当需要写新的表单的时候,也不需要复制粘贴,只需要弄个meta就行了,想变风格都变不了。
  3. 可以统一修改升级。UI版本升级了,VUE版本升级了,咋办?改一下组件内部代码即可,调用组件的代码并不需要修改。这样还怕升级了吗?
  4. 可以跨UI,甚至跨框架。之前看了一下element,本来想用的,但是不支持vue3.0只好作罢。element的使用方式也是大同小异,那么我基于element也封装一套组件,保证外部使用方式一致,那么是不是可以做到UI随便切换了呢?
  5. 便于项目升级。项目打包发布后,如果需求有变更,一般修改完后需要重新打包发布。而我们的项目是通过 meta 来控制表单的,也就是说如果有变动,那么改json文件即可,而json可以通过ajax来加载,不用打包到项目里面。

为啥还要封装

ant design vue 都已经提供那么的组件了,还不够用吗?为啥还要折腾

首先antdv 是一个非常强大UI库,提供了很强大的功能和漂亮的UI,使用方面也是非常的灵活,不仅有Form表单,还有各种Data Entry组件,非常灵活。只是鱼和熊掌不能兼得,antdv为了灵活而牺牲了一些简洁性。

select

比如a-select,官网代码如下:(有删减)



首先要设置一些属性,然后还要逐行设置 a-select-option,是不是有点麻烦?

form

再来看一下form的官网示例:(七个字段的简单表单)



在Form表单里面也是这样的设置方式,而表单里面有很多各种各样的控件,一个一个写起来实在是太累。看这样的代码有点眼晕,似乎也不太便于维护,不知道大家是怎么编写和维护的。

大家都知道我很懒,我想用v-for来做表单,这样即使一百个字段也是一个for搞定,这样代码就简单多了。

那么如何实现呢?

如何封装?

vue的思路就是——数据驱动,那么我就把这个思路做的更彻底一点,——让数据驱动dom的属性

统一标签名称

想要for循环,标签必须统一,a-input、a-select等等都不一样,这还怎么循环?所以要先做一个统一的组件,以便于for循环。然后内部再分为多种不同的组件,这样便于维护,要不然代码都写到一起就太乱了。
于是结构就是这样:

结构图
基于Ant Design Vue封装一个表单控件_第1张图片

统一属性

除了标签之外,属性也要一致,否则还是不能for。那么怎么办呢?不同的控件需要的属性都不一样呀,这个好办,我们整合成两个就行

v-model value

这个必须单独拿出来。

meta

其他的属性都统一放在这里,把这个东东传递进去就好,然后内部识别领取自己的属性。这样就搞定了。

代码

我们来看meta的结构。

meta

以input为例,其他都大同小异

props: {
    modelValue: String,
    meta: {
      type: Object,
      default: () => {
        return {
          controlId: Number, // 编号,区别同一个表单里的其他控件
          colName: String, // 字段名称
          controlType: Number, // 用类型编号表示type
          isClear: { // 连续添加时是否恢复默认值
            type: Boolean,
            default: false
          },
          defaultValue: String, // 默认值
          autofocus: { // 是否自动获得焦点
            type: Boolean,
            default: false
          },
          required: { // 必填
            type: Boolean,
            default: true
          },
          disabled: {
            // 是否禁用
            type: Boolean,
            default: false
          },
          readonly: { // 只读
            type: Boolean,
            default: false
          },
          pattern: String, // 用正则做验证。
          placeholder: String,
          title: String, // 提示信息
          maxlength: Number, // 最大字符数
          autocomplete: { // off
            type: String,
            default: 'on'
          }
        }
      }
    }
  },

不同类型的组件,会有所调整。

input

模板部分


先把需要的属性,通过meta都给绑定上

js


这样我们只要做好meta,就可以完全控制控件了。其他控件也是类似的思路,就不一一贴代码了。

form-Item

组件分的太零碎,使用的时候很麻烦,那么怎么办呢?再做个组件整合一下。


很笨的方法,挨个类型判断。这里使用了魔数,大概会被喷,不过早就习惯了。


在这里统一注册各种零散的组件,使用的时候就不用想,到底要用哪种组件了。

表单

好了,准备工作都做好了,我们可以开始for循环了。

找了半天,antdv没有提供单纯的table,只好手动找class了,于是代码变成了这样。

    
{{item.title}}:

代码行数和控件(字段)数量无关。代码数量也和有多少表单无关。

是不是看起来一点都不像一个表单?代码是不是少的有点可怜?
nfInput 控件有两个属性v-model 和 meta,他会根据meta自动创建需要的dom,并且绑定属性。当然实际干活的是vue和antdv,我只是做了一种尝试。


meta,单独的json文件

meta并不需要写在代码里,因为实在是太长了。可以写在单独的json文件里面,这样便于加载。另外也可以做成ajax加载的方式,这样项目发布后,如何需求有变动,需要调整表单的话,那么只需要单独修改json文件即可,不用重新打包发布。

{
    "companyForm":{
        "1000":{
            "controlId": 1000,
            "colName": "companyName",
            "controlType": 101,
            "isClear": true,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "请输入公司名称",
            "title": "公司名称",
            "autocomplete": "on",
            "size": 30,
            "maxlength": 100,
            "optionList": [] 
        },
        "1001":{
            "controlId": 1001,
            "colName": "companyCode",
            "controlType": 131,
            "isClear": true,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "公司邮编",
            "title": "公司邮编",
            "autocomplete": "on",
            "min": 100000,
            "max": 999999,
            "step": 1,
            "maxlength": 6,
            "optionList": [] 
        },
        "1002":{
            "controlId": 1002,
            "colName": "legalPerson",
            "controlType": 101,
            "isClear": true,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "请输入法人姓名",
            "title": "法人",
            "autocomplete": "on",
            "size": 20,
            "maxlength": 50,
            "optionList": [] 
        },
        "1003":{
            "controlId": 1003,
            "colName": "liaisonMan",
            "controlType": 101,
            "isClear": true,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "请输入联系人姓名",
            "title": "联系人",
            "autocomplete": "on",
            "size": 20,
            "maxlength": 50,
            "optionList": []
        },
        "1004": {
            "controlId": "1004",
            "colName": "address",
            "controlType": 101,
            "isClear": true,
            "defaultValue": "",
            "autofocus": false,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "请输入公司地址",
            "title": "公司地址",
            "autocomplete": "on",
            "size": 30,
            "maxlength": 50,
            "optionKey": "",
            "optionList": [
            ]
          },
          "1005": {
            "controlId": "1005",
            "colName": "telphone",
            "controlType": 103,
            "isClear": true,
            "defaultValue": "",
            "autofocus": false,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "请输入公司电话",
            "title": "公司电话",
            "autocomplete": "on",
            "size": 30,
            "maxlength": 50,
            "optionKey": "",
            "optionList": [
            ]
          },
          "1006": {
            "controlId": "1006",
            "colName": "URL",
            "controlType": 105,
            "isClear": true,
            "defaultValue": "",
            "autofocus": false,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "https://www.",
            "title": "公司网址",
            "autocomplete": "on",
            "size": 30,
            "maxlength": 50,
            "optionKey": "",
            "optionList": [
            ]
          },
          "1007": {
            "controlId": "1007",
            "colName": "Email",
            "controlType": 104,
            "isClear": true,
            "defaultValue": "",
            "autofocus": false,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "placeholder": "@",
            "title": "公司邮件",
            "autocomplete": "on",
            "size": 30,
            "maxlength": 50,
            "optionKey": "",
            "optionList": [
            ]
          },
          "1008": {
            "controlId": 1008,
            "colName": "type",
            "title": "公司类型",
            "controlType": 190,
            "isClear": true,
            "defaultValue": "",
            "autofocus": false,
            "disabled": false,
            "required": true,
            "pattern": "",
            "class": "",
            "optionList": [
              { "value": 1, "title": "有限责任公司" },
              { "value": 2, "title": "股份有限责任公司" },
              { "value": 3, "title": "个人独资企业" },
              { "value": 4, "title": "合伙企业" },
              { "value": 5, "title": "个体工商户" }
            ]
          },
          "1009": {
            "controlId": 1009,
            "colName": "createDate",
            "controlType": 140,
            "isClear": true,
            "defaultValue": "",
            "autofocus": false,
            "disabled": false,
            "required": true,
            "readonly": false,
            "pattern": "",
            "class": "",
            "title": "成立日期",
            "min": "1910-01-01",
            "max": "2999-12-31",
            "step": 1
          }
    }
}

数据和代码分离,是不是很完美。

为啥不直接用antdv提供的 Form 表单?

这个嘛,思路不太一样。好吧,其实是官网的代码,在本地还没有调试成功,等研究明白了还是会用的。

你可能感兴趣的:(基于Ant Design Vue封装一个表单控件)