马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
目录
前言
一、动态表单技术
1、包含的主要信息
2、元素属性设置
3、表单内容
二、表单数据存储和查询
1、数据存储
2、数据的查询
3、在5.7版本中进行JSON检索
4、8.0后的优化查询
三、总结
前言
在很多有工作流设置的地方、比如需要在不同的流程中,需要实现流程表单的自定义。在从前的一些业务中,我们几乎都需要用户来固化表单。这样的实现方式,非常不友好,扩展性也不强。通常在上线后会需要进行调解。同时,在调解时,一样平常需要用户先明确流程,然后再反馈给开发人员,由于当时没有在线的流程表单构造器,流程的表单调解还需要开发人员来配合。这样一来,体系的开发步骤就比较长,一个流程要想走下来,耗费的时间代价就非常大。因此,在这样的需求背景下,有的技术团队开始研究动态表单,将用户的创造和动手能力直接引入进来。用户不光能自定义流程引擎,同时还能基于流程引擎来定义挂载在流程引擎上的表单。
到了这一步,应该说黑白常友好的,用户可以深度的参与相关的计划,如果想调解流程。只需要下架旧的流程,然后计划新的流程,同时把相应的表单也计划好后一同发布。这样体系就会启动新的流程,表单也会自动更新。曾几何时,这种技术照旧少数部分用户玩的,而今再看,就像“旧时王谢堂前燕飞入寻常百姓家”,已经没有了什么秘密的面纱,向大众展示它背后的一面。
虽然在流程中利用动态表单,有很好的扩展性和可用性。但是,同时也带来了一些额外的技术实现复杂度,就是在天生动态表单的时候,表单通常比较复杂,通常我们需要进行很多表单元素的编辑,还要定义表单的值,各种各样的表单元素范例。如果我想利用表格来导入这些数据,应该怎么来进行对应。导出的时候,怎么精准的写出数据。这对我们在数据库中计划相应的表以及针对动态表单的数据进行表单元素级的精准匹配有了一定的技术要求。
本文重点不是在于解说怎样计划动态表单,而是重点解说,如安在将计划好的动态表单信息进行提取,比如进行模板数据导入的时候,可以根据不同的表单范例,比如根据单行文字框的名字来动态设置值,也可以在导数数据时,知道将数据库的性别一列保存的1和2翻译成男和女这两种属性。这都需要我们精准的提取表单中的不同的信息,能精准提取表单的文本、范例、默认值域还有其他的表单元素的设置。通过本文,您可以了解怎样正确的操作动态表单信息,同时了解怎样从表单中查找表单元素。
一、动态表单技术
为了让大家了解一些动态表单大概会包含哪些技术,我照旧决定对动态表单技术进行简单的解说,更深入的就不再进行赘述。目标是让大家对动态表单有个基本的认识。
1、包含的主要信息
众所周知,在Web界面的计划和实现中,表单实在就一个form界面,我们在这个Web界面中可以定义不同的表单元素,比如单行文本框,多行文本域、单选按钮、多选按钮、下拉框,而在现代的界面中,对元素的范例做了更进一步的细分,比如时间又可以分为时间选择器、日期选择器,别的的常见还有打分控件、计数器、颜色选择器、开关、滑块等等。在下图中列出来了常见的一些表单要素。
上面这个就是一个非常典范的表单计划器,它按照功能区域可以分为表单元素范例、表单计划渔区、属性计划渔区三个部分。从结构来说分为左、中、右三种范例。最左边的部分是表单中包含的元素范例,这个在上面的内容中有所涉及。下面有布局字段,布局的话就是用来控制页面的元素怎样部署,比如一行是摆放三个单行文本域照旧摆2个,这些都是通过布局元素来设置的。 中央就是主题的计划界面。点击左边的元素,然后拖到中央的计划器中即可。
2、元素属性设置
将元素和布局都设置好之后,一个计划精良的表单,还需要对表单的属性信息进行定义,比如表单的名字、它的默认值是什么,如果是下拉框,下拉框的值域又是什么?默认的下拉选项是哪个。表单的元素是否必填,是有别的的数据格式校验范例等等。这些属性信息都在最右边的属性编辑器中进行定义和设置。在中央的要素计划器中点击对应的表单元素,可以打开它对应的属性设置信息。如下图所示:
通过上面的表单计划界面,我们就可以实现表单的机动设置。
3、表单内容
信赖大家对于天生的表单内容是什么样的,一定很有兴趣吧。下面我们来看一下经过上面的动态表单计划之后,天生的表单内容是什么样的?具体的格式是什么?首先来点击预览,看一下表单计划器天生的页面效果。
点击“天生json”按钮可以将动态表单转成json,这样我们就可以把表单存储到数据库中,实现动态的管理和配置。来看下json的表单天生结果。
内容比较多,我们将表单内容复制到文本编辑器中,然后将json进行格式的美化厥后看实际的效果。
- "list": [
- {
- "type": "grid",
- "icon": "icon-grid-",
- "columns": [
- {
- "span": 12,
- "list": [
- {
- "type": "input",
- "icon": "icon-input",
- "options": {
- "width": "100%",
- "defaultValue": "",
- "required": false,
- "dataType": "string",
- "pattern": "",
- "placeholder": "请输入姓名",
- "disabled": false,
- "maxlength": -1,
- "showWordLimit": false,
- "remoteFunc": "func_1721826724000_49979"
- },
- "name": "姓名",
- "key": "1721826724000_49979",
- "model": "input_1721826724000_49979",
- "rules": [
- {
- "type": "string",
- "message": "姓名格式不正确"
- }
- ]
- }
- ]
- }
复制代码 以上就是我们对上面的动态表单的相关知识的介绍。介绍上面的内容,主要是为了让大家对数据的格式和样例有一个基本的掌握。
二、表单数据存储和查询
在掌握了动态表单的计划之后,为了实现可以机动的管理和检索,我们还需要将表单的数据进行存储,在需要调用的时候还需要进行高效的查询。本节就简单的来讲一下怎样进举措态表单数据的存储和查询。数据库接纳的是MySQL数据库,版本是5.7,接纳的数据范例是JSON。
1、数据存储
要想把上述的表单数据存储起来,我们可以有两种选择,第一种是将表单的内容直接存成字符串,配合后台的开发语言来实现表单的存储。第二种就是利用数据库的特性,直接将数据以JSON的形式存储。第一种方案,数据库简单,但是应用程序复杂。而第二种方案刚好相反。依然利用了数据库,而且用到了5.7这样的版本,在MySQL当中,是支持我们直接操作和管理JSON范例的数据的。因此这里我们接纳第二种方案,直接计划json字段。将动态表单的内容存储到json字段中。计划的表结构如下:
- -- ----------------------------
- -- Table structure for ems_equipment_classification_attr
- -- ----------------------------
- DROP TABLE IF EXISTS `ems_equipment_classification_attr`;
- CREATE TABLE `ems_equipment_classification_attr` (
- `equip_class_id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '设备分类ID',
- `attr_config` json NOT NULL COMMENT '属性信息,以json的形式存储',
- `create_uid` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '创建人',
- `create_time` int(11) NOT NULL DEFAULT 0 COMMENT '创建时间',
- `modify_uid` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '更新人',
- `modify_time` int(11) NOT NULL DEFAULT 0 COMMENT '更新时间',
- `version` int(4) NOT NULL DEFAULT 0 COMMENT '版本号',
- `inherit_flag` int(4) NOT NULL DEFAULT 1 COMMENT '属性继承标记,1表示继承上级属性,0表示独立属性',
- INDEX `idx_ems_equipment_class_attr_class_id`(`equip_class_id`) USING BTREE
- ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '设备分类属性,以json的形式进行存储,支持各层级单独定义,下级分类自动继承上级分类属性' ROW_FORMAT = Dynamic;
复制代码 在上述表中我们有一个attr_config 字段,这个字段计划成json范例的,主要用来保存动态表单的值。然后我们利用以下语句来新增一条记录:
- -- ----------------------------
- -- Records of ems_equipment_classification_attr
- -- ----------------------------
- INSERT INTO `ems_equipment_classification_attr` VALUES ('3', '{"formConfig": {"size": "", "cssCode": "", "refName": "vForm", "functions": "", "modelName": "formData", "rulesName": "rules", "labelAlign": "label-left-align", "labelWidth": 80, "layoutType": "PC", "customClass": "", "jsonVersion": 3, "labelPosition": "left", "onFormCreated": "", "onFormMounted": "", "onFormValidate": "", "onFormDataChange": ""}, "widgetList": [{"id": "grid93809", "key": 43585, "cols": [{"id": "grid-col-52064", "icon": "grid-col", "type": "grid-col", "options": {"md": 12, "sm": 12, "xs": 12, "name": "gridCol52064", "pull": 0, "push": 0, "span": 12, "hidden": false, "offset": 0, "responsive": false, "customClass": []}, "category": "container", "internal": true, "widgetList": [{"id": "input36308", "key": 90048, "icon": "text-field", "type": "input", "options": {"name": "input36308", "size": "", "type": "text", "label": "直径(DN)", "hidden": false, "onBlur": "", "onFocus": "", "onInput": "", "disabled": false, "onChange": "", "readonly": false, "required": true, "clearable": true, "maxLength": null, "minLength": null, "onCreated": "", "onMounted": "", "buttonIcon": "custom-search", "labelAlign": "", "labelWidth": null, "onValidate": "", "prefixIcon": "", "suffixIcon": "", "validation": "", "columnWidth": "200px", "customClass": [], "labelHidden": false, "placeholder": "", "appendButton": false, "defaultValue": "", "labelTooltip": null, "requiredHint": "品牌不能为空", "showPassword": false, "showWordLimit": false, "labelIconClass": null, "validationHint": "", "labelIconPosition": "rear", "onAppendButtonClick": "", "appendButtonDisabled": false}, "formItemFlag": true}]}, {"id": "grid-col-95927", "icon": "grid-col", "type": "grid-col", "options": {"md": 12, "sm": 12, "xs": 12, "name": "gridCol95927", "pull": 0, "push": 0, "span": 12, "hidden": false, "offset": 0, "responsive": false, "customClass": []}, "category": "container", "internal": true, "widgetList": [{"id": "select17890", "key": 27039, "icon": "select-field", "type": "select", "options": {"name": "select17890", "size": "", "label": "制动方式", "hidden": false, "onBlur": "", "remote": false, "onFocus": "", "disabled": false, "multiple": false, "onChange": "", "required": true, "clearable": true, "onCreated": "", "onMounted": "", "filterable": false, "labelAlign": "", "labelWidth": null, "onValidate": "", "validation": "", "allowCreate": false, "columnWidth": "200px", "customClass": [], "labelHidden": false, "optionItems": [{"label": "手动", "value": 1}, {"label": "电动", "value": 2}, {"label": "气动", "value": "3"}, {"label": "液压控制", "value": 4}, {"label": "其他", "value": 5}], "placeholder": "", "defaultValue": "", "labelTooltip": null, "requiredHint": "制动方式不能空", "multipleLimit": 0, "onRemoteQuery": "", "labelIconClass": null, "validationHint": "", "automaticDropdown": false, "labelIconPosition": "rear"}, "formItemFlag": true}]}], "icon": "grid", "type": "grid", "options": {"name": "grid93809", "gutter": 12, "hidden": false, "colHeight": null, "customClass": []}, "category": "container"}, {"id": "grid40469", "key": 43585, "cols": [{"id": "grid-col-77638", "icon": "grid-col", "type": "grid-col", "options": {"md": 12, "sm": 12, "xs": 12, "name": "gridCol77638", "pull": 0, "push": 0, "span": 12, "hidden": false, "offset": 0, "responsive": false, "customClass": []}, "category": "container", "internal": true, "widgetList": [{"id": "input30722", "key": 90048, "icon": "text-field", "type": "input", "options": {"name": "input30722", "size": "", "type": "text", "label": "压力(MP)", "hidden": false, "onBlur": "", "onFocus": "", "onInput": "", "disabled": false, "onChange": "", "readonly": false, "required": true, "clearable": true, "maxLength": null, "minLength": null, "onCreated": "", "onMounted": "", "buttonIcon": "custom-search", "labelAlign": "", "labelWidth": null, "onValidate": "", "prefixIcon": "", "suffixIcon": "", "validation": "", "columnWidth": "200px", "customClass": [], "labelHidden": false, "placeholder": "", "appendButton": false, "defaultValue": "", "labelTooltip": null, "requiredHint": "压力不能为空", "showPassword": false, "showWordLimit": false, "labelIconClass": null, "validationHint": "", "labelIconPosition": "rear", "onAppendButtonClick": "", "appendButtonDisabled": false}, "formItemFlag": true}]}, {"id": "grid-col-71433", "icon": "grid-col", "type": "grid-col", "options": {"md": 12, "sm": 12, "xs": 12, "name": "gridCol71433", "pull": 0, "push": 0, "span": 12, "hidden": false, "offset": 0, "responsive": false, "customClass": []}, "category": "container", "internal": true, "widgetList": [{"id": "select11081", "key": 31288, "icon": "select-field", "type": "select", "options": {"name": "select11081", "size": "", "label": "材质", "hidden": false, "onBlur": "", "remote": false, "onFocus": "", "disabled": false, "multiple": false, "onChange": "", "required": true, "clearable": true, "onCreated": "", "onMounted": "", "filterable": true, "labelAlign": "", "labelWidth": null, "onValidate": "", "validation": "", "allowCreate": false, "columnWidth": "200px", "customClass": [], "labelHidden": false, "optionItems": [{"label": "铸铁", "value": 1}, {"label": "不锈钢", "value": 2}, {"label": "碳钢", "value": 3}, {"label": "铜", "value": 4}, {"label": "其他", "value": 5}], "placeholder": "", "defaultValue": "", "labelTooltip": null, "requiredHint": "材质不能为空", "multipleLimit": 0, "onRemoteQuery": "", "labelIconClass": null, "validationHint": "", "automaticDropdown": false, "labelIconPosition": "rear"}, "formItemFlag": true}]}], "icon": "grid", "type": "grid", "options": {"name": "grid40469", "gutter": 12, "hidden": false, "colHeight": null, "customClass": []}, "category": "container"}, {"id": "grid4823", "key": 62462, "cols": [{"id": "grid-col-24973", "icon": "grid-col", "type": "grid-col", "options": {"md": 12, "sm": 12, "xs": 12, "name": "gridCol24973", "pull": 0, "push": 0, "span": 12, "hidden": false, "offset": 0, "responsive": false, "customClass": []}, "category": "container", "internal": true, "widgetList": [{"id": "input21916", "key": 28602, "icon": "text-field", "type": "input", "options": {"name": "input21916", "size": "", "type": "text", "label": "其他属性", "hidden": false, "onBlur": "", "onFocus": "", "onInput": "", "disabled": false, "onChange": "", "readonly": false, "required": false, "clearable": true, "maxLength": null, "minLength": null, "onCreated": "", "onMounted": "", "buttonIcon": "custom-search", "labelAlign": "", "labelWidth": null, "onValidate": "", "prefixIcon": "", "suffixIcon": "", "validation": "", "columnWidth": "400px", "customClass": [], "labelHidden": false, "placeholder": "", "appendButton": false, "defaultValue": "", "labelTooltip": null, "requiredHint": "", "showPassword": false, "showWordLimit": false, "labelIconClass": null, "validationHint": "", "labelIconPosition": "rear", "onAppendButtonClick": "", "appendButtonDisabled": false}, "formItemFlag": true}]}], "icon": "grid", "type": "grid", "options": {"name": "grid4823", "gutter": 12, "hidden": false, "colHeight": null, "customClass": []}, "category": "container"}]}', '', 1709188741, 'G6', 1711434246, 1021100012, 1);
复制代码 2、数据的查询
在利用上面的sql脚本将数据插入到数据库之后呢,我们怎么把页面的表单给精准的查询出来呢?实现我们最初的需求呢。这里就需要介绍一下在MySQL中的JSON数据的查询题目。在MySQL5.7中关于JSON的方法参见其官网的定义:
NameDescriptionIntroducedDeprecated->Return value from JSON column after evaluating path; equivalent to JSON_EXTRACT().->>Return value from JSON column after evaluating path and unquoting the result; equivalent to JSON_UNQUOTE(JSON_EXTRACT()).5.7.13JSON_APPEND()Append data to JSON documentYesJSON_ARRAY()Create JSON arrayJSON_ARRAY_APPEND()Append data to JSON documentJSON_ARRAY_INSERT()Insert into JSON arrayJSON_CONTAINS()Whether JSON document contains specific object at pathJSON_CONTAINS_PATH()Whether JSON document contains any data at pathJSON_DEPTH()Maximum depth of JSON documentJSON_EXTRACT()Return data from JSON documentJSON_INSERT()Insert data into JSON documentJSON_KEYS()Array of keys from JSON documentJSON_LENGTH()Number of elements in JSON documentJSON_MERGE()Merge JSON documents, preserving duplicate keys. Deprecated synonym for JSON_MERGE_PRESERVE()5.7.22JSON_MERGE_PATCH()Merge JSON documents, replacing values of duplicate keys5.7.22JSON_MERGE_PRESERVE()Merge JSON documents, preserving duplicate keys5.7.22JSON_OBJECT()Create JSON objectJSON_PRETTY()Print a JSON document in human-readable format5.7.22JSON_QUOTE()Quote JSON documentJSON_REMOVE()Remove data from JSON documentJSON_REPLACE()Replace values in JSON documentJSON_SEARCH()Path to value within JSON documentJSON_SET()Insert data into JSON documentJSON_STORAGE_SIZE()Space used for storage of binary representation of a JSON document5.7.22JSON_TYPE()Type of JSON valueJSON_UNQUOTE()Unquote JSON valueJSON_VALID()Whether JSON value is valid 3、在5.7版本中进行JSON检索
首先第一步查询一下数据版本,在客户端软件中执行以下sql:
- -- 查看mysql 的服务器版本
- SELECT VERSION();
- 5.7.14-log
复制代码 第二步、查询当前待提取的表单的选项的最大长度
- -- 这里只能在mysql中使用一种折中的实现方案
- -- 查出数组最大长度
- SELECT MAX(JSON_LENGTH(JSON_EXTRACT(attr_config, CONCAT('$**.options'))))
- FROM ems_equipment_classification_attr where equip_class_id = '3';
- 13
复制代码 这里要查询JSON_LENGTH的原因是由于,在当前我这个版本的MYSQL中,如果要实现行列转护转换,只能利用一种折中的方式。通过上述的步骤求出待提取的目标的长度,然后动态拼接参数,实现数据转成多行。这里直接给出最终拼接成的SQL,在得到这个SQL之前,我也是花了很长的时间进行实验才找到这个方法,感谢前人提供的思路。
- select
- tb.element -> '$[0].key' keystr,tb.element ->> '$[0].type' type, tb.element ->> '$[0].icon' icon,
- tb.element ->> '$[0].options.name' namestr, tb.element ->> '$[0].options.label' label, tb.element ->> '$[0].options.optionItems' optionItems,
- tb.element
- from (
- SELECT JSON_EXTRACT(ta.json_val, CONCAT('$[', idx ,']')) AS element
- FROM (
- SELECT JSON_TYPE( attr_config -> '$**.widgetList' ) type,
- attr_config -> '$**.widgetList' json_val,t.*
- FROM
- ems_equipment_classification_attr t
- WHERE
- JSON_TYPE( attr_config -> '$**.label' ) IS NOT NULL
- AND equip_class_id = '3'
- ) ta -- Inline table of sequential values to index into JSON array
- INNER JOIN (
- SELECT 0 AS idx UNION
- SELECT 1 UNION
- SELECT 2 UNION
- SELECT 3 UNION
- SELECT 4 UNION
- SELECT 5 UNION
- select 6 union
- select 7 union
- select 8
- -- 一直 UNION 到数组最大长度 - 1
- ) AS indexes
- -- 排除元素数量比最大值要小导致的空项
- WHERE JSON_EXTRACT(ta.json_val, CONCAT('$[', idx ,']')) IS NOT NULL
- ORDER BY idx
- ) tb where JSON_TYPE(tb.element ->'$**.label') IS NOT NULL;
复制代码 请注意这里的1到8就是需要动态天生,联合mybatis框架,可以支持按传入的参数进行天生。
查询出来的结果如下所示:
- 43585 grid grid grid93809
- 90048 input text-field input36308 直径(DN)
- 27039 select select-field select17890 制动方式 [{"label": "手动", "value": 1}, {"label": "电动", "value": 2}, {"label": "气动", "value": "3"}, {"label": "液压控制", "value": 4}, {"label": "其他", "value": 5}]
- 90048 input text-field input30722 压力(MP)
- 31288 select select-field select11081 材质 [{"label": "铸铁", "value": 1}, {"label": "不锈钢", "value": 2}, {"label": "碳钢", "value": 3}, {"label": "铜", "value": 4}, {"label": "其他", "value": 5}]
- 28602 input text-field input21916 其他属性
复制代码 到此,在MySQL5.7下的查询完全实现。剩下的参数匹配等,我们在体系重直接对应节即可。
4、8.0后的优化查询
在mysql8.0之前,mysql没有支持JSON_TABLE的用法,所以我们只用接纳上面的这种处理办法。如果您的项目环境用的是8.0的,那么可以直接利用JSON_TABLE的方法直接天生,更加简单。各位如果有8.0的环境,可以按照以下sql进行实验。
- SELECT
- *
- FROM
- ems_equipment_classification_attr t
- CROSS JOIN JSON_TABLE(
- t.attr_config -> '$**.widgetList',
- '$[*]' COLUMNS(
- value VARCHAR(255) PATH '$[0].options.value',
- label VARCHAR(255) PATH '$[0].options.label',
- name VARCHAR(255) PATH '$[0].options.name',
- type VARCHAR(255) path '$[0].type',
- icon VARCHAR(255) path '$[0].icon',
- requiredHint VARCHAR(255) PATH '$[0].options.requiredHint',
- optionItems json path '$[0].options.optionItems'
- )
- ) AS jt
- where t.equip_class_id = '3';
复制代码 正常执行的话可以得到以下的结果:
- 43585 grid grid grid93809
- 90048 input text-field input36308 直径(DN)
- 27039 select select-field select17890 制动方式 [{"label": "手动", "value": 1}, {"label": "电动", "value": 2}, {"label": "气动", "value": "3"}, {"label": "液压控制", "value": 4}, {"label": "其他", "value": 5}]
- 90048 input text-field input30722 压力(MP)
- 31288 select select-field select11081 材质 [{"label": "铸铁", "value": 1}, {"label": "不锈钢", "value": 2}, {"label": "碳钢", "value": 3}, {"label": "铜", "value": 4}, {"label": "其他", "value": 5}]
- 28602 input text-field input21916 其他属性
复制代码 基本上跟5.7的执行结果差不太多。到此就完成了在MySQL中两个版本的json数据的精准查询的支持。希望对大家有所帮助。
三、总结
以上就是本文的主要内容,本文重点解说如安在将计划好的动态表单信息进行提取,比如进行模板数据导入的时候,可以根据不同的表单范例,比如根据单行文字框的名字来动态设置值,也可以在导数数据时,知道将数据库的性别一列保存的1和2翻译成男和女这两种属性。这都需要我们精准的提取表单中的不同的信息,能精准提取表单的文本、范例、默认值域还有其他的表单元素的设置。通过本文,您可以了解怎样正确的操作动态表单信息,同时了解怎样从表单中查找表单元素。行文仓促,难免有不足之处,欢迎各位专家朋侪品评指正,不甚感激。
参考资料:
1、MySQL5.7从JSON数组提取行的办法。
2、json-function-reference。
3、流程计划器演示地点。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |