可视化表单&试卷搭建平台技术详解

hi, 大家好, 我是徐小夕. 年前和大家分享了新开发的轻量级零代码平台——橙子试卷, 主要用于可视化的搭建表单, 试卷等场景, 让不懂技术的人也能拖拽式创建自己的表单和试卷.

今天就来和大家分享一下橙子试卷的技术架构和技术实现, 如果你也在调研零代码, 低代码, 或者表单引擎等技术, 那么这篇文章也许可以给你带来一些灵感.

体验地址: https://turntip.cn/form-engine

技术栈

之前在开发 H5-dooring 零代码搭建平台时我采用的是React技术栈, 因为用 react 写复杂逻辑会更顺手, 好在 vue3 也支持了函数式的写法, 也提供了 hooks 机制, 让我们写逻辑驱动的应用更加方便, 所以橙子试卷 采用了国内最流行的 vue3 作为前端框架. 具体技术栈如下:

  • Vue3 前端框架
  • Vite 工程化工具
  • Typescript 类型加强
  • arco-design 精美的前端UI库
  • pinia 状态管理库
  • axios 请求库
  • json schema 数据存储规范
  • xijs 面向业务场景的js工具库
  • vue-echarts 数据分析图表库
  • koa2 服务端数据支持
  • koa-jwt 基于koa封装的jwt鉴权库

当然还有一些第三方组件这里就不一一介绍了. 我们的核心在于从零完整设计一套 MVP 的零代码搭建引擎, 所以后端部分, 大家可以替换成自己熟悉的 java, go, python 等语言.

技术架构

因为零代码表单引擎设计的核心是如何构造一套低成本且可扩展的组件库, 并快速应用到实际业务场景, 所以设计的核心就包括如下部分:

  • 可扩展的组件库系统
  • 灵活的拖拽搭建模式
  • 统一标准的DSL数据结构和数据规范
  • 一套可插拔的分析管理系统

接下来我会详细介绍这几块的技术实现, 当然实现思想和技术栈无关, 我们仍然可以把它应用到不同的技术体系中.

具体功能实现

1. 构造可扩展的组件库系统

因为我们的搭建场景是问卷, 试卷, 微页面, 所以组件库会围绕表单来扩展, 比如常用的:

  • 文本, 图片
  • 单选, 多选, 图片选择
  • 下拉框
  • 文本输入框
  • 文件上传
  • 电子签名
  • 评分

等等, 这些组件当然不能满足所有客户的业务场景, 所以我们要设计一种可扩展组件库, 并且开发成本较低的方案.

我们可以参考常见的低代码平台的设计思路:

简言之就是把核心UI和逻辑作为组件的主文件, 同时暴露标准的 的 可配置属性和可配置逻辑.

之所以强调“标准”, 是为了让不同组件能共用一套属性配置引擎, 从而让组件二开的成本大大降低(专注于组件的开发, 而不是配置的兼容).

其次为了尽可能让组件的配置更灵活, 我们需要提供一套标准的组件默认属性, 让用户可以根据默认属性来配置自己的个性化的样式, 那么我们的组件就需要这么来设计:

这里以橙子试卷文本组件给大家举个例子.

文本组件的主文件:

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
html复制代码<template>
<div
:class="{
text: true,
justify: editorStore.data[index].align == 'justify',
[editorStore.data[index].animation]: true,
}"
:style="{
paddingTop: editorStore.data[index].padding[0] + 'px',
paddingRight: editorStore.data[index].padding[1] + 'px',
paddingBottom: editorStore.data[index].padding[2] + 'px',
paddingLeft: editorStore.data[index].padding[3] + 'px',
color: editorStore.data[index].titleColor,
fontSize: editorStore.data[index].titleSize + 'px',
fontWeight: editorStore.data[index].titleWeight,
textAlign: editorStore.data[index].align,
animationDelay: editorStore.data[index].delay,
animationIterationCount: editorStore.data[index].number,
cursor: 'pointer',
}"
@click="jump(editorStore.data[index].link)"
>
{{ editorStore.data[index].titleText }}
</div>
</template>

<script setup lang="ts">
import { useEditorStore } from "@/store";

const editorStore = useEditorStore();

const jump = (link: string) => {
window.location.href = link;
};

defineProps(["index"]);
</script>

文本组件的可配置属性:

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
js复制代码export default class Text {
component: TextPropType;
constructor(id: string) {
this.component = {
component: 'text',
type:'editor.text',
id,
check: true,
titleText: 'https://turntip.cn/form-engine',
titleColor: 'black',
titleSize:16,
titleWeight:'500',
padding: [0, 0, 0, 0],
margin: [10, 10, 10, 10],
animation:'',
direction: 'center',
link:'https://turntip.cn/formManager',
delay:2,
attrbite: [
{
name: 'editor.titleText',
field: 'titleText',
component: 'textarea'
},
{
name: 'editor.padding',
field: 'padding',
component: "padding",
props: {
min: 0,
type:'padding'
}
},
{
name: 'editor.margin',
field: 'margin',
component: "padding",
props: {
min: 0,
type:'margin'
}
},
]
}
}

}

