V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chenquincy
V2EX  ›  Vue.js

支持嵌套对象、多级数组的 Vue 动态多级表单组件 —— vue-dynamic-form-component

  •  
  •   chenquincy ·
    chenquincy · 2019-06-25 11:42:15 +08:00 · 3357 次点击
    这是一个创建于 1739 天前的主题,其中的信息可能已经有所发展或是发生改变。

    方便不想看完全篇文章的童鞋,简单总结一下,这是篇软广,主要是推广自己在业务中沉淀的一个开源组件 vue-dynamic-form-component 。基于 element-ui 实现的 vue 组件,只需编写类似 async-validator 的规则,自动生成对应的表单,支持常见输入类型的同时,支持嵌套对象、hashmap、多维数组等复杂类型。有需要的童鞋欢迎使用和贡献代码,顺便给个 star(我也不知道为什么字体自动加黑了,不关我事)

    前言

    几个月前,我在 github 开源了一个前端解析手机应用安装包(IPAAPK 文件)信息的工具 app-info-parser ,算是第一次正儿八经的做开源这件事,之后就有了半夜三四点回复 issue、修 bug 的体验,说实话,上完班还要处理 issue 是挺累的,但也是乐在其中。正所谓开源一时爽,一直开源一直爽。

    对于程序员而言,最不喜欢的事情,除了和产品经理 ~~吵架~~ (交流,是交流,不是吵架,要 peace )外,估计就是一直做重复的事情了。在程序界,有相当一部分开源工具都是为了把人从重复的事情中解放出来,去做更有趣、更能体现个人价值的事情。比如 ~~AI 智能回复老婆消息~~ (请勿随意尝试,老婆没了我不负责)。

    之前开源的工具 app-info-parser 是减少重复工作,提高生产效率,接下来的主角 vue-dynamic-form-component 也是如此。

    先贴一个展示大概功能的 gif,动图有点大,如果加载不出来的话可以到 组件首页 查看。

    左边是你需要编写的主要代码,右边是对应生成的表单。

    vue-dynamic-form-component.gif

    背景( Why )

    为什么要做这个组件?其实在前言中已经提到:因为不想一直做重复、没有技术含量的事情

    对于本篇文章而言,这件重复、没有技术含量的事情就是:简单的表单代码

    我所在的小组主要负责公司的公共服务系统搭建及维护,随之而来的便是一套接一套的CURD系统,目前业界已经存在很多优秀的 UI 库,比如为 Vue 而生的 element-ui, iView,基于 reactant-design 等,已经在很大程度上提高了 PC 管理系统的开发效率,减少了很多重复工作。

    但是对于表单功能,UI 库出于通用性的考虑,实际使用中,对于简单的数据对象,我们仍然需要编写大量的表单代码来实现,因此出现了很多优秀的动态表单组件,比如 vue-form-generatorvue-form-making

    那么为什么我还要再造一个类似的轮子?这其实要结合组内的技术栈来说:

    由于组内的人员配置问题:前端 1 人(没错,就是孤独的我)、后端 8 人+,在技术栈上,选用了后端同事相对容易上手的 Vue ,基于 element-ui 开发管理系统。而目前已有的动态表单组件存在以下不适用的问题:

    • vue-form-generator:设计思想很好,但是组件样式比较 old school,同时对多级对象、多维数组等复杂数据支持不是很好,需要自己实现 field 组件,使用成本较高
    • vue-form-making:也是基于element-ui, 样式统一,但是基于组件类型生成表单的方式不够灵活,只能利用已存在的输入组件,因此不支持多级对象等复杂类型

    其他的组件相对而言存在更多的问题,就不一一列举了。

    以上就是为什么我会想要再造一个 Vue 的动态表单轮子,其实里面就已经包含了接下来我们要讲的: vue-dynamic-form-component 有什么作用?

    功能( What )

    动态生成表单

    基于 async-validator 的规则来生成表单,只需要编写简单的声明配置,即可自动生成表单,只需要数据类型,无需关注数据类型对应何种输入组件

    <template>
      <dynamic-form
        v-model="data"
        :descriptors="descriptors">
      </dynamic-form>
    </template>
    
    export default {
      data () {
        return {
          data: {},
          descriptors: {
           	date: { type: 'date', label: 'date \'s label', required: false },
            number: { type: 'number', label: 'number \'s label', required: true, placeholder: 'please input the number' },
            string: { type: 'string', label: 'string \'s label', required: true, pattern: /^test$/g },
            url: { type: 'url', label: 'url \'s label', required: true, placeholder: 'please input the url' },
            email: { type: 'email', label: 'email \'s label', required: false },
            enum: { type: 'enum', label: 'enum\'s label', enum: ['value-1', 'value-2'] }   
          } 
        }
      }
    }
    

    demo

    如果是直接使用 element-ui,除了长得多的 html 代码,还需要编写对应的 rule 以供表单验证,对应的代码量要多得多。

    支持嵌套对象 /Hashmap/多维数组

    使用 element-ui 编写表单时,为了支持嵌套对象等复杂类型,我们可以在 el-form-item 中再添加一个 el-form 或者 el-form-item

    但是,如果有更好的选择,你真的愿意一遍一遍的写这样的代码吗?

    <!-- 省略 data,rules 等代码 -->
    <el-form label-width="120px" :model="data" :rules="rules">
      <el-form-item prop="user" label="用户">
        <el-form :model="data.user">
          <el-form-item label="名字" prop="name">
            <el-input v-model="data.user.name" placeholder="只允许英文、数字"></el-input>
          </el-form-item>
        </el-form>
      </el-form-item>
    </el-form>
    

    要注意,以上仅仅只是一个输入框(已省略 data 代码,为了美观还需要额外的样式代码),就需要写这么多代码,能忍?谁爱忍谁忍,我不忍了。看看如果是使用 vue-dynamic-form-component, 我们需要做什么

    // 只展示 descriptors 代码,其他无需变更
    {
      user: {
        type: 'object',
        fields: {
          name: { type: 'string', label: '名字', placeholder: '只允许英文、数字' }
        }
      }
    }
    

    并且, vue-dynamic-form-component 还对多级表单做了样式优化,自动加深子表单的背景色(初始背景色、颜色偏移量可配置),方便区分:

    demo

    同时,针对 hashmap / array 等复杂数据类型实现了对应的交互逻辑,提供添加、删除等操作:

    demo

    看完这个你确定你还想再回去写前面的代码?(等我不干程序员了我就去当推销员-。-)

    使用( How )

    假如你心动了,那么,心动不如行动(自我嘲讽:好老土的说法),赶紧用起来,把自己从繁琐无聊的表单代码中解放出来。

    具体用法请查阅组件文档: https://vue-dynamic-form.quincychen.cn/

    别忘了先去 github 点个 star 哦(~~你以为这里就不加粗了吗,不存在的兄 dei~~)

    最后

    不要拘泥于业务开发,善于(或者说要热衷于)寻找业务 /生活中存在的待解决问题,然后干掉它,这才是真正体现你价值的地方。

    共勉~

    10 条回复    2019-06-27 10:47:05 +08:00
    renmu
        1
    renmu  
       2019-06-25 11:55:44 +08:00 via Android
    github 中文档的链接加载失败
    chenquincy
        2
    chenquincy  
    OP
       2019-06-25 15:15:51 +08:00
    @renmu 已修复,感谢反馈
    mzsongyan
        3
    mzsongyan  
       2019-06-25 16:15:57 +08:00
    已 star,接下来项目中试一下,看起来确实方便不少
    tuutoo
        4
    tuutoo  
       2019-06-25 17:17:14 +08:00
    厉害厉害
    chenquincy
        5
    chenquincy  
    OP
       2019-06-25 19:03:17 +08:00
    @mzsongyan 感谢支持,有问题的话随时提 issue,也欢迎提 PR
    puzzle9
        6
    puzzle9  
       2019-06-26 07:48:07 +08:00 via Android
    支持支持
    waiaan
        7
    waiaan  
       2019-06-26 10:12:55 +08:00
    曾经写过一个,不过有个需求不知道 lz 怎么解决,就是某组表单项要根据另一个表单项来显示或隐藏。
    chenquincy
        8
    chenquincy  
    OP
       2019-06-26 11:31:14 +08:00
    @waiaan 这个其实并不难,添加一个 hidden 属性,至于根据某个表单项隐藏另一个表单项是比较外部的逻辑,所以用户自己对 data 添加 watch 去修改 descriptor 的 hidden 值即可。hidden 这个功能我会考虑加上~
    waiaan
        9
    waiaan  
       2019-06-26 11:39:23 +08:00
    @chenquincy 做好了 @我一下,学习学习,谢谢。
    chenquincy
        10
    chenquincy  
    OP
       2019-06-27 10:47:05 +08:00
    @waiaan hidden 属性已经加上,显示 /隐藏的逻辑只需要为字段值加个 watch 修改 hidden 即可
    ``` js
    watch: {
    'data.string' (value) {
    if (value) {
    this.descriptors.object.fields.string.hidden = true
    }
    }
    }
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2869 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 13:10 · PVG 21:10 · LAX 06:10 · JFK 09:10
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.