表单定义编写指南¶
目标:Agent 直接生成/修改 workspace 表单定义。知识以本文为准。不写原理;不依赖外链。
上游流水线(有 SCHEMA/PLAN 时遵守;小改动可跳过):
事务表字段以已确认的 DATABASE_SCHEMA.md / PLAN.md 为准;本技能负责 .form 落盘并投递 .create_form_table。库表设计见 design-database-schema;应用蓝图见 plan-application。
iScript:写 valuescript / optionsscript / hiddenscript / readonlyscript / 校验 / 操作前后置等时,先读 iscript-usage(含 GraalVM 差异)→ form.md、activity.md;本文只定属性名与落盘。
产出物¶
| 部分 | 落盘 | 形态 |
|---|---|---|
| 表单元数据+模板 | {表单名}.form |
JAXB XML 根 Form;templatecontext 为 CDATA 字符串 |
| 工具栏操作 | {表单名}.form/{操作名}.activity |
XML 根 activity(或设计态 API 的 Activity JSON,字段同名) |
/{应用名}.application/module/{模块名}.module/{表单名}.form
/{应用名}.application/module/{模块名}.module/{表单名}.form/{操作名}.activity
新建最低配置:
.form:id、name、type=1、showType=new、非空templatecontexttemplatecontext:合法 JsonTemplate(根键fields+layout);普通字段默认用两栏容器布局(见「布局约定」)- 常用:至少一个保存操作
type=34的.activity - 落盘后收尾(见「动态表同步触发」):
type∈{1,3,16} 时向workspace/.sync/写入*.create_form_table(内容=.form内层文件 URI{名}.form/{名}.form)触发表结构创建/更新;确认进.done/;另建议 rebuild 索引供热加载可见
约定:
- Form/Activity 元数据:camelCase(API JSON)或同名 XML 子元素;脚本用 CDATA
- JsonTemplate 字段属性:设计器 小写键(
hiddenscript、texttype、datepattern、viewid、acttype) 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 缓存键:editProp、viewsoptions、optionstextoptions、processprevalue、formsOptions - 日期 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 收尾步骤(按序)¶
- 写完并保存
{表单名}.form(及所需.activity)。 - 向
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
- 等待监视器处理:读 URI → 读盘 JAXB 加载 Form →
FormTableProcessBean.createOrUpdateDynaTable→ 创建/更新动态表。 - 校验结果:成功则文件移入
.sync/.done/;失败移入.sync/.failed/。失败时先查数据源/软件激活/URI 是否正确,修正后再投递一份新文件(勿改.failed内旧文件指望重试)。 - (建议)再执行
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。
步骤¶
- 确认目标表已存在,且非
auth_/tlk_/parm_前缀表;记下表名与主键列名(常见ID)。 - 写
.form:type=65536、showType=new、非空templatecontext(字段/布局与普通表单相同)。 - 写
mappingStr(CDATA JSON,见下);缺此字段或主键映射不全则设计器校验失败。 - 常用:至少一个保存类
.activity(如type=34)。 - **禁止**投递
.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_ 前缀(如 NAME、REMARK、主键 ID) |
硬约束:
- **必须**含一条
{ "fieldName":"MAPPINGID", "columnName":"<主键列>" }(主键列按实际表,多为ID) - 每个存值业务字段都应有对应映射;
fieldName与fields中字段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.py:form_type=65536 + mapping_str=ftb.build_mapping_str(...)(或传入 JSON 字符串/对象)。
2. templatecontext → JsonTemplate¶
结构硬规则¶
根对象 有且仅有 fields 与 layout 两个键(**不再**使用旧根级 { 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 | 每个 twoColumnContainer 的 children 固定两个 container(名建议「栏1」「栏2」) |
| 3 | 普通字段的 fieldRef **成对**放入左栏/右栏(一对字段 = 一个两栏容器) |
| 4 | 字段较多时继续追加 twoColumnContainer,不要改成单列直挂 |
全宽例外(textarea / attachment / viewdialog 等):挂在 formPanel.children 下的 单独 container(style.width=100%),不要裸 fieldRef 直挂。
两栏布局硬规则(设计器对齐;错了会错乱)¶
运行/设计器按下列属性渲染;缺 width:50% / flex:1 1 50% 或 style 用 camelCase 会导致两栏错乱。
style键必须 kebab-case:flex-direction、padding-top……禁止flexDirection、paddingTop- 栏容器(左右
container):myselfrows=50;style含width:50%、min-width:50%、flex:1 1 50%、box-sizing:border-box、display:flex twoColumnContainer.style:display:flex、flex-direction:row、flex-wrap:nowrap、width:100%、min-height:60px- 字段:补
paddingtop/right/bottom/left(5/10/10/10)及对应style.padding-* formPanel:补direction、padding*、containerHeight、borderwidth、containerwidth
最小可用两栏骨架(与设计器导出一致):
{
"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:脚本选项 →optionsscriptoptionseditmode=00:视图选项 → 填module、dialogview、optionsvalue/optionstext
fieldtype 枚举:VALUE_TYPE_VARCHAR | VALUE_TYPE_NUMBER | VALUE_TYPE_DATE | VALUE_TYPE_TEXT | VALUE_TYPE_BLOB | VALUE_TYPE_INCLUDE
texttype:text | password | readonly | hidden | number(input 数字模式)
editmode:01=脚本;设计模式用 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:row,containerHeight,borderwidth,padding*,children |
| twoColumnContainer | 默认布局 | style.display=flex;children 两个 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¶
特有:fieldtype、width(100)、widthunit(%)、numberPattern(00.00)、defaultValueIsNull(false)、fieldkeyevent(Tabkey)、worklimitchecked(false)、textareamaxlimit、secretText(false)
数字:fieldtype=VALUE_TYPE_NUMBER,texttype=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¶
特有:rows、textareaheight、textareawidth、widthunit
radioField — VARCHAR + OPTIONS¶
特有:layout=horizontal + OPTIONS
checkboxField — TEXT + OPTIONS¶
多值 ; 分隔;+ OPTIONS
selectField — VARCHAR + OPTIONS¶
特有:multiselect(false)、selectleafnodesonly(false)、selectwidth、widthunit + OPTIONS
dataField — DATE(日期!)¶
特有:datepattern(默认设计器 YM;常用 YMD)、limit(false)、prev_name、width/widthunit
datepattern:Y|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¶
特有:relatedfield、selectleafnodesonly(false)、limitbyuser("false")、defaultoptiontype(16)、allowempty(false)、selectwidth
treedepartmentField — TEXT¶
特有:limit、selectleafnodesonly(false)、width/widthunit
userField — TEXT¶
特有:selectmode(默认 multiSelect)、width/widthunit
selectaboutField — VARCHAR + OPTIONS¶
查询表单禁止含此字段。
suggestField — VARCHAR + OPTIONS¶
特有:datamode=local|remote、width/widthunit
surveyField — TEXT¶
特有:questionscript(问卷脚本,必填业务内容)
attachmentField — TEXT¶
特有:limitsize、filetype(00全/01自定义)、customizetype、limitnumber(10)、filepattern(00/01)、filecatalog、previewedit(true)、openwatermark(false)、watermarksupportmode、watermarkscript、showtrackrevisions(true)、showusernameanddate(true)、downloadscript/deletescript/renamescript/previewscript
filepattern=01 时 filecatalog 必填;filetype=01 时 customizetype 必填。
kmdataField — TEXT¶
类似附件 + supportsorting;默认 watermarksupportmode=preview,print,download
imageuploadField — TEXT¶
特有:imgh/imgw(100)、limitsize、limitnumber(10)、filepattern/filecatalog、ishidetype/hidetype
onlinetakephotoField — TEXT¶
特有:imgh/imgw(100)、album(false)
weixingpsField — VARCHAR¶
最小:BASE 精简 + fieldtype
weixinrecordField — TEXT¶
mapField — TEXT¶
特有:maptype、opentype、valuetype(Point|LineString|Bounds|Polygon)、defaultcenteraddress、level(1–18)
genericwordField — TEXT¶
特有:opentype(1)、showtrackrevisions(true)
htmleditorField — TEXT¶
特有:areawidth、areaheight
展示/按钮(写入 fields)¶
noField — store=Y TEXT 流水号¶
特有:headText、digit(1)、isYear/isMonth/isDay(true)、width(200)、calculateonrefresh(true)
splitField — store=N¶
特有:height、titleScript、hiddenscript/hiddenvalue
labelField — store=N¶
特有:fontfamily、fontsize(14)、fontcolor(#3d464d)、fontbold(true)、fontitalic/fontunderline/fontstrikethrough(false);无标题栏脚本块
qrcodeField — store=N¶
特有:handletype(text)、size(200)、valuescript、callbackscript
calctextField — store=N¶
特有:valuescript、calculateonrefresh;(不落库)
flowhistoryField — store=N¶
特有:showmode(必填):text|diagram|textAndDiagram
flowreminderhistoryField — store=N¶
commentField — store=N¶
特有:title、width(200)、calculateonrefresh(true)
buttonField — store=N(模板内按钮,≠工具栏 Activity)¶
特有:label、colorType(default)、acttype(动作类型数字字符串,同 Activity type;勿为 0)、beforeactionscript/afteractionscript/actionscript、statetoshow、actionselection、relatedformid、actiontype(0无/1返回/2关闭/3跳转)、actiondispatcherurlscript、jumpmode、targetlist(formselect/moduleselect)、dispatcherurl、dispatcherparams、jumpactopentype、filenamescript、transpond、actionprint、withold、签章相关 signaturetype/signatureaction/signaturePosScript/datafield
{
"buttonField": {
"scope": "buttonField",
"id": "UUID",
"name": "btnSave",
"myselfrows": 50,
"label": "保存",
"acttype": "34",
"colorType": "default",
"hiddenscript": "",
"readonlyscript": ""
}
}
viewdialogField — store=N¶
必填:module、dialogview。特有:caption、maximization、divwidth/divheight、selectone、mutilselect、allowviewdoc、mapping([])、eventmapping、okscript、callbackscript、isshowpic(no)、icontype/icon/imgpath/iconpath、showTitle(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.py 的 viewdlg_f(...)。独立选择按钮可传 show_title=True 保留 caption。
包含类 store=N(写入 fields)¶
includeField¶
必填:module。特有:includetype(0)、viewid、relate(true)、fixation(false)、fixationheight(0)、includeelwidth、includepercentage(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)、selectedscript、allowsamename(false)、tabselwidth/tabselheight;name/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关联表单)、relatedFormId、actionScript、actionType(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)、dispatcherUrl、moduleSelect/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 根):
用法¶
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_") |
批量生成收尾¶
- 运行应用内
_gen_forms.py(或等价脚本)落盘全部.form/.activity type∈{1,3,16} 向workspace/.sync/投递*.create_form_table;type=65536跳过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