背景
平台需要实现自定义表单功能,作为低代码开发的一部分,通过技术预研和技术选型,选择form-create和form-create-designer这两个组件进行集成作为实现方案。通过深入了解和技术验证,确认了组件的功能能满足需求,具备良好的开放性和扩展性。
上篇对表单构造器的深度了解,今天通过对fc组件的集成,验证下运行模式下的整体效果。
实体视图功能扩展
实体属性扩展
扩展实体视图的属性,新增三个属性,如下图所示:
enableAdvanceConfig:启用高级配置,标识位,用于判断是基于标准布局(自上而下单列显示所有属性),还是基于表单设计器实现的高级布局。增加该属性的主要目的是便于控制模式,可以直接根据该属性判断是否启用了高级配置,而不是通过advanceConfigRule是否为空来判断。
advanceConfigRule:高级配置规则,用于存放fcd组件生成的规则。
advanceConfigOption:高级配置选项,用于存放fcd组件生成的表单选项。
页面调整
相应调整列表查询页面,在查询结果增加“启用高级配置”显示。
相应调整新增、修改、查看页面,增加“启用高级配置”属性。
功能调整
增加了一个服务方法,更新高级配置,接收前端传入的高级配置规则和选项,持久化到库表。
public void updateAdvanceConfig(String id, String advanceConfigRule, String advanceConfigOption) {
EntityView entity=query(id);
entity.setAdvanceConfigRule(advanceConfigRule);
entity.setAdvanceConfigOption(advanceConfigOption);
modify(entity);
}
控制器层新增方法,接收前端调用,调用后端服务。
/**
* 更新高级配置
*/
@PutMapping("/updateAdvanceConfig")
@SystemLog(value = "实体视图-高级配置")
@PreAuthorize("hasPermission(null,'entityconfig:entityView:advanceConfig')")
public ResponseEntity<Result> updateAdvanceConfig(@RequestBody EntityViewVO vo) {
EntityView entity=convert2Entity(vo);
entityViewService.updateAdvanceConfig(entity.getId(),entity.getAdvanceConfigRule(),entity.getAdvanceConfigOption());
EntityViewVO newVO = convert2VO(entity);
return ResultUtil.success(newVO);
}
前端API定义
// 实体视图
export const entityView = Object.assign({}, COMMON_METHOD, {
serveUrl: '/' + moduleName + '/' + 'entityView' + '/',
// 更新高级配置
updateAdvanceConfig(params) {
return request.put({ url: this.serveUrl + 'updateAdvanceConfig', data: params })
}
})
前端使用,在高级配置页面,扩展fcd的自定义按钮区,增加“保存”按钮,点击时调用api。
<fc-designer v-model="value" ref="designer">
<template #handle>
<el-button type="primary" @click="save">保存</el-button>
<el-button type="primary" @click="preview">预览</el-button>
</template>
</fc-designer>
save() {
const param = {
id: this.$route.query.id,
advanceConfigRule: JSON.stringify(this.$refs.designer.getRule()),
advanceConfigOption: JSON.stringify(this.$refs.designer.getOption())
}
this.$api.entityconfig.entityView.updateAdvanceConfig(param)
}
高级配置模型的运行
通过表单设计器fcd组件,生成高级配置的规则和选项后,写入到库表中,这是配置阶段的工作。
然后平台进行代码生成时,根据启用高级配置的标识位,来决定采用哪种模式类生成前端的功能页面。
在高级配置模式下,需要读取配置阶段的规则和选项,来初始化fc组件。
以修改视图为例,UI部分如下:
<template>
<Dialog title="修改" v-model="visible" width="500px">
<form-create :rule="rule" v-model:api="fApi" :option="options" />
<template #footer>
<el-button type="primary" @click="save" v-permission="pageCode + 'modify'">保存</el-button>
<el-button @click="close">关闭</el-button>
</template>
</Dialog>
</template>
使用fc组件,替代了原来的el-form表单以及el-form-item表单元素。
集成过程中遇到了以下几个问题,下面列出来,并说明对应的处理方案。
如何去除提交按钮?
fcd组件默认显示内置的提交按钮,与平台自身的保存按钮重复,如下图所示:
这个简单,调整fcd组件选项,设置提交按钮不可见即可。
如何初始化表单数据?
对于修改视图,通过数据的标识调用后端服务获取到实体数据后,赋值到表单。
标准模式下,使用entityData来做这件事情。
//获取数据
this.api.get(id).then((res) => {
this.entityData = res.data
})
//数据绑定到表单
<el-form
ref="form"
:model="entityData"
:rules="rules"
label-width="120px"
label-position="right"
style="width: 90%; margin: 0 auto"
>
<!--表单区域 -->
<el-form-item label="组织机构" prop="organization">
<OrganizationReference
v-model="entityData.organization"
:organization-param="organizationParam"
/>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="entityData.name" />
</el-form-item>
<el-form-item label="编码" prop="code">
<el-input v-model="entityData.code" />
</el-form-item>
<el-form-item label="类型" prop="type">
<dictionary-select v-model="entityData.type" code="OrganizationType" />
</el-form-item>
<el-form-item label="状态" prop="status">
<dictionary-select v-model="entityData.status" code="Status" />
</el-form-item>
<el-form-item label="排序" prop="orderNo">
<el-input v-model="entityData.orderNo" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="entityData.remark" />
</el-form-item>
</el-form>
使用fc组件的情况下,找了一圈api,没找到合适的赋值方法,然后在属性绑定里找到了解决方法,即使用v-model来绑定。
<form-create :rule="rule" v-model:api="fApi" :option="options" v-model="entityData" />
按照上述方式,修改视图的初始化可以正常处理。
如何验证数据?
标准模式下使用el-form的验证机制,如下:
this.$refs.form.validate((valid) => {
if (valid) {
this.saveData()
}
})
使用高级配置的情况下,需要遵循fc组件的验证方式,官方示例如下:
this.fApi.validate((valid, fail) => {
if (valid) {
this.saveData()
}
})
经测试存在问题,当表单验证失败后,回调参数valid中放的是验证失败的提示信息数组
按照js的判定规则, if (valid)会返回true,从而继续往下走,数据发给了后端服务,需做以下调整:
this.fApi.validate((valid, fail) => {
if (valid===true) {
this.saveData()
}
})
如何提交表单?
标准模式下,使用entityData即可,如下处理:
// 保存
saveData() {
this.api
.modify(this.entityData)
.then((res) => {
this.entityData = res.data
this.$emit('refresh', this.entityData)
this.close()
})
.finally(() => {
this.loading = false
})
}
高级配置模式下,entityData与fc组件的v-model属性进行了绑定,出现了问题。
在初始化环节,调用后端服务,通过实体标识获取数据后赋值给entityData,fc组件绑定entityData的时候,内部会解析,只保留表单规则rule中定义的属性,其他属性会抛弃掉,如实体标识,这样就会造成保存异常。
基于上述fc组件的表现,另行增加一个属性formValue用于fc组件的属性绑定,然后在初始化时,使用entityData赋值,保存前,合并entityData与formValue的值,提交到后端。
<form-create :rule="rule" v-model:api="fApi" :option="options" v-model="formValue" />
// 初始化
init(id) {
this.api.get(id).then((res) => {
this.entityData = res.data
this.formValue = res.data
this.visible = true
})
}
// 保存
saveData() {
const data = Object.assign(this.entityData, this.formValue)
this.api
.modify(data)
.then((res) => {
this.entityData = res.data
this.formValue = res.data
this.$emit('refresh', this.entityData)
this.close()
})
.finally(() => {
this.loading = false
})
}
高级配置模式的代码生成
高级配置模式需要做出调整的是前端,并且限于前端的新增、修改和查看视图。
以修改视图为例,说明相关调整,新增与查看类似。
代码模板
拷贝实体配置模块modify.vue.ftl,命名为modifyForAdvanceConfig.vue.ftl,修改为使用fc组件实现的高级配置模式。
<template>
<Dialog title="修改" v-model="visible" width="500px">
<form-create :rule="rule" v-model:api="fApi" :option="options" v-model="formValue" />
<template #footer>
<el-button type="primary" @click="save" v-permission="pageCode + 'modify'">保存</el-button>
<el-button @click="close">关闭</el-button>
</template>
</Dialog>
</template>
<script>
import {modifyMixin} from '@/mixin/modifyForAdvanceConfigMixin.js'
<#list modifyViewPropertyList as item>
<#if item.dataType=="ENTITY">
import ${item.entityCode}Reference from '@/modules/${item.moduleCode}/view/${item.entityCode?uncap_first}/${mainReferenceViewMap[item.entityCode]}.vue'
</#if>
</#list>
const MODULE_CODE = '${package.ModuleName}'
const ENTITY_TYPE = '${entity?uncap_first}'
export default {
name: ENTITY_TYPE + '-modify',
mixins: [modifyMixin],
components:{
<#list modifyViewPropertyList as item>
<#if item.dataType=="ENTITY">
${item.entityCode}Reference,
</#if>
</#list>
},
data() {
return {
entityType: ENTITY_TYPE,
moduleCode: MODULE_CODE,
// eslint-disable-next-line no-eval
api: eval('this.$api.' + MODULE_CODE + '.' + ENTITY_TYPE),
pageCode: MODULE_CODE + ':' + ENTITY_TYPE + ':',
entityData: {},
<#list modifyViewPropertyList as item>
<#if item.dataType=="ENTITY">
// ${item.name}组件参数,用于传递数据
${item.entityCode?uncap_first}Param: {},
</#if>
</#list>
rules: {
//前端验证规则
<#list entityModelPropertyList as item>
<#if item.nullFlag=="NO">
${item.code}: [{ required: true, message: '【${item.name}】不能为空', trigger: 'blur' }]<#if item_has_next>, </#if>
</#if>
</#list>
},
//fc组件
options:${modifyEntityView.advanceConfigOption},
rule:${modifyEntityView.advanceConfigRule}
}
},
methods: {
<#if modifyEntityView.beforeInit?? && modifyEntityView.beforeInit!="">
beforeInit(param){
${modifyEntityView.beforeInit}
},
</#if>
<#if modifyEntityView.afterInit?? && modifyEntityView.afterInit!="">
afterInit(param){
${modifyEntityView.afterInit}
},
</#if>
<#if modifyEntityView.validateData?? && modifyEntityView.validateData!="">
validateData(){
${modifyEntityView.validateData}
},
</#if>
<#if modifyEntityView.beforeSave?? && modifyEntityView.beforeSave!="">
beforeSave(){
${modifyEntityView.beforeSave}
},
</#if>
<#if modifyEntityView.afterSave?? && modifyEntityView.afterSave!="">
afterSave(){
${modifyEntityView.afterSave}
},
</#if>
}
}
</script>
<style></style>
代码生成
调整修改视图的生成逻辑,实际调整点非常少,依据实体视图的新加属性“启用高级配置”判断,调用不同的代码模板。
private void generateModifyView(EntityView entityView, Map<String, Object> customKeyValue, InjectionConfig.Builder builder) {
// 视图对象放入自定义键值map
customKeyValue.put("modifyEntityView", entityView);
//模板路径
String templatePath="";
if(entityView.getEnableAdvanceConfig().equals(YesOrNoEnum.NO.name())){
//标准配置模式
templatePath="/templates/modify.vue.ftl";
}else{
//高级配置模式
templatePath="/templates/modifyForAdvanceConfig.vue.ftl";
}
// 获取视图属性配置
List<ViewProperty> viewPropertyList = viewPropertyService.listByView(entityView.getId());
customKeyValue.put("modifyViewPropertyList", viewPropertyList);
// 自定义编辑视图模板
CustomFile templateFile = new CustomFile.Builder()
.fileName("modify.vue")
.templatePath(templatePath)
.enableFileOverride()
.packageName(viewFolderName)
.build();
builder.customFile(templateFile);
}
前端混入
拷贝modifyMixin.js,重命名为modifyForAdvanceConfigMixin.js,内容调整为使用fc组件的高级配置模式,如下:
/**
* 高级配置修改页面混入
*/
import { Dialog } from '@/components/abc/Dialog'
import DictionaryRadioGroup from '@/components/abc/DictionarySelect/DictionaryRadioGroup.vue'
import DictionarySelect from '@/components/abc/DictionarySelect/DictionarySelect.vue'
import DataDictionarySelect from '@/modules/system/view/dictionaryType/treeReferenceUseCode.vue'
import IconPicker from '@/components/abc/IconPicker/index.vue'
import { Editor } from '@/components/abc/Editor'
import CronExpression from '@/components/abc/CronExpression/index.vue'
import OrganizationSingleSelect from '@/modules/system/view/organization/treeReference.vue'
import OrganizationMultipleSelect from '@/modules/system/view/organization/treeMultipleSelect.vue'
import UserSingleSelect from '@/modules/system/view/user/treeListReference.vue'
import AttachmentManager from '@/modules/support/view/attachment/attachmentManager.vue'
import AttachmentUploader from '@/modules/support/view/attachment/attachmentUploader.vue'
import AttachmentViewer from '@/modules/support/view/attachment/attachmentViewer.vue'
export const modifyMixin = {
components: {
Dialog,
DictionaryRadioGroup,
DictionarySelect,
DataDictionarySelect,
Editor,
CronExpression,
OrganizationSingleSelect,
OrganizationMultipleSelect,
UserSingleSelect,
IconPicker,
AttachmentManager,
AttachmentUploader,
AttachmentViewer
},
data() {
return {
// 可见性
visible: false,
// 加载中
loading: false,
fApi: {},
formValue: {}
}
},
methods: {
// 初始化
init(id) {
this.$formCreate.component('DictionarySelect', DictionarySelect)
if (this.beforeInit) {
this.beforeInit()
}
this.api.get(id).then((res) => {
this.entityData = res.data
this.formValue = res.data
if (this.afterInit) {
this.afterInit()
}
this.visible = true
})
},
// 保存
save() {
if (this.beforeSave) {
this.beforeSave()
}
this.fApi.validate((valid, fail) => {
if (valid) {
if (this.validateData) {
// 数据验证通过后才执行保存操作
if (this.validateData()) {
this.saveData()
}
} else {
// 无需数据验证,直接执行
this.saveData()
}
}
})
},
// 关闭
close() {
this.visible = false
},
// 保存
saveData() {
const data = Object.assign(this.entityData, this.formValue)
this.api
.modify(this.data)
.then((res) => {
this.entityData = res.data
this.formValue = res.data
if (this.afterSave) {
this.afterSave()
}
this.$emit('refresh', this.entityData)
this.close()
})
.finally(() => {
this.loading = false
})
},
// 附件上传完成,刷新管理组件
fileComplete() {
this.$refs.attachmentManager.list()
}
}
}
开源平台资料
平台名称:一二三开发平台
简介: 企业级通用开发平台
设计资料:csdn专栏
开源地址:Gitee
开源协议:MIT
欢迎收藏、点赞、评论,你的支持是我前行的动力。