通过这种方式, 我们只需要根据业务需求实现自己的组件, 编写组件可配置的属性json, 即可通过配置引擎来动态生成组件的可编辑面板, 从而让非技术人员轻松编辑组件.

通过以上的方式, 我们可以轻松开发各种自定义的组件, 提供给用户使用:

2. 灵活的拖拽搭建模式

对于表单场景, 我们不需要特别复杂的布局交互, 所以这里我才用拖拽排序来实现页面的搭建, 同时支持组件快捷复制和删除.

目前 vue3 的比较成熟的拖拽组件有:

  • vuedraggable
  • vue3-draggable-resizable

这里选择vuedraggable 来实现拖拽排序, 并对其进行上层封装, 实现体验更好的组件搭建排序效果.

当然还有很多优秀的拖拽库, 如果大家对vue3-draggable-resizable 感兴趣, 也可以试试, 它支持网格布局和自由布局, 可以实现更自由的布局搭建效果:

3. 统一标准的DSL数据结构和数据规范

在组件库设计中我们为了统一管理和维护组件和组件的属性配置, 需要定义统一化的 DSL 结构, 这个结构包含了组件的如下信息:

  • 组件元数据
  • 位置信息
  • 样式信息
  • 事件 / 交互
  • 埋点定义(高级配置)

后4个都好理解, 这里介绍一下组件元数据, 它的价值在于定义组件的基本信息:

  • 组件类型
  • 组件名称
  • 图标
  • 初始化数据
  • 组件路径 (高级, 可选)
  • 状态 / 版本 (高级, 可选)

通过对 元信息 的定义, 我们可以很方便的建立更系统的组件库, 比如支持组件分类, 组件版本切换, 组件加载(通过路径元信息来加载远程组件).

所以我们需要尽可能规范统一的定义组件的通用规则和自定义规范, 以便让不同组件都遵行统一的规则来实现零代码搭建引擎的设计.

这里还是以橙子试卷为例子, 来介绍一下我们统一的DSL:

首先我们看看文本的元信息:

1
2
3
4
5
ts复制代码{
name: 'editor.text', // 组件名称
component: IconLanguage, // 组件图标
type: 'text' // 组件类型
}

这是一个简单的元信息, 它可以帮我们快速识别组件, 并为画布提供组件更具体的渲染信息, 不同组件都通过统一的配置来定义, 可以让我们的渲染器更加高效的渲染组件, 并降低组件维护成本.

在介绍组件的内容中我已经介绍了组件接受的 json 配置结构, 这里分享一个由多个组件组成的完整页面的 DSL 结构和实际代码:

案例代码:

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
swift复制代码{
"id": "5137706621235472",
"title": "2024前端笔试题",
"author": "徐小夕",
"desc": "页面描述",
"icon": "",
"bgColor": "",
"bgImage": "",
"isLogin": false,
"data": [
{
"component": "sign",
"type": "editor.sign",
"id": "JelhSP",
"check": false,
"titleText": "请在此处签名",
"titleColor": "black",
"margin": [
10,
10,
10,
10
],
"limit": 1,
"btnColor": "#165DFF",
"titleSize": 15,
"titleWeight": "500",
"analysis": ""
},
{
"component": "math",
"type": "editor.math",
"id": "3-U9XA",
"check": true,
"titleText": "数学题目:5^9>\\pi",
"titleColor": "#D41C1C",
"options": [
{
"label": "A",
"value": "苹果"
},
{
"label": "B",
"value": "香蕉"
}
],
"symbol": "A,B,C...",
"direction": "horizontal",
"optionsColor": "black",
"analysis": "",
"auto": "",
"margin": [
10,
10,
10,
10
],
"scores": 0,
"required": false
},
]
}

有了以上的统一 DSL 结构, 我们就可以轻松通过 JSON 来渲染页面, 同时也有更多的想象空间, 比如:

  • 通过DSL来实现跨端渲染搭建
  • 通过DSL来实现多人协作共享
  • 通过DSL来实现A出码能力
  • 通过AI学习DSL来批量自动化生产页面

4. 可插拔的分析管理系统

一个可视化零代码解决方案一定包含完整的用户使用链路, 即从搭建到投放再到信息收集的完整分析链路.

当然不同的公司业务分析需求不同, 所以需要支持纯粹的数据收集和流转, 以便供不同业务使用.

目前橙子试卷提供了一套完整的数据收集能力, 对于试卷场景, 也提供了自动打分机制, 可以一键分析数据情况:

当然这都是可以基于自身规则自己二次开发的, 橙子试卷 只是提供了一套案例参考.

应用场景和价值

  • 帮助中小企业或者个体提供一套低成本零代码表单搭建解决方案,且不止于表单/试卷
  • 基于橙子试卷的最佳实践, 轻松扩展出更多的搭建业务场景
  • 开箱即用的组件和模版管理最佳实践, 积累和沉淀内部技术资产
  • 结合AIGC, 创造更强大的生产力工具

体验地址: https://turntip.cn/form-engine

技术交流:

更多推荐

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%