跳转至

表单定义编写指南

目标:Agent 直接生成/修改 workspace 表单定义。知识以本文为准。不写原理;不依赖外链。

上游流水线(有 SCHEMA/PLAN 时遵守;小改动可跳过):

SCHEMA 定库表 → PLAN 定蓝图 → form + .create_form_table 落事务表

事务表字段以已确认的 DATABASE_SCHEMA.md / PLAN.md 为准;本技能负责 .form 落盘并投递 .create_form_table。库表设计见 design-database-schema;应用蓝图见 plan-application

iScript:写 valuescript / optionsscript / hiddenscript / readonlyscript / 校验 / 操作前后置等时,先读 iscript-usage(含 GraalVM 差异)→ form.mdactivity.md;本文只定属性名与落盘。

产出物

部分 落盘 形态
表单元数据+模板 {表单名}.form JAXB XML 根 Formtemplatecontext 为 CDATA 字符串
工具栏操作 {表单名}.form/{操作名}.activity XML 根 activity(或设计态 API 的 Activity JSON,字段同名)
/{应用名}.application/module/{模块名}.module/{表单名}.form
/{应用名}.application/module/{模块名}.module/{表单名}.form/{操作名}.activity

新建最低配置:

  1. .formidnametype=1showType=new、非空 templatecontext
  2. templatecontext:合法 JsonTemplate(根键 fields + layout);普通字段默认用两栏容器布局(见「布局约定」)
  3. 常用:至少一个保存操作 type=34.activity
  4. 落盘后收尾(见「动态表同步触发」):type∈{1,3,16} 时向 workspace/.sync/ 写入 *.create_form_table(内容=.form 内层文件 URI {名}.form/{名}.form)触发表结构创建/更新;确认进 .done/;另建议 rebuild 索引供热加载可见

