第一章:为什么需要低代码?
1.1 业务痛点
| 场景 | 传统开发方式 | 低代码优势 |
|---|---|---|
| 新增报销单 | 提 PR → 开发 3 天 → 测试 → 上线 | 运营 10 分钟配置上线 |
| 客户信息变更流程 | 需求评审 → 排期 → 开发 | 业务自行设计审批链 |
| 临时调研表单 | 占用开发资源 | 自助创建,用完即弃 |
原则 :低代码不是取代开发,而是将重复性 UI/流程抽象为配置。
1.2 架构定位
[业务人员]
↓(使用)
[低代码管理后台] → 生成 → [表单 JSON + 流程定义]
↓ 存储
[PostgreSQL: form_schemas, workflows]
↓
[用户端] ← 动态渲染 ← [Flask API 提供表单元数据]
- 开发人员:维护引擎、安全、扩展能力
- 业务人员:通过 UI 配置表单与流程
第二章:表单协议选型 ------ JSON Schema + UISchema
2.1 为什么不用纯 JSON?
纯结构缺乏语义,难以扩展。采用 JSON Schema(数据模型) + UISchema(UI 描述) 分离:
// JSON Schema(数据校验)
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名" },
"age": { "type": "integer", "minimum": 18 }
},
"required": ["name"]
}
// UISchema(UI 布局)
{
"type": "layout",
"component": "form",
"children": [
{ "component": "input", "field": "name", "props": { "placeholder": "请输入姓名" } },
{ "component": "number-input", "field": "age" }
]
}
优势:
- 数据模型与 UI 解耦
- 支持复杂布局(Tabs、Grid)
- 易于校验与序列化
2.2 前端引擎选型:Formily
| 方案 | 优点 | 缺点 |
|---|---|---|
| React JSONSchema Form | 生态成熟 | 需引入 React |
| Vue Form Generator | 轻量 | 功能有限 |
| Formily(阿里开源) | 高性能、支持 Vue/React、设计器友好 | 学习曲线稍陡 |
选择 Formily :提供
@formily/vue+@formily/core+ 官方设计器组件。
第三章:可视化表单设计器(前端)
3.1 安装依赖
npm install @formily/vue @formily/core @formily/json-schema @formily/element-plus
npm install element-plus # UI 库(可替换为 Ant Design Vue)
3.2 设计器核心组件
<!-- src/views/FormBuilder.vue -->
<template>
<div class="form-builder">
<!-- 左侧:组件库 -->
<div class="components-panel">
<draggable v-for="comp in components" :key="comp.type" group="components">
<ComponentItem :component="comp" />
</draggable>
</div>
<!-- 中间:画布 -->
<FormProvider :form="form">
<FormConsumer>
<template #default="{ schema }">
<FormRenderer :schema="schema" />
</template>
</FormConsumer>
</FormProvider>
<!-- 右侧:属性面板 -->
<PropertyPanel v-model:current-field="currentField" />
</div>
</template>
<script setup lang="ts">
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer } from '@formily/vue'
const form = createForm()
const components = [
{ type: 'input', label: '文本输入', schema: { type: 'string' } },
{ type: 'number', label: '数字输入', schema: { type: 'number' } },
{ type: 'select', label: '下拉选择', schema: { type: 'string', enum: [] } }
]
</script>
3.3 拖拽与绑定
使用 vue-draggable-next 实现组件拖入画布,并生成 Formily Schema:
// utils/schema-generator.ts
export const addFieldToSchema = (schema: any, fieldConfig: FieldConfig) => {
const newField = {
type: 'void',
'x-component': fieldConfig.component,
'x-decorator': 'FormItem',
properties: {
[fieldConfig.name]: {
type: fieldConfig.schema.type,
title: fieldConfig.label,
'x-decorator': 'FormItem',
'x-component': fieldConfig.component,
...fieldConfig.props
}
}
}
schema.properties[fieldConfig.name] = newField.properties[fieldConfig.name]
return schema
}
输出:标准 Formily JSON Schema,可直接用于渲染。
第四章:动态表单渲染(用户端)
4.1 前端渲染器
<!-- src/components/DynamicForm.vue -->
<template>
<FormProvider :form="form">
<SchemaField :schema="formSchema" :components="components" />
</FormProvider>
</template>
<script setup lang="ts">
import { createForm } from '@formily/core'
import { FormProvider, SchemaField } from '@formily/vue'
import { Input, NumberInput, Select } from 'element-plus'
const props = defineProps<{ schema: any }>()
const form = createForm()
const components = { Input, NumberInput, Select }
const formSchema = reactive(props.schema)
</script>
4.2 提交与校验
Formily 内置校验,提交时获取值:
const handleSubmit = async () => {
try {
const values = await form.validateFields() // 自动校验
await axios.post('/api/forms/submit', {
form_id: props.formId,
data: values
})
} catch (err) {
// 显示校验错误
}
}
第五章:后端动态处理(Flask)
5.1 表单模型设计
# models.py
class FormSchema(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
json_schema = db.Column(JSONB) # PostgreSQL JSON 类型
ui_schema = db.Column(JSONB)
created_by = db.Column(db.Integer, db.ForeignKey('user.id'))
class FormData(db.Model):
id = db.Column(db.Integer, primary_key=True)
form_id = db.Column(db.Integer, db.ForeignKey('form_schema.id'))
data = db.Column(JSONB) # 用户提交的原始数据
status = db.Column(db.String(20), default='draft') # draft, submitted, approved
workflow_state = db.Column(db.String(50)) # 当前流程节点
5.2 动态校验与存储
# api/forms.py
from jsonschema import validate, ValidationError
@app.route('/api/forms/<int:form_id>/submit', methods=['POST'])
@jwt_required()
def submit_form(form_id):
form_schema = FormSchema.query.get_or_404(form_id)
user_data = request.json['data']
# 使用 jsonschema 校验
try:
validate(instance=user_data, schema=form_schema.json_schema)
except ValidationError as e:
return {"error": str(e)}, 400
# 保存
form_data = FormData(
form_id=form_id,
data=user_data,
status='submitted'
)
db.session.add(form_data)
db.session.commit()
# 触发工作流
start_workflow(form_data.id)
return {"message": "Submitted", "data_id": form_data.id}
依赖 :
pip install jsonschema
第六章:轻量级工作流引擎
6.1 流程定义模型
class WorkflowDefinition(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
steps = db.Column(JSONB) # [{"name": "主管审批", "role": "manager"}, ...]
# 示例 steps:
# [
# {"step_name": "提交", "actor": "submitter", "action": "submit"},
# {"step_name": "一级审批", "actor_role": "finance", "action": "approve/reject"},
# {"step_name": "归档", "actor": "system", "action": "archive"}
# ]
6.2 状态机驱动
# services/workflow.py
def start_workflow(data_id):
data = FormData.query.get(data_id)
workflow = WorkflowDefinition.query.filter_by(form_id=data.form_id).first()
if not workflow:
data.status = 'completed'
return
# 初始化到第一步
data.workflow_state = workflow.steps[0]['step_name']
db.session.commit()
def approve_step(data_id, user_id):
data = FormData.query.get(data_id)
workflow = get_workflow_for_form(data.form_id)
current_step_index = get_current_step_index(workflow, data.workflow_state)
next_step = workflow.steps[current_step_index + 1]
# 权限校验:当前用户是否有权审批
if not user_has_role(user_id, next_step['actor_role']):
raise PermissionError("No permission")
# 更新状态
data.workflow_state = next_step['step_name']
if is_last_step(workflow, next_step):
data.status = 'approved'
db.session.commit()
6.3 前端流程展示
<template>
<el-steps :active="currentStepIndex" finish-status="success">
<el-step v-for="step in workflowSteps" :key="step.name" :title="step.name" />
</el-steps>
<DynamicForm v-if="!isCompleted" :schema="formSchema" @submit="handleSubmit" />
</template>
第七章:字段级权限控制
7.1 权限模型扩展
在 UISchema 中增加权限字段:
{
"component": "input",
"field": "salary",
"permissions": {
"visible": ["hr", "manager"],
"editable": ["hr"]
}
}
7.2 后端过滤敏感字段
def filter_schema_by_role(schema, user_roles):
"""移除用户无权查看的字段"""
filtered_props = {}
for field, config in schema['properties'].items():
visible_roles = config.get('permissions', {}).get('visible', [])
if not visible_roles or set(user_roles) & set(visible_roles):
filtered_props[field] = config
return {**schema, 'properties': filtered_props}
@app.route('/api/forms/<int:form_id>/schema')
@jwt_required()
def get_form_schema(form_id):
schema = FormSchema.query.get_or_404(form_id)
user_roles = get_current_user_roles()
filtered = filter_schema_by_role(schema.ui_schema, user_roles)
return jsonify(filtered)
7.3 前端禁用不可编辑字段
<!-- DynamicForm.vue -->
<SchemaField
:schema="applyPermissions(formSchema, userRoles)"
:components="components"
/>
// applyPermissions.ts
export const applyPermissions = (schema: any, roles: string[]) => {
const clone = _.cloneDeep(schema)
Object.values(clone.properties).forEach((field: any) => {
const perms = field.permissions || {}
if (perms.editable && !roles.some(r => perms.editable.includes(r))) {
field['x-component-props'] = { ...field['x-component-props'], disabled: true }
}
})
return clone
}
第八章:操作审计与版本管理
8.1 审计日志模型
class AuditLog(db.Model):
id = db.Column(db.Integer, primary_key=True)
action = db.Column(db.String(50)) # 'form_create', 'form_submit', 'workflow_approve'
user_id = db.Column(db.Integer)
target_id = db.Column(db.Integer) # 关联 form_id 或 data_id
details = db.Column(JSONB) # 包含旧值/新值
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
8.2 自动记录关键操作
# 在 submit_form 中
log_audit(
action='form_submit',
user_id=get_jwt_identity(),
target_id=form_data.id,
details={'form_id': form_id, 'data_snapshot': user_data}
)
8.3 表单版本控制
当业务人员修改表单设计时,不覆盖原版,而是创建新版本:
class FormVersion(db.Model):
id = db.Column(db.Integer, primary_key=True)
form_id = db.Column(db.Integer, db.ForeignKey('form_schema.id'))
version = db.Column(db.String(20)) # v1, v2, ...
json_schema = db.Column(JSONB)
is_active = db.Column(db.Boolean, default=False)
提交数据时 :关联具体
version_id,确保历史数据可追溯。
第九章:安全与性能考量
9.1 防止恶意 Schema
- 后端校验 Schema 结构 :仅允许白名单组件(
input,select...) - 限制嵌套深度:防止递归爆炸
- XSS 防护 :渲染时对
title、description进行 HTML 转义
9.2 性能优化
-
缓存表单 Schema :Redis 缓存
form_id → schema -
懒加载大表单:分步骤渲染(Wizard 模式)
-
索引优化 :
FormData.data字段建立 GIN 索引(PostgreSQL)CREATE INDEX idx_form_data_gin ON form_data USING GIN (data);
第十章:管理后台与用户体验
10.1 业务人员工作台
- 表单列表:创建/编辑/复制表单
- 流程设计器:拖拽审批节点,设置角色
- 数据看板:查看提交记录、审批状态
10.2 开发者扩展点
-
自定义组件注册 :
// 开发者可注册新组件 registerCustomComponent('address-picker', AddressPicker) -
API 扩展:通过 Webhook 触发外部系统