在我们一些系统里面,有时候会需要一些让用户自定义的数据信息,一般这些可以使用扩展JSON进行存储,不过每个业务表的显示项目可能不一样,因此需要根据不同的表单进行设计,然后进行对应的数据存储。本篇随笔结合基于Vue+Element实现动态表单的设计、数据录入存储的相关操作。
1、动态表单的设计
动态表单的设计一般是基于某个能够动态设计界面的方式实现的,界面上定义对应的说明以及录入数据的方式,如标签后面加上文本,或者多文本等方式。由于我们后台是强类型的数据实体,后端一般不采用动态修改数据库字段的方式构建,而是采用扩展JSON结构的方式来定义整个动态表单的结构,动态扩展的JSON结构比较弹性,不用可以随时移除,也可随时增加,非常方便,这是一样常见的处理数据结构方式。
表单设计器有很多组件可以使用
不过我倾向于使用前者vue-form-making,因为它提供一个设计器的组件,可以集成在项目中使用,而form-generator好像没有找到,只有在线的设计器。不过两者的概念都差不多。
vue-form-making的设计器界面如下:
而form-generator设计器界面如下,和前者不同的是,这个没有提供组件可以集成在项目里面。
有了工具,我们就要考虑如何处理我们具体项目里面表的扩展表单界面的设计工作了。
我们设计一个表单用来存储对应的业务表的结构设计,然后在具体表的查看、新增、编辑界面里面,根据键值标识获取对应的动态表单界面,整合到我们实际的界面里面,给用户查看或者录入、编辑等。
因此我们在系统模块里面增加一个动态表单的功能入口,以便设计系统所需业务表单的界面结构。
这里存储的信息不多,主要就是一个用来区分表单名称的键和说明信息,以及JSON界面结构信息即可 。
这里【立即编辑】功能就是前面说到的整合表单设计界面的入口。
其中使用making-form的代码如下所示,自定义了保存的操作功能。
<el-dialog title="创建表单" :close-on-click-modal="false" :append-to-body="true" :visible="isShowForm" fullscreen @close="isShowForm=false"> <fm-making-form ref="makingform" style="height: calc(100vh - 84px);" preview generate-code generate-json upload clearable > <template slot="action"> <el-button type="primary" icon="el-icon-upload" size="mini" @click="saveCode()">保存代码</el-button> </template> </fm-making-form>
由于这段代码是在自己定义的组件makingform.vue中处理的,那么获取到JSON信息后,需要抛出一个事件来告诉外部即可。
saveCode() { // 保存代码 const json = this.$refs.makingform.getJSON();// getHtml() // console.log(json) this.$emit('save', JSON.stringify(json)); // 触发事件,返回内容 this.isShowForm = false }
在刚才的界面中,使用自定义的makingform组件代码如下所示。
<makingform ref="makingform" @save="saveCode" />
当然这里保存的操作就是把新的JSON代码存储到界面的textarea组件上了,这样就实现了我们动态表单界面结构JSON的更新了。
saveCode(json) { if (this.isAdd) { this.addForm.content = json } else if (this.isEdit) { this.editForm.content = json } },
当然,我们打开makingform组件的时候,如果已经有了JSON信息,那么也是需要加载它已有的界面结果的。
在主体调用界面上,我们打开设计界面的时候,就需要传入对应的JSON数据。
showMaking() { if (this.isAdd) { this.$refs.makingform.show()// 显示窗口 } else if (this.isEdit) { this.$refs.makingform.show(this.editForm.content)// 显示窗口 } },
而在组件上,我们根据JSON赋值给设计器控件即可。
show(json) { // 显示窗口并加载数据 this.isShowForm = true if (!this.isEmpty(json)) { // 表单结构 this.jsonData = JSON.parse(json) // console.log(this.jsonData) this.$nextTick(() => { this.$refs.makingform.setJSON(this.jsonData); }) } },
有了这些动态表单界面数据的准备,我们就可以在具体表单里面,整合这些设计的界面,从而实现动态表单的展示了。
为了比较直观显示我们对应设计的表单,我们也在列表中提供了一个预览的界面,用于预览生成的表单界面效果。
单击预览按钮,可以查看具体设计的表单效果,表单的呈现是通过其中的fm-generate-form 来呈现效果的。
2、动态表单的数据存储
这里根据上面的动态表单设计的界面,整合并存储对应界面控件的值,从而实现了动态表单和动态表单数据的整合显示了。
为了有效管理动态表单的数据和是否展示的处理,我们在业务表单的data属性集合中增加了两个变量,如下所示。
hasDynamicForm: false, // 是否有动态表单 dynamicFormJson: '', // 动态表单的JSON数据
这样我们在业务表单列表界面呈现的时候,也同时获取对应的动态界面结构JSON,如下代码所示。
created() { this.getlist() // 获取并显示列表 // 处理动态表单 var param = { name: 'testuser' } dynamicForm.FindByName(param).then(data => { var result = data.result if (result && !this.isEmpty(result.content)) { this.dynamicFormJson = result.content // 表单数据 this.hasDynamicForm = true // 是否有 } }) },
这些属性,可以在查看、编辑、新增界面中使用,为了独立性考虑,我们添加一个选项卡用来显示动态表单的设计,如果对应的记录中存在了动态表单结构,就显示,否则不显示即可。
界面代码如下所示。
其中动态表单数据主要存储在extensionData字段里面的。
其中的generateform 组件,是我们自定义整合fm-generate-form 组件的,完整的自定义组件generateform代码如下所示。
主要就是定义了两个prop属性,一个是json,用来存储结构数据,一个是edit,用来存储界面组件的JSON数据信息的。
<template> <div class="app-container"> <div class="fm-container"> <fm-generate-form ref="generateForm" :data="jsonData" :remote="remoteFuncs" :value="editData" /> </div> </div> </template> <script> import Vue from 'vue' import VueEditor from 'vue2-editor' Vue.use(VueEditor) import FormMaking from 'form-making' import 'form-making/dist/FormMaking.css' Vue.use(FormMaking) export default { props: { json: { type: String, default: '' }, edit: { type: String, default: '' } }, data() { return { jsonData: {}, editData: {}, remoteFuncs: {} } }, created() { this.show(this.json, this.edit) }, methods: { clear() { this.$nextTick(() => { this.$refs.generateForm.reset() }) }, show(json, edit) { // 显示窗口并加载数据 // console.log(json) // console.log(edit) if (!this.isEmpty(json)) { // 表单结构 this.jsonData = JSON.parse(json) } if (!this.isEmpty(edit)) { // 表单结构 this.editData = JSON.parse(edit) } }, getData() { // 获取动态表单数据并转换JSON return this.$refs.generateForm.getData() } } } </script> <style lang="scss" scoped> .app-container,.fm-container{ height: calc(100vh - 84px); } </style> <style> #app .app-container { padding: 0 !important; } </style>
3、后端ABP框架的扩展数据处理
前面说到的显示动态表单及其数据的内容,其中动态表单数据主要存储在extensionData字段里面的。
这个需要我们后端提供数据存储的处理,在设计表中增加一个ntext类型的字段ExtensionData,如下所示。
那样ABP后端的Entity实体,和DTO数据对象里面,都添加这个字段信息了
/// <summary> /// 扩展JSON数据 /// </summary> public string ExtensionData { get; set; }
这样ABP就可以通过不同的前端实现数据的存储处理了。
为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:
循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作
循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用
循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理
循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理
循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理
循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数
循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用
循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理
循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示
循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用
循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理
循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理
循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示
循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理
循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理
循序渐进VUE+Element 前端应用开发(17)--- 菜单管理
循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制
循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合
循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理
循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理
循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)
循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)
ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)
ABP框架中一对多,多对多关系的处理以及功能界面的处理(2)