约定:

  • Form/Activity 元数据:camelCase(API JSON)或同名 XML 子元素;脚本用 CDATA
  • JsonTemplate 字段属性:设计器 小写键hiddenscripttexttypedatepatternviewidacttype
  • id 生成规则:根元素 id(表单根 <Form id>、工具栏 .activity<activity id> —— 均为独立文件根、进 url.index)= __ + 短 UUID(示例 __MpEzTToulqZtNEFisw6);其它 id(templatecontext 内**字段 id**、布局节点 formPanel/container/twoColumnContainer/fieldRef 的 id)= 短 UUID(无 __。字段 name 用英文标识 → 列名 ITEM_+大写 name
  • 布尔 true/false;勿输出 UI 缓存键:editPropviewsoptionsoptionstextoptionsprocessprevalueformsOptions
  • 日期 scope=dataField(不是 dateField);上传=attachmentField/imageuploadField/kmdataField

1. Form 属性 → .form

属性表

属性 类型 默认 说明
id string 须生成 __ + 短 UUID
name string 必填;文件名;动态表 TLK_{name}
type int 1 见下表;保存后勿改
templatecontext CDATA/string JsonTemplate 字符串;或历史 HTML
showType string old JsonTemplate 须 new
styleId string 样式库 id
permissionType string public public/private
orderno int 1 模块内排序
description / remark string 描述
isopenablescript CDATA 可打开;空≈true
iseditablescript CDATA 可编辑
confirmLeaveEdit bool false 离开确认
showWaterMark bool false
waterMarkScript CDATA
openComment bool false
commentTitleScript / commentFlagScript / commentHiddenScrtipt / commentExecuteScript CDATA 注意 Hidden Scrtipt 拼写
showLog bool false
showLogType string default default/table
recordMode string every every/other/script
recordModeScript CDATA
mappingStr string/CDATA 仅 type=65536 必填;JSON 见「创建映射表单」
layoutType string mobile:horizontal/vertical
sortId string
version int 保存自增
parentId / applicationid string 模块/应用(与系统基类一致时写入)

type

含义 建动态表
1 普通 TLK_
2 标签页片段
3 流程参数 PARM_
4 数据模型 外部
16 子表单 TLK_
256 查询表单
4096 首页
65536 映射表单 (映射已有业务表,见下节)
1048576 模板表单

动态表规则(写字段时必须遵守)

  • 存值:节点实现 ValueStoreField 且 onlyCalculate!=true → 列 ITEM_{NAME大写}映射表单除外:列名由 mappingStr.columnMappings 指定,无 ITEM_
  • $ 开头字段名:列名为去 $ 后大写(无 ITEM_ 前缀)
  • fieldtype→列类型:VARCHAR / NUMBER / DATE / TEXT(CLOB) / BLOB
  • 查询/片段/模板表单不建业务动态表;映射表单不建 TLK_,读写目标表

动态表同步触发(表单创建/改字段后必做)

Agent / 手工落盘 .form **不走**设计器 save API,平台**不会**自动创建或更新动态表(TLK_* / PARM_*)。表单定义写完后,须额外投递 .create_form_table 触发同步;否则运行时无法按字段落库。

机制与目录约定见 docs/design/workspace-structure-and-mechanism.md §6.2、§7.4。

何时投递

场景 是否投递
新建 type ∈ {1, 3, 16} 的 .form 必须
已有上述表单增删改存值字段(影响列) 必须(再次投递即可更新表结构)
type=65536 映射表单 不投递
查询/片段/模板/首页/数据模型等(不建业务动态表) 不投递

前提

  • 所属软件已 activated=true,且默认数据源可用(否则同步会跳过或失败)。
  • 监视仅 Runtime / Manager / Job 启用;Designer 不处理 .sync/。投递后需对应服务在跑。
  • **不要求**事先 rebuild 索引;同步按文件 URI 读盘加载 Form,不查 url.index

Agent 收尾步骤(按序)

  1. 写完并保存 {表单名}.form(及所需 .activity)。
  2. workspace/.sync/ **新建**触发文件(勿把手写业务 XML 放进该目录):
约定
路径 {myapps.storage.root}/workspace/.sync/{任意前缀}.create_form_table
内容 一行纯文本:.form 内层同名文件**的逻辑 URI(相对 workspace 根,以 / 开头);必须指到 {表单名}.form/{表单名}.form,**不能只写到 .form 目录
前缀 建议用表单名,如 LeaveRequest.create_form_table
# 文件:workspace/.sync/LeaveRequest.create_form_table
# 注意:URI 必须指到 .form 内层同名文件,不是 .form 目录本身;
# 只写目录会导致 VirtualFileSystemUtils 报「文件 '.../*.form' 解析失败」并归档 .failed/
/LeaveOA.application/module/Leave.module/LeaveRequest.form/LeaveRequest.form
  1. 等待监视器处理:读 URI → 读盘 JAXB 加载 Form → FormTableProcessBean.createOrUpdateDynaTable → 创建/更新动态表。
  2. 校验结果:成功则文件移入 .sync/.done/;失败移入 .sync/.failed/。失败时先查数据源/软件激活/URI 是否正确,修正后再投递一份新文件(勿改 .failed 内旧文件指望重试)。
  3. (建议)再执行 rebuild-index / PUT /indexs/rebuild,使 Runtime 经 url.index 热加载可见定义;与动态表同步**无强制先后**。
写入 .sync/{name}.create_form_table
  → SyncMonitorListener(或启动 drain)
  → 读 form URI → 读盘加载 Form
  → createOrUpdateDynaTable
  → 成功 → .sync/.done/;失败 → .sync/.failed/

.form XML 骨架

<?xml version="1.0" encoding="UTF-8"?>
<Form>
  <id>FORM_UUID</id>
  <name>LeaveRequest</name>
  <type>1</type>
  <showType>new</showType>
  <styleId></styleId>
  <permissionType>public</permissionType>
  <orderno>1</orderno>
  <description><![CDATA[请假申请]]></description>
  <showLog>false</showLog>
  <showWaterMark>false</showWaterMark>
  <openComment>false</openComment>
  <confirmLeaveEdit>false</confirmLeaveEdit>
  <isopenablescript><![CDATA[]]></isopenablescript>
  <iseditablescript><![CDATA[]]></iseditablescript>
  <templatecontext><![CDATA[{"fields":[],"layout":{"pc":{"formPanel":{"scope":"formPanel","id":"fp-001","name":"表单1","myselfrows":100,"containerwidth":500,"children":[]}},"mobile":null}}]]></templatecontext>
</Form>

API 保存时用同名字段的 JSON;templatecontext 仍是字符串。

创建映射表单(type=65536)

映射表单把表单字段绑到**应用数据源中已有业务表**(非平台 TLK_/PARM_/auth_),用于查询/增删改该表;**不**生成 TLK_{name},**不**投递 .create_form_table

步骤

  1. 确认目标表已存在,且非 auth_ / tlk_ / parm_ 前缀表;记下表名与主键列名(常见 ID)。
  2. .formtype=65536showType=new、非空 templatecontext(字段/布局与普通表单相同)。
  3. mappingStr(CDATA JSON,见下);缺此字段或主键映射不全则设计器校验失败。
  4. 常用:至少一个保存类 .activity(如 type=34)。
  5. **禁止**投递 .create_form_table;建议 rebuild-index 供热加载。

mappingStr 结构

{
  "formName": "mapping_test",
  "tableName": "mapping",
  "columnMappings": [
    {"fieldName": "name", "columnName": "NAME"},
    {"fieldName": "remark", "columnName": "REMARK"},
    {"fieldName": "MAPPINGID", "columnName": "ID"}
  ]
}
说明
formName 必须等于 Form.name
tableName 目标物理表名(已存在)
columnMappings 字段↔列映射数组

columnMappings 项:

说明
fieldName 表单字段 name;**主键固定**用字面量 MAPPINGID(**不要**在 templatecontext.fields 里画同名字段)
columnName 物理列名;ITEM_ 前缀(如 NAMEREMARK、主键 ID

硬约束:

  • **必须**含一条 { "fieldName":"MAPPINGID", "columnName":"<主键列>" }(主键列按实际表,多为 ID
  • 每个存值业务字段都应有对应映射;fieldNamefields 中字段 name 一致
  • 列名不可与平台文档系统固定列冲突(见设计器校验:须选表名、主键;列映射成对)
  • XML 落盘:<mappingStr><![CDATA[{...}]]></mappingStr>

最小 .form 骨架(映射)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<form id="__FORM_UUID">
  <name>mapping_test</name>
  <parentId>__MODULE_ID</parentId>
  <type>65536</type>
  <showType>new</showType>
  <permissionType>public</permissionType>
  <orderno>1</orderno>
  <templatecontext><![CDATA[{"fields":[...],"layout":{...}}]]></templatecontext>
  <mappingStr><![CDATA[{"formName":"mapping_test","tableName":"mapping","columnMappings":[{"fieldName":"name","columnName":"NAME"},{"fieldName":"remark","columnName":"REMARK"},{"fieldName":"MAPPINGID","columnName":"ID"}]}]]></mappingStr>
</form>

程序化生成优先 scripts/form_template_builder.pyform_type=65536 + mapping_str=ftb.build_mapping_str(...)(或传入 JSON 字符串/对象)。


2. templatecontext → JsonTemplate

结构硬规则

根对象 有且仅有 fieldslayout 两个键(**不再**使用旧根级 { formPanel }):

{
  "fields": [
    {
      "inputField": {
        "scope": "inputField",
        "id": "fld-001",
        "name": "title",
        "myselfrows": 100,
        "fieldtype": "VALUE_TYPE_VARCHAR",
        "texttype": "text"
      }
    },
    {
      "dataField": {
        "scope": "dataField",
        "id": "fld-002",
        "name": "startDate",
        "myselfrows": 50,
        "fieldtype": "VALUE_TYPE_DATE",
        "datepattern": "YMD",
        "texttype": "text"
      }
    }
  ],
  "layout": {
    "pc": {
      "formPanel": {
        "scope": "formPanel",
        "id": "fp-001",
        "name": "表单1",
        "myselfrows": 100,
        "containerwidth": 500,
        "style": {"border":"none","padding":"20px 20px 40px","margin":"0 auto","width":"100%"},
        "children": [
          {
            "twoColumnContainer": {
              "scope": "twoColumnContainer",
              "id": "tc-001",
              "name": "两栏容器1",
              "myselfrows": 100,
              "style": {"display":"flex","flex-direction":"row","flex-wrap":"nowrap","width":"100%","min-height":"60px"},
              "children": [
                {
                  "container": {
                    "scope": "container",
                    "id": "col-1",
                    "name": "容器1",
                    "direction": "row",
                    "children": [
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "fld-001" } }
                    ]
                  }
                },
                {
                  "container": {
                    "scope": "container",
                    "id": "col-2",
                    "name": "容器2",
                    "direction": "row",
                    "children": [
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "fld-002" } }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    },
    "mobile": null
  }
}
约定
根键 fields + layout;禁止旧根级 formPanel
fields 数组;元素为 { [scope]: { scope, id, props..., style? } }(字段完整定义)
layout.pc { formPanel: formPanelNode };容器与布局关系在此树
layout.mobile 固定 null
布局内字段 { fieldRef: { scope: "fieldRef", fieldId } }fieldId === fields 中字段 id
节点属性 propValues 平铺;样式可选 style 对象
children 仅布局节点(formPanel / container / 多栏等);每项 单键对象,键=scope
tabField 完整定义在 fields;页签用 relstr,不用 children 装页签体;布局中为 fieldRef
写入 Form JSON.stringify(对象) 放进 templatecontext

布局约定(新建表单默认)

默认配方:普通字段一律进两栏容器,禁止把普通 fieldRef 直接挂在 formPanel.children

步骤 做法
1 formPanel.children 放一个或多个纵向堆叠的 twoColumnContainer
2 每个 twoColumnContainerchildren 固定两个 container(名建议「栏1」「栏2」)
3 普通字段的 fieldRef **成对**放入左栏/右栏(一对字段 = 一个两栏容器)
4 字段较多时继续追加 twoColumnContainer,不要改成单列直挂

全宽例外(textarea / attachment / viewdialog 等):挂在 formPanel.children 下的 单独 containerstyle.width=100%),不要裸 fieldRef 直挂。

两栏布局硬规则(设计器对齐;错了会错乱)

运行/设计器按下列属性渲染;width:50% / flex:1 1 50%style 用 camelCase 会导致两栏错乱。

  1. style 键必须 kebab-caseflex-directionpadding-top……禁止 flexDirectionpaddingTop
  2. 栏容器(左右 containermyselfrows=50stylewidth:50%min-width:50%flex:1 1 50%box-sizing:border-boxdisplay:flex
  3. twoColumnContainer.styledisplay:flexflex-direction:rowflex-wrap:nowrapwidth:100%min-height:60px
  4. 字段:补 paddingtop/right/bottom/left(5/10/10/10)及对应 style.padding-*
  5. formPanel:补 directionpadding*containerHeightborderwidthcontainerwidth

最小可用两栏骨架(与设计器导出一致):

{
  "fields": [
    { "inputField": { "scope": "inputField", "id": "fld-left", "name": "a", "myselfrows": 100, "showTitle": true, "layout": "horizontal", "paddingtop": 5, "paddingright": 10, "paddingbottom": 10, "paddingleft": 10, "fieldtype": "VALUE_TYPE_VARCHAR", "width": 100, "widthunit": "%", "style": { "padding-top": "5px", "padding-right": "10px", "padding-bottom": "10px", "padding-left": "10px" } } },
    { "inputField": { "scope": "inputField", "id": "fld-right", "name": "b", "myselfrows": 100, "showTitle": true, "layout": "horizontal", "paddingtop": 5, "paddingright": 10, "paddingbottom": 10, "paddingleft": 10, "fieldtype": "VALUE_TYPE_VARCHAR", "width": 100, "widthunit": "%", "style": { "padding-top": "5px", "padding-right": "10px", "padding-bottom": "10px", "padding-left": "10px" } } }
  ],
  "layout": {
    "pc": {
      "formPanel": {
        "scope": "formPanel",
        "id": "fp-1",
        "name": "表单1",
        "myselfrows": 100,
        "direction": "row",
        "paddingtop": 10,
        "paddingright": 10,
        "paddingbottom": 10,
        "paddingleft": 10,
        "containerHeight": 60,
        "borderwidth": 0,
        "containerwidth": 500,
        "style": { "flex-direction": "row", "padding-top": "10px", "padding-right": "10px", "padding-bottom": "10px", "padding-left": "10px" },
        "children": [
          {
            "twoColumnContainer": {
              "scope": "twoColumnContainer",
              "id": "tc-1",
              "name": "两栏容器1",
              "myselfrows": 100,
              "direction": "row",
              "paddingtop": 10,
              "paddingright": 10,
              "paddingbottom": 10,
              "paddingleft": 10,
              "containerHeight": 60,
              "borderwidth": 0,
              "style": {
                "display": "flex",
                "flex-direction": "row",
                "flex-wrap": "nowrap",
                "min-height": "60px",
                "width": "100%",
                "padding-top": "10px",
                "padding-right": "10px",
                "padding-bottom": "10px",
                "padding-left": "10px"
              },
              "children": [
                {
                  "container": {
                    "scope": "container",
                    "id": "col-1",
                    "name": "栏1",
                    "myselfrows": 50,
                    "direction": "row",
                    "paddingtop": 1,
                    "paddingright": 1,
                    "paddingbottom": 1,
                    "paddingleft": 1,
                    "containerHeight": 60,
                    "borderwidth": 0,
                    "style": {
                      "display": "flex",
                      "flex-direction": "row",
                      "flex-wrap": "wrap",
                      "min-height": "60px",
                      "width": "50%",
                      "min-width": "50%",
                      "flex": "1 1 50%",
                      "box-sizing": "border-box",
                      "padding-top": "1px",
                      "padding-right": "1px",
                      "padding-bottom": "1px",
                      "padding-left": "1px"
                    },
                    "children": [{ "fieldRef": { "scope": "fieldRef", "fieldId": "fld-left" } }]
                  }
                },
                {
                  "container": {
                    "scope": "container",
                    "id": "col-2",
                    "name": "栏2",
                    "myselfrows": 50,
                    "direction": "row",
                    "paddingtop": 1,
                    "paddingright": 1,
                    "paddingbottom": 1,
                    "paddingleft": 1,
                    "containerHeight": 60,
                    "borderwidth": 0,
                    "style": {
                      "display": "flex",
                      "flex-direction": "row",
                      "flex-wrap": "wrap",
                      "min-height": "60px",
                      "width": "50%",
                      "min-width": "50%",
                      "flex": "1 1 50%",
                      "box-sizing": "border-box",
                      "padding-top": "1px",
                      "padding-right": "1px",
                      "padding-bottom": "1px",
                      "padding-left": "1px"
                    },
                    "children": [{ "fieldRef": { "scope": "fieldRef", "fieldId": "fld-right" } }]
                  }
                }
              ]
            }
          }
        ]
      }
    },
    "mobile": null
  }
}

三/四栏容器仅在用户明确要求时使用;未指定时用两栏。

公共属性块(存值字段合并此块)

记为 BASE(所有字段几乎都有;非存值可省略 fieldtype/校验/值脚本):

{
  "id": "UUID",
  "name": "fieldName",
  "myselfrows": 100,
  "showTitle": true,
  "layout": "horizontal",
  "paddingtop": 5,
  "paddingright": 10,
  "paddingbottom": 10,
  "paddingleft": 10,
  "texttype": "text",
  "editmode": "01",
  "valuescript": "",
  "processdescription": "",
  "filtercondition": "",
  "isdefaultvalue": false,
  "onlyCalculate": false,
  "validatelibs": [],
  "validaterule": "",
  "instantvalidate": false,
  "hiddenscript": "",
  "hiddenvalue": "",
  "hiddenprintscript": "",
  "printhiddenvalue": "",
  "readonlyscript": "",
  "refreshonchanged": false,
  "calculateonrefresh": false,
  "refreshmode": "0",
  "refreshfields": [],
  "mobile": true,
  "discript": "",
  "placeholder": "",
  "readonlyshowvalonly": false
}

OPTIONS(radio/checkbox/select/selectabout/suggest):

{
  "optionseditmode": "01",
  "optionsscript": "[{text:'选项A',value:'a'}]",
  "module": "",
  "dialogview": "",
  "optionsvalue": "",
  "optionstext": ""
}
  • optionseditmode=01:脚本选项 → optionsscript
  • optionseditmode=00:视图选项 → 填 moduledialogviewoptionsvalue/optionstext

fieldtype 枚举VALUE_TYPE_VARCHAR | VALUE_TYPE_NUMBER | VALUE_TYPE_DATE | VALUE_TYPE_TEXT | VALUE_TYPE_BLOB | VALUE_TYPE_INCLUDE

texttypetext | password | readonly | hidden | number(input 数字模式)

editmode01=脚本;设计模式用 processdescription(格式 "[];[]"


3. 全部 scope 与默认属性

下列字段样例为 fields 数组中的单键包裹节点(完整 JsonTemplate 还须含 layout,布局内用 fieldRef 引用字段 id)。store=Y 须有 fieldtype

布局(仅出现在 layout.pc.formPanel 树)

scope 要点 节点示例
formPanel layout.pc 下唯一根 见上;children 必有(可 []
fieldRef 字段占位 { "fieldRef": { "scope":"fieldRef", "fieldId":"字段id" } };定义在 fields
container 列/通用 direction:rowcontainerHeightborderwidthpadding*children
twoColumnContainer 默认布局 style.display=flexchildren 两个 container;普通字段放列内
threeColumnContainer / fourColumnContainer 三/四栏 仅用户明确要求时用;同两栏,列数 ¾

两栏 + 字段占位(新建默认形态):

{
  "fields": [],
  "layout": {
    "pc": {
      "formPanel": {
        "scope": "formPanel",
        "id": "fp-001",
        "name": "表单1",
        "myselfrows": 100,
        "children": [
          {
            "twoColumnContainer": {
              "scope": "twoColumnContainer",
              "id": "UUID",
              "name": "两栏容器1",
              "myselfrows": 100,
              "style": {"display":"flex","flex-direction":"row","flex-wrap":"nowrap","width":"100%","min-height":"60px"},
              "children": [
                {
                  "container": {
                    "scope": "container",
                    "id": "UUID",
                    "name": "容器1",
                    "direction": "row",
                    "children": [
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "字段id左" } }
                    ]
                  }
                },
                {
                  "container": {
                    "scope": "container",
                    "id": "UUID",
                    "name": "容器2",
                    "direction": "row",
                    "children": [
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "字段id右" } }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    },
    "mobile": null
  }
}

输入类 store=Y(写入 fields

inputField — 单行文本 — 默认 VARCHAR

特有:fieldtypewidth(100)、widthunit(%)、numberPattern(00.00)、defaultValueIsNull(false)、fieldkeyevent(Tabkey)、worklimitchecked(false)、textareamaxlimitsecretText(false)

数字:fieldtype=VALUE_TYPE_NUMBERtexttype=number

{
  "inputField": {
    "scope": "inputField",
    "id": "UUID",
    "name": "title",
    "myselfrows": 100,
    "showTitle": true,
    "layout": "horizontal",
    "fieldtype": "VALUE_TYPE_VARCHAR",
    "texttype": "text",
    "width": 100,
    "widthunit": "%",
    "numberPattern": "00.00",
    "defaultValueIsNull": false,
    "fieldkeyevent": "Tabkey",
    "worklimitchecked": "false",
    "textareamaxlimit": "",
    "secretText": false,
    "editmode": "01",
    "valuescript": "",
    "isdefaultvalue": false,
    "onlyCalculate": false,
    "validatelibs": [],
    "validaterule": "",
    "instantvalidate": false,
    "hiddenscript": "",
    "hiddenvalue": "",
    "hiddenprintscript": "",
    "printhiddenvalue": "",
    "readonlyscript": "",
    "refreshonchanged": false,
    "calculateonrefresh": false,
    "refreshmode": "0",
    "refreshfields": [],
    "mobile": true,
    "discript": "",
    "placeholder": ""
  }
}

textareaField — TEXT

特有:rowstextareaheighttextareawidthwidthunit

radioField — VARCHAR + OPTIONS

特有:layout=horizontal + OPTIONS

checkboxField — TEXT + OPTIONS

多值 ; 分隔;+ OPTIONS

selectField — VARCHAR + OPTIONS

特有:multiselect(false)、selectleafnodesonly(false)、selectwidthwidthunit + OPTIONS

dataField — DATE(日期!)

特有:datepattern(默认设计器 YM;常用 YMD)、limit(false)、prev_namewidth/widthunit

datepatternY|YM|YMD|YMD_HM|YMD_HMS|HMS

{
  "dataField": {
    "scope": "dataField",
    "id": "UUID",
    "name": "startDate",
    "myselfrows": 100,
    "fieldtype": "VALUE_TYPE_DATE",
    "datepattern": "YMD",
    "limit": false,
    "prev_name": "",
    "texttype": "text",
    "width": "200",
    "widthunit": "px",
    "editmode": "01",
    "valuescript": "",
    "hiddenscript": "",
    "readonlyscript": "",
    "mobile": true
  }
}

deptField — VARCHAR

特有:relatedfieldselectleafnodesonly(false)、limitbyuser("false")、defaultoptiontype(16)、allowempty(false)、selectwidth

treedepartmentField — TEXT

特有:limitselectleafnodesonly(false)、width/widthunit

userField — TEXT

特有:selectmode(默认 multiSelect)、width/widthunit

selectaboutField — VARCHAR + OPTIONS

查询表单禁止含此字段。

suggestField — VARCHAR + OPTIONS

特有:datamode=local|remotewidth/widthunit

surveyField — TEXT

特有:questionscript(问卷脚本,必填业务内容)

attachmentField — TEXT

特有:limitsizefiletype(00全/01自定义)、customizetypelimitnumber(10)、filepattern(00/01)、filecatalogpreviewedit(true)、openwatermark(false)、watermarksupportmodewatermarkscriptshowtrackrevisions(true)、showusernameanddate(true)、downloadscript/deletescript/renamescript/previewscript

filepattern=01filecatalog 必填;filetype=01customizetype 必填。

kmdataField — TEXT

类似附件 + supportsorting;默认 watermarksupportmode=preview,print,download

imageuploadField — TEXT

特有:imgh/imgw(100)、limitsizelimitnumber(10)、filepattern/filecatalogishidetype/hidetype

onlinetakephotoField — TEXT

特有:imgh/imgw(100)、album(false)

weixingpsField — VARCHAR

最小:BASE 精简 + fieldtype

weixinrecordField — TEXT

mapField — TEXT

特有:maptypeopentypevaluetypePoint|LineString|Bounds|Polygon)、defaultcenteraddresslevel(1–18)

genericwordField — TEXT

特有:opentype(1)、showtrackrevisions(true)

htmleditorField — TEXT

特有:areawidthareaheight

展示/按钮(写入 fields

noField — store=Y TEXT 流水号

特有:headTextdigit(1)、isYear/isMonth/isDay(true)、width(200)、calculateonrefresh(true)

splitField — store=N

特有:heighttitleScripthiddenscript/hiddenvalue

labelField — store=N

特有:fontfamilyfontsize(14)、fontcolor(#3d464d)、fontbold(true)、fontitalic/fontunderline/fontstrikethrough(false);无标题栏脚本块

qrcodeField — store=N

特有:handletype(text)、size(200)、valuescriptcallbackscript

calctextField — store=N

特有:valuescriptcalculateonrefresh;(不落库)

flowhistoryField — store=N

特有:showmode(必填):text|diagram|textAndDiagram

flowreminderhistoryField — store=N

commentField — store=N

特有:titlewidth(200)、calculateonrefresh(true)

buttonField — store=N(模板内按钮,≠工具栏 Activity)

特有:labelcolorType(default)、acttype(动作类型数字字符串,同 Activity type;勿为 0)、beforeactionscript/afteractionscript/actionscriptstatetoshowactionselectionrelatedformidactiontype(0无/1返回/2关闭/3跳转)、actiondispatcherurlscriptjumpmodetargetlist(formselect/moduleselect)、dispatcherurldispatcherparamsjumpactopentypefilenamescripttranspondactionprintwithold、签章相关 signaturetype/signatureaction/signaturePosScript/datafield

{
  "buttonField": {
    "scope": "buttonField",
    "id": "UUID",
    "name": "btnSave",
    "myselfrows": 50,
    "label": "保存",
    "acttype": "34",
    "colorType": "default",
    "hiddenscript": "",
    "readonlyscript": ""
  }
}

viewdialogField — store=N

必填:moduledialogview。特有:captionmaximizationdivwidth/divheightselectonemutilselectallowviewdocmapping([])、eventmappingokscriptcallbackscriptisshowpic(no)、icontype/icon/imgpath/iconpathshowTitle(false)

禁止用 base_props / 勿抄 inputField 属性(不要写 discript/texttype/editmode/valuescript/fieldtype/opentype/validatelibs 等)。

属性 正确值 错误值(会导致前台不渲染按钮)
eventmapping ""字符串 [](空数组 → HTML 成 eventmapping="[]"o_view_dialog 要求 eventMapping == '' 才显示按钮)
maximization "default"字符串 false / true
showTitle false(设计器默认;独立按钮可 true 并设 caption
mapping {视图列id: 表单字段name} {formField, viewColumn} 或列显示名

推荐用 scripts/form_template_builder.pyviewdlg_f(...)。独立选择按钮可传 show_title=True 保留 caption

包含类 store=N(写入 fields

includeField

必填:module。特有:includetype(0)、viewidrelate(true)、fixation(false)、fixationheight(0)、includeelwidthincludepercentage(px)

{
  "includeField": {
    "scope": "includeField",
    "id": "UUID",
    "name": "detailView",
    "myselfrows": 100,
    "includetype": 0,
    "module": "MODULE_ID",
    "viewid": "VIEW_ID",
    "relate": true,
    "fixation": false,
    "hiddenscript": "",
    "readonlyscript": ""
  }
}

tabField

特有:relstr 数组、openAll(true)、showmode(0)、selectedscriptallowsamename(false)、tabselwidth/tabselheightname/tabName

relstr 项:

{
  "name": "页签1",
  "type": "form",
  "moduleId": "",
  "formId": "FORM_OR_FRAGMENT_ID",
  "recalculate": true,
  "relate": false,
  "hiddenScript": "",
  "readOnlyScript": "",
  "hiddenPrintScript": "",
  "selectRefreshScript": ""
}

写盘时去掉 formsOptions

{
  "tabField": {
    "scope": "tabField",
    "id": "UUID",
    "name": "tabs",
    "openAll": true,
    "showmode": 0,
    "relstr": [
      {"name":"基本信息","type":"form","moduleId":"","formId":"SUB_FORM_ID","recalculate":true,"relate":false,"hiddenScript":"","readOnlyScript":"","hiddenPrintScript":"","selectRefreshScript":""}
    ],
    "tabselwidth": "",
    "tabselheight": ""
  }
}

后端有、面板未挂(勿新建)

CustomField、ReminderField、ScancodeField、HandwritingField、NullField;废弃:AttachmentUploadToDataBaseField、FileManagerField。

完整最小业务模板示例

{
  "fields": [
    {
      "inputField": {
        "scope": "inputField",
        "id": "f1",
        "name": "title",
        "myselfrows": 100,
        "fieldtype": "VALUE_TYPE_VARCHAR",
        "texttype": "text",
        "width": 100,
        "widthunit": "%",
        "editmode": "01",
        "valuescript": "",
        "hiddenscript": "",
        "readonlyscript": "",
        "validatelibs": [],
        "refreshfields": [],
        "mobile": true
      }
    },
    {
      "dataField": {
        "scope": "dataField",
        "id": "f2",
        "name": "startDate",
        "myselfrows": 50,
        "fieldtype": "VALUE_TYPE_DATE",
        "datepattern": "YMD",
        "texttype": "text",
        "mobile": true
      }
    },
    {
      "selectField": {
        "scope": "selectField",
        "id": "f3",
        "name": "status",
        "myselfrows": 50,
        "fieldtype": "VALUE_TYPE_VARCHAR",
        "optionseditmode": "01",
        "optionsscript": "[{text:'草稿',value:'draft'},{text:'提交',value:'ok'}]",
        "multiselect": false,
        "mobile": true
      }
    }
  ],
  "layout": {
    "pc": {
      "formPanel": {
        "scope": "formPanel",
        "id": "fp-001",
        "name": "表单1",
        "myselfrows": 100,
        "containerwidth": 500,
        "children": [
          {
            "twoColumnContainer": {
              "scope": "twoColumnContainer",
              "id": "tc-001",
              "name": "两栏容器1",
              "myselfrows": 100,
              "style": {"display":"flex","flex-direction":"row","flex-wrap":"nowrap","width":"100%","min-height":"60px"},
              "children": [
                {
                  "container": {
                    "scope": "container",
                    "id": "col-1",
                    "name": "容器1",
                    "direction": "row",
                    "children": [
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "f1" } },
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "f3" } }
                    ]
                  }
                },
                {
                  "container": {
                    "scope": "container",
                    "id": "col-2",
                    "name": "容器2",
                    "direction": "row",
                    "children": [
                      { "fieldRef": { "scope": "fieldRef", "fieldId": "f2" } }
                    ]
                  }
                }
              ]
            }
          }
        ]
      }
    },
    "mobile": null
  }
}

(上例:title+status 左列,startDate 右列;奇数个字段时右列可少一项。)


4. Activity → .activity

与 Form 分开落盘。属性 camelCase。

通用属性

属性 说明
id / name id:__ + 短 UUID;name:**必填**显示名
label 名称标签脚本
multiLanguageLabel 多语言
type **必填**见下表(字符串或数字均可)
icontype img/font/""
icon / iconurl / fontUrl 图标
colorType 颜色
stateToShow 流程状态可见
beforeActionScript / actionScript / afterActionScript 前/中/后脚本
retractBeforeActionScript / retractAfterActionScript 回撤脚本
readonlyScript / hiddenScript true=只读/隐藏
orderno 排序

type(表单工具栏)

type 含义 附加属性
13 自定义 actionSelection(0脚本/1关联表单)、relatedFormIdactionScriptactionType(0无/1返回/2关闭/3跳转)、actionDispatcherUrlScript
34 保存
4 保存并启动流程
11 保存并返回
42 保存并新建 withOld
19 保存草稿不校验
21 保存并复制
5 流程处理 workFlowType(0预设/1自由)、processPreview(0/1)、预设时 onActionFlow 必填
33 流程启动 editMode(0用户/1脚本)、startFlowScript
10 返回
8 关闭窗口
14 网页打印
30 自定义打印 onActionPrint
25 PDF 导出
26 文件下载 fileNameScript
28 电子签章
37 邮件/短信分享 transpond
43 跳转 jumpMode(0表单/1URL)、dispatcherUrlmoduleSelect/formSelect 或 URL、jumpActOpenType(0当前/1弹层/2页签/3新窗)、dispatcherParams([{paramKey,paramValue}])
46 在线签章

Activity JSON / XML 示例

{
  "id": "ACT_UUID",
  "name": "保存",
  "type": "34",
  "icontype": "font",
  "fontUrl": "fa fa-save",
  "beforeActionScript": "",
  "afterActionScript": "",
  "readonlyScript": "",
  "hiddenScript": "",
  "orderno": 1
}
<?xml version="1.0" encoding="UTF-8"?>
<activity>
  <id>ACT_UUID</id>
  <name>保存</name>
  <type>34</type>
  <icontype>font</icontype>
  <fontUrl>fa fa-save</fontUrl>
  <orderno>1</orderno>
  <beforeActionScript><![CDATA[]]></beforeActionScript>
  <afterActionScript><![CDATA[]]></afterActionScript>
  <readonlyScript><![CDATA[]]></readonlyScript>
  <hiddenScript><![CDATA[]]></hiddenScript>
</activity>

流程处理:

{
  "id": "ACT_UUID",
  "name": "提交审批",
  "type": "5",
  "workFlowType": "0",
  "processPreview": "0",
  "onActionFlow": "FLOW_UUID",
  "orderno": 2
}

5. 脚本库 scripts/form_template_builder.py

Agent 批量或程序化**生成多个表单时,**优先 import 本库,勿手写简化版两栏布局或 XML。

路径(相对 workspace 根):

.claude/skills/generate-form-file/scripts/form_template_builder.py

用法

import sys
from pathlib import Path

WORKSPACE = Path("<workspace-root>")
sys.path.insert(0, str(WORKSPACE / ".claude" / "skills" / "generate-form-file" / "scripts"))
import form_template_builder as ftb

fields = [
    ftb.no_f("category_code", "分类编码", head="CAT"),
    ftb.input_f("category_name", "分类名称"),
    ftb.textarea_f("remark", "备注"),
]
ftb.write_form_bundle(
    Path("MyApp.application/module/mod.module/category_form.form"),
    form_id="__MyAppForm_category_form",
    form_name="category_form",
    parent_id="__ModuleId",
    application_id="__AppId",
    description="文件分类",
    form_type=1,
    fields=fields,
    activities=ftb.acts_plain(),
    activity_id_prefix="__MyAppAct_",
)

应用内薄脚本(如 {app}.application/_gen_forms.py)只保留:模块/应用 id、业务选项常量、各表单字段列表、main();通用工厂与落盘逻辑 import 本库。

API 摘要

类别 函数
选项脚本 opts(*labels) → optionsscript IIFE
字段工厂 input_f, select_f, radio_f, textarea_f, data_f, user_f, dept_f, attach_f, viewdlg_f, no_f, number_f
视图映射 map_cols(("formField","列名"), ...)
布局 build_layout(fields), build_template(fields), templatecontext_json(fields)
XML render_form_xml(...), render_activity_xml(...)
映射表单 build_mapping_str(form_name, table_name, column_mappings, pk_column="ID") → mappingStr JSON;write_form_bundle(..., form_type=65536, mapping_str=...)
落盘 write_form_bundle(form_dir, ...), write_activity_file(...)
Activity 预设 acts_plain(print_btn=False), acts_flow(flow_name, states, flow_id_prefix="__Flow_")

批量生成收尾

  1. 运行应用内 _gen_forms.py(或等价脚本)落盘全部 .form / .activity
  2. type∈{1,3,16} 向 workspace/.sync/ 投递 *.create_form_tabletype=65536 跳过
  3. rebuild-index + verify-workspace

参考实现:iso_doc.application/_gen_forms.py(iso_doc 业务字段 + import 本库)。

映射表单示例:

fields = [ftb.input_f("name", "名称"), ftb.textarea_f("remark", "备注")]
ftb.write_form_bundle(
    Path("App.application/module/mod.module/mapping_test.form"),
    form_id="__Form_mapping_test",
    form_name="mapping_test",
    parent_id="__ModuleId",
    application_id="__AppId",
    description="映射表示例",
    form_type=65536,
    fields=fields,
    mapping_str=ftb.build_mapping_str(
        "mapping_test",
        "mapping",
        [("name", "NAME"), ("remark", "REMARK")],
        pk_column="ID",
    ),
    activities=ftb.acts_plain(),
)

6. 硬规则清单

- [ ] Form.name 非空唯一;type 正确;JsonTemplate 时 showType=new
- [ ] templatecontext 为字符串;parse 后根键仅为 fields + layout(禁止旧根级 formPanel)
- [ ] fields 存字段完整定义;layout.pc.formPanel 存布局;layout.mobile=null
- [ ] 布局内字段用 fieldRef(fieldId 指向 fields 中 id);勿在 layout 内嵌完整 *Field
- [ ] 新建默认:普通字段经 twoColumnContainer→左右 container(栏1/栏2,style 含 width:50% + flex:1 1 50%)→fieldRef;style 键用 kebab-case;勿把普通 fieldRef 直挂 formPanel.children(全宽用 100% container)
- [ ] 两栏禁止:`style.flexDirection`(须 `flex-direction`)、栏容器缺 `width:50%`/`flex:1 1 50%`(会导致布局错乱)
- [ ] 每节点 scope=包裹键;children 单项为单键对象(仅布局节点有 children)
- [ ] **根元素 id**(表单根 `<Form id>` / `.activity` 根)= `__`+短 UUID;**其它 id**(字段 id / 布局节点 id)= 短 UUID(无 `__`);字段 name 英文;同表单 name/id 不重复
- [ ] store=Y 字段设 fieldtype;onlyCalculate=true 则不建列
- [ ] 日期=dataField;上传=attachmentField/imageuploadField/kmdataField
- [ ] 选项类填 OPTIONS;查询表单不用 selectaboutField
- [ ] includeField 填 module;viewdialogField 填 module+dialogview
- [ ] flowhistoryField 填 showmode;buttonField.acttype 非 0
- [ ] 工具栏用 .activity;勿把工具栏塞进 templatecontext(buttonField 仅画布按钮)
- [ ] type=5 且预设流程时 onActionFlow 必填
- [ ] 禁止输出 editProp/viewsoptions/formsOptions 等 UI 缓存
- [ ] type∈{1,3,16} 写完 .form 后向 workspace/.sync/ 投递 *.create_form_table(内容=.form **内层文件** URI `{名}.form/{名}.form`,不是 `.form` 目录)触发动态表创建/更新;确认归档 .done/(失败查 .failed/,常见为 URI 只写目录致「解析失败」);65536 与不建表类型不投递;另建议 rebuild 索引
- [ ] **映射表单 type=65536**:必填 mappingStr(formName=Form.name、tableName=已有表、columnMappings);必含 fieldName=MAPPINGID→主键列;业务列无 ITEM_ 前缀;MAPPINGID 不进 templatecontext.fields;不投递 create_form_table