Skip to content

构建复杂表单

这一页从实际建设角度说明如何用 FormX 构建一个可维护的复杂业务表单。

从业务对象开始

先建值树,不要先想 UI。

例如“策略型配置表单”可以拆成:

json
{
  "name": "",
  "enabled": true,
  "scope": {
    "type": "department",
    "targets": []
  },
  "rules": [
    {
      "field": "",
      "method": "",
      "level": "medium",
      "condition": ""
    }
  ]
}

这一步决定 schema 的长期可维护性。值树应该贴近业务模型,而不是贴近某个页面布局。

再映射字段

把每个业务属性映射成字段:

ts
{
  fields: [
    { id: 'name', type: 'input', label: '配置名称' },
    { id: 'enabled', type: 'switch', label: '启用' },
    {
      id: 'scope',
      type: 'form-object',
      label: '适用范围',
      fields: [
        { id: 'type', type: 'select', label: '范围类型' },
        { id: 'targets', type: 'tree-select', label: '目标对象' }
      ]
    },
    {
      id: 'rules',
      type: 'field-group',
      label: '规则明细',
      template: [
        { id: 'field', type: 'select', label: '字段' },
        { id: 'method', type: 'select', label: '方法' },
        { id: 'level', type: 'select', label: '级别' },
        { id: 'condition', type: 'input', label: '条件' }
      ]
    }
  ]
}

用短写处理局部规则

字段自己的规则优先写在字段上:

ts
{
  id: 'condition',
  type: 'input',
  label: '条件',
  showWhen: '$self.method === "conditional"',
  requiredWhen: '$self.method === "conditional"'
}

这样设计器也容易展示和编辑。

rulesV2 管理流程规则

当一个规则影响多个字段时,使用 rulesV2

ts
{
  id: 'scope-targets-by-type',
  watch: ['scope.type'],
  effects: [
    {
      type: 'fetchOptions',
      target: 'scope.targets',
      requestKey: 'getScopeTargets',
      params: { type: '${scope.type}' }
    },
    {
      type: 'setValue',
      target: 'scope.targets',
      value: []
    }
  ]
}

这比在 Vue 里写 watch 更可审查,也能被诊断工具看到。

远程资源单独注册

不要把接口实现写进 schema。

ts
ResourceManager.register('getScopeTargets', async (params) => {
  return api.scopeTargets(params)
})

ResourceManager.register('getProtectMethods', async () => {
  return api.protectMethods()
})

schema 只保存 requestKey、参数模板和映射配置。

自定义组件作为插件

如果标准控件不够,用 custom 字段接入:

ts
{
  id: 'conditionBuilder',
  type: 'custom',
  label: '条件构造器',
  component: 'ConditionBuilder'
}

自定义组件应该遵守 FormX 字段协议,避免绕过 engine 直接改业务对象。

提交前处理

提交时使用组件 API:

ts
const valid = await formRef.value?.validate()
if (!valid) return

const values = formRef.value?.getSubmitValues()
await api.save(values)

如果隐藏字段不应该提交,在 policy 中配置:

ts
{
  policy: {
    omitOnSubmit: true,
    validation: {
      skipHidden: true
    }
  }
}

何时拆 schema

表单大到难以维护时,可以按业务区域拆:

txt
schemas/
  base.ts
  scope.ts
  rules.ts
  validation.ts
  resources.ts

但最终导出的仍然是一份 schema。拆文件是工程组织,不应该破坏运行时协议。

构建 Checklist

  • 值树是否贴近业务对象。
  • 对象和数组是否保留层级。
  • 简单联动是否用短写。
  • 复杂规则是否集中在 rulesV2
  • 资源请求是否统一注册。
  • 异步回填是否处理竞态。
  • 自定义组件是否遵守字段协议。
  • 大表单是否开启诊断。
  • 提交值是否符合后端契约。