Python Web 开发进阶实战:低代码平台集成 —— 可视化表单构建器 + 工作流引擎实战

第一章:为什么需要低代码?

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 防护 :渲染时对 titledescription 进行 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 触发外部系统


总结:低代码 ≠ 无代码,而是"高生产力开发"

相关推荐
数智化管理手记2 小时前
精益生产中的TPM管理是什么?一文破解设备零故障的密码
服务器·网络·数据库·低代码·制造·源代码管理·精益工程
kyriewen113 小时前
你点的“刷新”是假刷新?前端路由的瞒天过海术
开发语言·前端·javascript·ecmascript·html5
极梦网络无忧4 小时前
OpenClaw 基础使用说明(中文版)
python
codeJinger4 小时前
【Python】操作Excel文件
python·excel
XLYcmy5 小时前
一个针对医疗RAG系统的数据窃取攻击工具
python·网络安全·ai·llm·agent·rag·ai安全
Islucas5 小时前
Claude code入门保姆级教程
python·bash·claude
skywalk81635 小时前
Kotti Next的tinyfrontend前端模仿Kotti 首页布局还是不太好看,感觉比Kotti差一点
前端
萝卜白菜。5 小时前
TongWeb7.0相同的类指明加载顺序
开发语言·python·pycharm
赵钰老师5 小时前
【ADCIRC】基于“python+”潮汐、风驱动循环、风暴潮等海洋水动力模拟实践技术应用
python·信息可视化·数据分析