EasyForm的组件库分为两类,第一类是容器组件,主要负责表单元素的布局排列,第二类为字段组件,主要负责数据收集或内容展示。容器组件可以嵌套其他容器或字段组件,字段组件可以放置于容器之内或者表单上(即整个表单也相当于一个大容器)。
一、容器组件开发
本节通过开发一个简单的card卡片容器讲解如何实现容器组件的扩展开发。
- 首先拉取EasyForm源码:
Gitlab: http://loan.git.com.cn/gitlab/sadp/easy-low-code/easy-form-pro
VS Code导入源码项目,并执行 npm install 安装依赖包,安装依赖完成之后执行 npm run serve 进入开发模式;
打开 src/extension/sample/extension-schema.js,可看到card容器定义的JSON Schema如下:
export const cardSchema = {type: 'card',category: 'container',icon: 'card',widgetList: [],options: {name: '',label: 'card',hidden: false,cardWidth: '100%', //卡片宽度,百分比或像素宽度shadow: 'never', //是否显示阴影customClass: '', //自定义css样式}}
JSON Schema解释说明:
- type:容器组件的类型名称,必须唯一,不能跟已有组件重复;
- icon:容器图标名称,可以去iconfont.cn下载所需的svg文件,放入src/icons/svg目录即可(自动加载);
- category:容器组件必须设置为’container’;
- widgetList:容器内存放的组件列表,初始值必须为[];
- options:组件属性对象,每一个属性值对应一个属性编辑器。
- 编写card容器的SFC组件文件,容器组件对应两个状态:设计期状态可以接收组件拖拽,运行期状态负责组件渲染,所以需要编写两个SFC文件,命名规则需严格遵守:容器名称-widget(设计期)、容器名称-item(运行期);
4.1 card-widget.vue代码如下:
<template><container-wrapper :designer="designer" :widget="widget" :parent-widget="parentWidget" :parent-list="parentList":index-of-parent-list="indexOfParentList"><el-card :key="widget.id" class="card-container" @click.native.stop="selectWidget(widget)":shadow="widget.options.shadow" :style="{width: widget.options.cardWidth + '!important' || ''}":class="[selected ? 'selected' : '', customClass]"><div slot="header"><span>{{widget.options.label}}</span></div><draggable :list="widget.widgetList" v-bind="{group:'dragGroup', ghostClass: 'ghost',animation: 200}"handle=".drag-handler"@add="(evt) => onContainerDragAdd(evt, widget.widgetList)"@update="onContainerDragUpdate" :move="checkContainerMove"><transition-group name="fade" tag="div" class="form-widget-list"><template v-for="(subWidget, swIdx) in widget.widgetList"><template v-if="'container' === subWidget.category"><component :is="subWidget.type + '-widget'" :widget="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList":index-of-parent-list="swIdx" :parent-widget="widget"></component></template><template v-else><component :is="subWidget.type + '-widget'" :field="subWidget" :designer="designer" :key="subWidget.id" :parent-list="widget.widgetList":index-of-parent-list="swIdx" :parent-widget="widget" :design-state="true"></component></template></template></transition-group></draggable></el-card></container-wrapper></template>
封装了Element UI的el-card组件,重点看4~7行代码,如何跟JSON Schema的属性进行结合,其他部分代码复制粘贴即可。
4.2 card-item.vue代码如下:
<template><container-item-wrapper><el-card :key="widget.id" class="card-container" :class="[customClass]":shadow="widget.options.shadow" :style="{width: widget.options.cardWidth + '!important' || ''}":ref="widget.id" v-show="!widget.options.hidden"><div slot="header"><span>{{widget.options.label}}</span></div><template v-if="!!widget.widgetList && (widget.widgetList.length > 0)"><template v-for="(subWidget, swIdx) in widget.widgetList"><template v-if="'container' === subWidget.category"><component :is="subWidget.type + '-item'" :widget="subWidget" :key="swIdx" :parent-list="widget.widgetList":index-of-parent-list="swIdx" :parent-widget="widget"></component></template><template v-else><component :is="subWidget.type + '-widget'" :field="subWidget" :designer="null" :key="swIdx" :parent-list="widget.widgetList":index-of-parent-list="swIdx" :parent-widget="widget"></component></template></template></template></el-card></container-item-wrapper></template>
重点看3-6行代码,其他部分代码复制粘贴即可。
- 加载options对应的属性编辑器,对应表单设计器右侧的SettingPanel面板,具体代码参见extension-loader.js:
PERegister.registerCPEditor('cardWidth', 'card-cardWidth-editor',PEFactory.createInputTextEditor('cardWidth', 'extension.setting.cardWidth'))let shadowOptions = [{label: 'never', value: 'never'},{label: 'hover', value: 'hover'},{label: 'always', value: 'always'},]PERegister.registerCPEditor('shadow', 'card-shadow-editor',PEFactory.createSelectEditor('shadow', 'extension.setting.cardShadow',{optionItems: shadowOptions}))
代码解释: 此处只加载了cardWidth、shadow两个属性的编辑器组件,因为其他属性编辑器在VForm框架内已经预先加载了。以上使用到的字符串资源分别位于src\lang\zh-CN_extension.js、src\lang\en-US_extension.js两个文件中。
二、字段组件开发
本节通过开发一个简单的alert组件讲解如何实现字段组件、静态内容组件的扩展开发。
步骤1、2同上。
- 打开 src/extension/sample/extension-schema.js,可看到alert组件定义的JSON Schema如下:
export const alertSchema = {type: 'alert',icon: 'alert',formItemFlag: false, //是否需嵌套于el-form-itemoptions: {name: '',title: 'Good things are coming...',type: 'info',description: '',closable: true,closeText: '',center: true,showIcon: false,effect: 'light',hidden: false,onClose: '',customClass: '',}}
JSON Schema解释说明:
- type:字段组件的类型名称,必须唯一,不能跟已有组件重复;
- icon:容器图标名称,可以去iconfont.cn下载所需的svg文件,放入src/icons/svg目录即可(自动加载);
- formItemFlag:是否嵌套于el-form-item组件内,因el-alert并不需要显示字段标签,故此处设置为false;
- options:组件属性对象,每一个属性值对应一个属性编辑器。
编写字段组件的SFC文件,字段组件在设计期和运行期共用,故只需要编写一个SFC文件,命名规则需严格遵守:组件名称-widget。
4.1 alert-widget.vue代码如下:
<template><static-content-wrapper :designer="designer" :field="field" :design-state="designState":parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList":sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId"><el-alert ref="fieldEditor" :title="field.options.title" :type="field.options.type":description="field.options.description" :closable="field.options.closable":center="field.options.center" :close-text="field.options.closeText":show-icon="field.options.showIcon" :effect="field.options.effect" @close="handelCloseCustomEvent"></el-alert></static-content-wrapper></template><script>//...methods: {handelCloseCustomEvent() {if (!!this.field.options.onClose) {let closeFn = new Function(this.field.options.onClose)closeFn.call(this)}}}//...</script>
因为formItemFlag设置为false,故此处必须使用static-content-wrapper包裹组件(如formItemFlag设置为true,则应使用form-item-wrapper包裹组件),5~8行代码对应options属性值的动态绑定,handelCloseCustomEvent方法处理了自定义事件onClose交互逻辑。
- 加载options对应的属性编辑器,对应表单设计器右侧的SettingPanel面板,具体代码参见extension-loader.js:
PERegister.registerCPEditor('title', 'alert-title-editor',PEFactory.createInputTextEditor('title', 'extension.setting.alertTitle'))let typeOptions = [{label: 'success', value: 'success'},{label: 'warning', value: 'warning'},{label: 'info', value: 'info'},{label: 'error', value: 'error'},]PERegister.registerCPEditor('type', 'alert-type-editor',PEFactory.createSelectEditor('type', 'extension.setting.alertType',{optionItems: typeOptions}))PERegister.registerCPEditor('description', 'alert-description-editor',PEFactory.createInputTextEditor('description', 'extension.setting.description'))PERegister.registerCPEditor('closable', 'alert-closable-editor',PEFactory.createBooleanEditor('closable', 'extension.setting.closable'))PERegister.registerCPEditor('closeText', 'alert-closeText-editor',PEFactory.createInputTextEditor('closeText', 'extension.setting.closeText'))PERegister.registerCPEditor('center', 'alert-center-editor',PEFactory.createBooleanEditor('center', 'extension.setting.center'))PERegister.registerCPEditor('showIcon', 'alert-showIcon-editor',PEFactory.createBooleanEditor('showIcon', 'extension.setting.showIcon'))let effectOptions = [{label: 'light', value: 'light'},{label: 'dark', value: 'dark'},]PERegister.registerCPEditor('effect', 'alert-effect-editor',PEFactory.createRadioButtonGroupEditor('effect', 'extension.setting.effect',{optionItems: effectOptions}))PERegister.registerEPEditor('onClose', 'alert-onClose-editor',PEFactory.createEventHandlerEditor('onClose', []))
上述代码了共加载了9个属性编辑器,其中包含onClose事件编辑器,其他属性使用预先加载的默认属性编辑器。以上使用到的字符串资源分别位于src\lang\zh-CN_extension.js、src\lang\en-US_extension.js两个文件中。
- 字段组件开发完毕,扩展组件的加载由extension-loader.js的loadExtension()方法执行,该方法在main.js中被调用。
三、SFC代码生成
如果你最终使用VFormRender渲染表单JSON,则此部分内容完全可以跳过,无须实现该功能。
- 编写容器组件SFC生成器方法
实现思路很简单,根据容器的JSON配置生成对应的Element UI组件模板代码,打开src\extension\samples\extension-sfc-generator.js,可看到card容器的SFC生成器方法如下:
export const cardTemplateGenerator = function (cw, formConfig) {const wop = cw.optionsconst headerAttr = `header="${wop.label}"`const classAttr = buildClassAttr(cw)const styleAttr = !!wop.cardWidth ? `style="{width: ${wop.cardWidth} !important}"` : ''const shadowAttr = `shadow="${wop.shadow}"`const vShowAttr = !!wop.hidden ? `v-show="false"` : ''const cardTemplate =`<div class="card-container"><el-card ${headerAttr} ${classAttr} ${styleAttr} ${shadowAttr} ${vShowAttr}>${cw.widgetList.map(wItem => {if (wItem.category === 'container') {return buildContainerWidget(wItem, formConfig)} else {return buildFieldWidget(wItem, formConfig)}}).join('')}</el-card></div>`return cardTemplate}
SFC代码生成大量使用了ES6的模板字符串功能,极大地简化了代码生成逻辑。
- 编写字段组件SFC生成器方法
打开src\extension\samples\extension-sfc-generator.js,可看到alert组件的SFC生成器方法如下:
export const alertTemplateGenerator = function(fw, formConfig) {const wop = fw.optionsconst titleAttr = `title="${wop.title}"`const typeAttr = `type=${wop.type}`const descriptionAttr = !!wop.description ? `description="${wop.description}"` : ''const closableAttr = `:closable="${wop.closable}"`const closeTextAttr = !!wop.closeText ? `close-text="${wop.closeText}"` : ''const centerAttr = `:center="${wop.center}"`const showIconAttr = `:show-icon="${wop.showIcon}"`const effectAttr = `effect="${wop.effect}"`const alertTemplate =`<el-alert ${titleAttr} ${typeAttr} ${descriptionAttr} ${closableAttr} ${closeTextAttr} ${centerAttr}${showIconAttr} ${effectAttr}></el-alert>`return alertTemplate}
实现思路同上,根据组件的JSON配置生成相应的Element UI组件模板代码。
- 注册上述两种代码生成器
在extension-loader.js中注册SFC代码生成器,每个代码生成器都需注册。
// ...registerCWGenerator('card', cardTemplateGenerator) //注册card容器组件的代码生成器// ...// ...registerFWGenerator('alert', alertTemplateGenerator) //注册alert组件的代码生成器// ...
以上内容已完结。
