设计思路及布局结构

表单编辑器从布局上一般分为三个部分,参照上图,左侧是可以使用的组件,中间是组件面板,右侧是具体某个组件的配置属性。
Authors
朱森林-W9057201
技术预研选型
在开发前,先在网上进行了一些调研,找了几个表单编辑器做参考,下面是自己找到的几个比较成熟的表单编辑器:
由于这些表单编辑器都只是demo,并没有考虑具体的业务场景,产品需求有很多定制化的内容(比如定制化样式,组件通信等)无法支持,所以只是参考了这几个成熟的开源库的设计思想,为自己后面的实现提供了一些思路。
首先需要找一个拖拽的组件,参考了几个开源库,最终选择了 vue-draggable 这个开源库,它是基于SortableJs进行封装的,vue-draggable支持分组拖拽、禁用拖拽、设置拖拽节点样式等功能,刚好符合项目的业务功能需求。
其次要选择UI组件库,我选择了比较常用的 Element UI 组件库,这也是Vue写PC端项目常用的UI组件库。
技术栈
Client: Vue、Vue-Router、Element UI、VueDraggable
第三方库
- Vue:cn.vuejs.org/v2/guide/in…
- Vue-Router:router.vuejs.org/zh/api
- Element UI:element.eleme.cn/#/zh-CN/com…
- VueDraggable:github.com/SortableJS/…
安装、运行及部署构建步骤
Install grid-plus with npm
# 安装依赖(第一次生成项目后)
npm install
# 开始本地开发,通过webpack-dev-server实时编译代码
npm run dev
# 构建测试环境的代码
npm run build:test
# 构建生产环境的代码
npm run build:prod
功能架构及设计思想
动态表单编辑器算是比较复杂的组件,所以需要一开始就要仔细考虑好整体的设计。我希望这个表单编辑器是易扩展、高可用、可维护性高的,所以基本上所有内容都是配置项组成,而不是写死的。

整个表单设计器就是一个Vue页面,要实现的功能主要包括:
- 左侧组件列表的显示
- 拖拽新增组件、删除组件
- 组件支持交换位置
- 组件可以设置宽度,独占一行或一行两列
- 右侧显示对应组件的属性配置,修改配置能实时更新显示效果
- 组件字段配置项可以设置是否必填
- 组件间可以进行数据交互
- 组件绑定元数据
结构剖析及核心代码逻辑
首先看左侧的组件面板,左侧的列表数据是由组件配置项动态渲染,组件配置的数据存在一个js文件里,数据格式是多个组件配置项组成的数组,通过读取文件配置,显示文件中的组件列表,一个输入框组件的配置项如下:
{
name: '输入框',
icon: 'text-box', // 组件图标名
type: 'base',
componentName: 'input', // 组件名
componentProps: { // 组件属性
label: '输入框', // 标签名
type: 'text',
placeholder: '请输入内容',
value: '',
required: false, // 是否必填
multiple: false, // 是否多选
alone: false // 是否占一行
},
componentForm: [ // 右侧面板组件列表
{
name: '名称',
componentName: 'option-input',
componentProps: {
bindKey: 'label',
placeholder: '请输入名称',
required: true
},
},
...
]
}


所有组件配置项的格式都是相同的,介绍一下里面几个重要的字段
| name | 组件名称 |
|---|---|
| icon | 组件图标名,根据名称显示对应的图标 |
| componentName | 组件名称,组件动态渲染时候component的is属性,标识是哪个组件,pc端会加上“pc-”前缀,移动端会加上“mb-”前缀 |
| componentProps | 组件属性props,这里的数据都会传给定义的组件 |
| componentForm | 组件右侧配置项,数组类型,每一项都对应组件的一个属性,比如是否必填,组件的配置项也是通过动态渲染生成,修改属性视图会实时更新 |
左侧面板只需要把组件循环遍历显示即可,componentData就是从文件中读取的数据,参考下图:



Draggable是vue-draggable提供的组件,这里不详细介绍 vue-draggable 使用,感兴趣可以看看它的文档,使用十分简单。下面是中间面板的部分代码,分组内的组件拖动本身已经支持,两个Draggable的group属性相同组件即可在两个分组间拖动,通过clone属性,我们可以自定义拖动到右侧时,添加的数据内容,我们这里的内容就是拖动组件的配置项。

当组件从左边拖动到右边时,右边面板v-model绑定的数组对象(上面代码中的contentList)会添加一项对应组件的配置项(需要深拷贝),拖拽修改组件顺序,绑定的数组里的顺序也会自动变化,最终保存表单数据的时候,保存这个数组即可。
Draggable拖拽时,基础/人事字段组件(注意拖动的元素都是根元素)里面的内容通过component标签动态渲染组件,渲染的组件由配置项里的componentName决定,比如输入框的componentName为input,这里渲染为cp-input标签。
接下来只需要把cp-input组件的代码写好就可以使用了,代码参考下图:

上面props里的data就是配置项中componentProps,props里的prop和Element UI的表单校验相关联。再列一下输入框的componentProps对象属性。
配置项中的componentProps的所有属性会传给组件做初始化值,组件根据这些属性做相应的逻辑处理,比如里面required属性,传true/false代表该项必填/非必填;再比如mulitple,代表显示单行输入框还是多行输入框,这里面的值可以通过右侧面板动态进行修改。
右侧面板显示的内容是根据组件配置项里的componentForm数据动态渲染而成的,这里可能有人会有疑问:为什么把组件配置项也做成动态配置的。虽然技术预研时参考的几个表单设计器方案都是把所有的组件配置写到一个vue文件中,再通过传入文件类型决定要显示哪些配置项,但这会造成所有逻辑耦合在一个文件里,代码里有很多v-if,不同配置项的判断逻辑全部都在一个文件,造成整个文件很臃肿并且很不直观,所以采用动态配置的方式,如下图:

其实和中间的组件面板类似的,仔细观察上面的componentForm里的每一项配置项和外层的组件配置项长的差不多,也有componentName和componentProps属性。当我们选中一个组件的时候,右侧面板会根据组件的componentForm属性动态渲染出它的配置项,如下图:
注意componentProps里有一个非常重要的属性bindKey,这就是它所关联的组件配置项,这个值对应组件componentProps的某一个key,修改它会同步修改componentProps对应key的值,以达到实时同步的目的,如下图:

当然这样比较麻烦的一点是每类配置项都需要在configComponent目录单独写一个Vue组件,但是这样的好处是:每个组件自己能处理自己相应的逻辑,不需要和其它配置项耦合。比如“名称”配置项需要限制输入字符个数和加上必填的逻辑,单独的文件可以自己把这些逻辑写进去,通过传入配置项的方式控制。
组件内写好配置项需要的功能,然后在componentProps里加上想要属性即可,比如想要设置成必填就在加上componentProps里加上required:true。组件根据外部传参实现不同逻辑,这样逻辑清晰,不与其它模块耦合;并且调整配置项显示的上下位置也很方便,只需要在componentForm里改对象数组的位置即可。
到此表单编辑页已经基本完成,当我们完成表单的自定义后,点击保存会将当前表单的所有的组件配置项数据,请求接口并落库,后面在使用表单时候也是同样通过请求表单详情接口,拿到表单数据,把整个表单动态渲染显示出来。
最终成品Demo示例
结语
整体设计思想介绍完了,到此自定义表单的所有功能已经介绍完毕,有不对的地方欢迎指出~