• 应用开发平台集成表单设计器系列之3——整体集成思路及表单设计器功能深度了解


    背景

    平台需要实现自定义表单功能,作为低代码开发的一部分,通过技术预研和技术选型,选择form-create和form-create-designer这两个组件进行集成作为实现方案。通过深入了解和技术验证,确认了组件的功能能满足需求,具备良好的开放性和扩展性。
    接下来,重点来说说整体设计。

    核心问题

    实际有个核心的问题需要先考虑清楚,即现有的开发平台低代码配置功能与表单设计器,二者的关系是什么样。思考后总结,主要有三种模式:
    1.相互独立,分别用于不同的场景;
    2.协作关系,现有配置功能进行初步配置,表单设计器在其基础上再进行二次调整;
    3.替代关系,只保留其中一种实现方式,放弃另外一种。

    开发平台现有配置功能,是通过配置+模板化技术,实现了自动生成前端页面的功能,目前只支持表单属性单列纵向展示,其实扩展为可设置两列展示实际也比较简单,合并单元格方面则略麻烦,虽然不如表单设计器直观,可以灵活的通过拖动来改变位置和次序以及预览效果,但优点是配置简便易行,通过实体属性和配置可以快速生成标准页面。
    对于属性较少的实体,如系统基础数据的管理、配置数据的维护,采用标准化的模板模式来生成,配置简便,快速高效。
    但不得不说,对于非标准化的复杂表单,如三五十个字段的单表,需要分组显示;主子关系表,需要多tab页展示……在这些场景下,表单的可视化配置仍然很有价值,相比原生开发,或者基于平台现有的模板式生成后手工修改,仍存在大幅提高开发效率的空间。

    基于上述考虑,模式3只保留其中实现方式首先出局;模式1会形成一定程度的功能割裂,某个功能实现时即需要评估应该用哪种模式来实现,如随着业务发展和需求变动,需要调整实现模式时,意味着原模式的配置需要重来一遍。模式2的协作模式相对而言是最佳方案,对于简单的实体,直接通过配置化功能快速实现,对于复杂的实体,系统根据实体配置自动生成一个两列的表单,填充到表单设计器上,从而进行二次修改与调整。

    整体集成思路

    开发阶段,集成form-create-designer,实现表单的可视化配置。
    运行阶段,集成form-create,实现表单的运行。

    表单设计器技术预研

    组件列表配置

    官方说明

    这里说的组件列表是指表单设计器左侧的分组及组件,如下图红框所示:
    image.png
    官方说明文档章节中命名是左侧按钮,api部分命名使用的是menuItem,分组使用menu,并不一致,所以我重新命名为组件列表,与业务含义更贴合。

    示例中文档错误的地方较多,比如
    追加自定义拖拽组件(http://designer.form-create.com/guide/menu.html#%E7%A4%BA%E4%BE%8B

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    这里加组件,但是明显没有指定分组……应该是有问题的,然后去别处找答案,在appendMenuItem的方法说明中,实际有两个参数,第一个参数是分组名,第二个参数才是组件。

    type appendMenuItem = (menuName:string, item: MenuItem) => void
    
    • 1

    基于上述现状,通过摸索来说说集成方面的具体实现。

    分组配置

    官方内置了三个分组:main,aide,layout,每个分组下预置了若干组件。
    前面提到过,开发平台这边实际有大量自封装的组件,比如数据字典下拉,组织机构选择、人员选择等,放到官方现有分组中并不合适,需要添加自己的分组单独显示更好一些。
    官方提供了Api,addMenu = (menu: Menu) => void,参数的数据结构如下:

    //左侧拖拽按钮分类
    export interface Menu {
        title: string;
        name: string;
        list: MenuItem[]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    该方法虽然可用,但是没提供插入方法,比如,我想把自定义分组放到最上面,明显做不到。
    经摸索后,采用如下方式:
    给组件指定menu属性

    
    
    • 1

    通过设置menu变量来重置设置分组。

    menu: [
            {
              title: '自定义',
              name: 'custom',
              list: []
            }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中分组要遵循menu对象的数据结构,即留一个空的数组属性list,然后在mouted事件中注册组件,并将组件添加到分组的list数组中

    mounted() {
        //注册组件
        this.$refs.designer.addComponent(DictionarySelectDesigner)
      	//声明组件
        const dictionarySelect = {
          icon: DictionarySelectDesigner.icon,
          name: DictionarySelectDesigner.name,
          label: DictionarySelectDesigner.label
        }
        //
        this.menu[0].list.push(dictionarySelect)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    实现效果如下:
    image.png
    如要保留官方的分组,则可以参考官方初始化的方式,把分组和组件按需加进来,并可以灵活选择去除认为不适用的部分组件。

    以下是把fcd自带的布局组件分组(layout)以及下属的栅格布局(row)加入进来。

    menu: [
            {
              title: '自定义',
              name: 'custom',
              list: []
            },
            {
              name: 'layout',
              title: '布局组件',
              list: [row]
            }
          ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    显示效果如下:
    image.png
    注意,上面操作栅格布局组件实际并没有正常显示名称,拖放时也会报错,无法正常使用……。

    组件属性

    作为一个独立的组件,位于表单设计器左侧列表中,首先需要有三要素:组件标签(label)、组件名称(name)、组件图标(icon)。实际对应的数据结构如下:

    export interface MenuItem {
        label: string,
        name: string,
        icon: string;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注意,这里的name实际是对应着自定义组件的编码,这层对应关系是隐含的,官方文档并未着重说明。

    组件配置

    上面介绍了分组如何配置,下面来说下分组中的具体组件如何配置。
    首先来说下,如何配置使用fcd自身的组件,官方文档中说的过于简略,可以参照官方源码中来配置。

    import radio from './rule/radio';
    import checkbox from './rule/checkbox';
    import input from './rule/input';
    import number from './rule/number';
    import select from './rule/select';
    import _switch from './rule/switch';
    import slider from './rule/slider';
    import time from './rule/time';
    import date from './rule/date';
    import rate from './rule/rate';
    import color from './rule/color';
    import row from './rule/row';
    import divider from './rule/divider';
    import cascader from './rule/cascader';
    import upload from './rule/upload';
    import transfer from './rule/transfer';
    import tree from './rule/tree';
    import alert from './rule/alert';
    import span from './rule/span';
    import space from './rule/space';
    import button from './rule/button';
    import editor from './rule/editor';
    import tab from './rule/tab';
    
    export default function createMenu() {
        return [
            {
                name: 'main',
                title: '表单组件',
                list: [
                    input, number, radio, checkbox, select, _switch, time, date, slider, rate, color, cascader, upload, transfer, tree, editor
                ]
            },
            {
                name: 'aide',
                title: '辅助组件',
                list: [
                    alert, button, span, divider
                ]
            },
            {
                name: 'layout',
                title: '布局组件',
                list: [
                    row, /*tab,*/ space
                ]
            },
        ];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    fcd的源码中是使用的相对路径,我们实际安装配置fcd组件,会放到npm_modules目录下,引用的时候不能使用如下方式:

    import row from '@form-create/designer/src/config/rule/row.js'
    
    • 1

    更换为下面这种方式

    import row from '@form-create/designer'
    
    • 1

    引用阶段不报错了,但是组件名称没有正常显示,拖放时也会报错。

    综上,目前没找到了灵活配置内置组件的方式,建议要么全部保留,要么全部移除。

    临时方案

    保留fcd内置组件,通过API将自定义分组和组件添加进去。

    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    自定义组件实现

    解决了组件列表配置问题,接下来是自定义组件封装问题,在上一篇技术验证中,实际拿数据字典下拉组件作为示例已经做过说明,不过相对零散,本节完整说下。

    官方说明

    自定义组件需要遵循fcd的规范和要求,官方说明地址为http://designer.form-create.com/guide/component.html#%E5%AD%97%E6%AE%B5%E8%AF%B4%E6%98%8E

    字段名说明类型
    name组件名称string
    rule获取组件生成规则的方法Function
    props获取组件配置规则的方法Function
    children子组件名称string
    drag是否可以拖入组件string | Boolean
    dragBtn是否显示拖拽按钮Boolean

    首先,组件名称name非常重要,唯一性标识该组件,将该组件添加到fcd左侧组件列表时,实际使用的就是属性,需要一致起来才能实现对应关系。
    其次,rule是一个方法,当组件被拖放到表单区域时,由该方法决定如何来生成UI元素,以内置checkbox组件为例

    rule() {
      //如果在 props 方法中需要修改 rule 的属性,需要提前在 rule 上定义对应的属性
      return {
        //生成组件的名称
        type: name,
        //field 自定不能重复,所以这里每次都会生成一个新的
        field: uniqueId(),
        title: label,
        info: '',
        effect: {
          fetch: ''
        },
        //这里设置组件的默认props配置项, 在下面的 props 方法里面设置无效
        props: {},
        options: [
          {value: '1', label: '选项1'},
          {value: '2', label: '选项2'},
        ]
      };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    再次,props也是一个方法,用于生成组件的配置属性

    props() {
      return [
        //生成`checkbox`组件的`options`配置规则
        FcDesigner.makeOptionsRule('options'),
        {
          type: 'switch',
          field: 'type',
          title: '按钮类型',
          props: {activeValue: 'button', inactiveValue: 'default'}
        }, 
        {type: 'switch', field: 'disabled', title: '是否禁用'}, 
        {
          type: 'inputNumber',
          field: 'min',
          title: '可被勾选的 checkbox 的最小数量'
        }, 
        {type: 'inputNumber', field: 'max', title: '可被勾选的 checkbox 的最大数量'}, 
        {
          type: 'input',
          field: 'textColor',
          title: '按钮形式的 Checkbox 激活时的文本颜色'
        }, 
        {type: 'input', field: 'fill', title: '按钮形式的 Checkbox 激活时的填充色和边框色'}]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    image.png

    数据字典下拉组件

    开发平台研发过程中封装了一个基于系统数据字典的下拉组件,其实现的功能是通过通过字典类型编码,自动请求后端服务,拿到字典项后填充下拉列表,源码如下:

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110

    该功能的详细设计与实现可参见之前的博文:https://blog.csdn.net/seawaving/article/details/128225379

    二次封装

    之所以要二次封装,是因为自定义组件需要遵循fcd的规范和要求,才能融入到fcd中,拖放时正常显示以及设置属性。
    封装其实不难,引入原组件,按照fcd组件要求定义相关属性,完整源码如下:

    import FcDesigner from '@form-create/designer'
    import { markRaw } from 'vue'
    import DictionarySelect from '@/components/abc/DictionarySelect/DictionarySelect.vue'
    const label = '下拉列表'
    const name = 'DictionarySelect'
    let i = 1
    const uniqueId = () => `uni${i++}`
    export default {
      icon: 'icon-select',
      label,
      name,
      rule() {
        return {
          type: name,
          component: markRaw(DictionarySelect),
          field: uniqueId(),
          title: label,
          info: '',
          props: {}
        }
      },
      props() {
        return [
          //生成`checkbox`组件的`options`配置规则
          // FcDesigner.makeOptionsRule('options'),
          { type: 'input', field: 'code', title: '字典类型编码' }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    其中的注意事项还是要说一下。
    首先,标签(label)和图标(icon)并非fcd自定义组件的必须实现项,在组件内部定义和设置,主要是考虑了将来添加到左侧组件列表时的便利,即可以采用如下方式:

     //读取自定义组件信息,生成左侧组件对象
        const dictionarySelect = {
          icon: DictionarySelectDesigner.icon,
          name: DictionarySelectDesigner.name,
          label: DictionarySelectDesigner.label
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其次,需要注意rule规则中的field属性,需要保持唯一,因为同一个组件会被多次拖入到表单中重复使用,标识需要唯一,此处暂时使用了一个方法来,以后会设置为实体的属性编码。
    最后,引入了markRaw来处理,如不加,浏览器会报警告,大概意思是组件自身已经是响应式的,不需要再包一层。

    还有一点,是在props中指定了该组件的配置属性,这里用一个文本框来设置字典类型编码,先做简单化处理,后续实际改造成为弹出对话框,从系统字典中搜索和选择更便利。

    自定义操作按钮

    官方说明

    官方说明极简,通过名为handle的插槽可添加自定义按钮,示例代码如下

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    实测效果如下:
    image.png
    预览和清空按钮是fcd内置的,最左侧“自定义按钮”是我们自己加的。

    实战

    官方提供了扩展能力,在实际应用中能发挥什么作用呢?
    我们可以添加自定义按钮,然后调用fcd的API,来实现一些额外的功能,如通过导入json的方式来生成表单。
    也就是说,可以把下图中官方示例中的功能按钮,放到该区域来实现。
    image.png

    表单API

    这里设计两个概念,一是表单的配置,即表单的属性设置信息,如下图所示
    image.png
    二是通过表单设计器,拖拽组件并配置属性后,fcd会生成对应的json数据,这部分json官方命名为规则(rule),从字面意思上不太直观,不好理解。

    这里涉及到FCD组件的4个API,分别是读取属性、读取规则和设置属性、设置规则

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    通过设置规则和属性,来回显表单

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    接下来,表单设计器的最终目的是生成表单,是通过表单设计器来获取属性和规则,赋值给fc组件

    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    开发平台资料

    平台名称:一二三开发平台
    简介: 企业级通用开发平台
    设计资料:csdn专栏
    开源地址:Gitee
    开源协议:MIT
    开源不易,欢迎收藏、点赞、评论。

  • 相关阅读:
    高效防汛决策:山海鲸可视化系统助力城市防洪
    Javasript中的BOM
    【Springboot】Filter 过滤器的使用
    WebKit Insie: Active 样式表
    第十二章 哈希表与字符串哈希
    手写redux和applyMiddleware中间件react示例
    SPA项目开发关于动态树+数据表格+分页
    理解MySQL的会话变量、局部变量和全局变量
    『忘了再学』Shell基础 — 31、字符处理相关命令
    跨境电商与监管:合规化IPO的长征路
  • 原文地址:https://blog.csdn.net/seawaving/article/details/134465095