支持嵌套对象、多级数组的 Vue 动态多级表单组件 vue-dynamic-form-component - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
chenquincy
V2EX    Vue.js

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

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

    方便不想看完全篇文章的童鞋,简单总结一下,这是篇软广,主要是推广自己在业务中沉淀的一个开源组件 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     Solana     1421 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 16:49 PVG 00:49 LAX 08:49 JFK 11:49
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86