表单定义编写指南¶
目标: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;本文只定属性名与落盘。硬规则:凡含 return 的脚本必须 IIFE — (function () { ... return ...; })();禁止 return getWebUser().getId(); 等顶层裸 return。
枚举/选项存值(硬规则)¶
表单优先:数据库存储英文,在表单界面显示中文。
| 层 | 规则 |
|---|---|
| 库存 | selectField / radioField / checkboxField 等选项类字段的**存值**用英文码(如 DRAFT、INTERNAL、Y/N);禁止把中文文案当存值写入库 |
| 界面 | 选项**显示文本**用中文(如「草稿」「内部编制」「是」) |
| 平台 API | Options.add(option, value) → 第一参=显示文本(中文),第二参=存值(英文码) |
| 落盘脚本 | optionsscript 写成 opts.add("草稿","DRAFT");Python 用 opts_pairs(("DRAFT","草稿"), …)(pairs=(存值, 显示) → JS opts.add(显示, 存值)) |
| 禁止 | opts.add("DRAFT","草稿")(颠倒会导致界面显英文、库存中文);opts("是","否") 仅当显示与存值同为同一字符串时可用,**枚举/是否类禁止**用 opts(*labels) 冒充 |
| iScript | Activity / 值脚本比较、赋值一律用英文码;兼容历史脏数据时可短暂双判,新写入不得再写中文存值 |
| 编码选择 | 密级/分类等主数据:存 CODE(英文或业务编码),界面通过选择视图 / mapping 显 NAME(中文) |
SCHEMA 枚举列说明与表单选项必须对齐(同一英文码);列表/视图展示中文见 generate-view-file 状态类列(码→中文标签)。
产出物¶
| 部分 | 落盘 | 形态 |
|---|---|---|
| 表单元数据+模板 | {表单名}.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+pcLayoutMode=pc,可选mobileAutoLayout);所有表单普通字段默认三栏容器布局;业务分组用 分割线splitField(见「布局约定」)- 常用:至少一个保存操作
type=34的.activity - 落盘后收尾(见「动态表同步触发」):
type∈{1,3,16} 时向workspace/.sync/写入*.create_form_table(内容=.form内层文件 URI{名}.form/{名}.form)触发表结构创建/更新;确认进.done/;另建议 rebuild 索引供热加载可见
默认:拖拽表单(硬规则)¶
新建 / Agent 生成表单 一律默认拖拽表单。印刷(经典)表单只在用户明确要求时使用。
| 项 | 拖拽表单(默认) | 印刷(经典)表单 |
|---|---|---|
| 何时 | 用户未指定版式;或未要求印刷 / 纸张 / 页眉页脚排版 | **仅当**用户明确要求印刷、经典、纸张版式 |
showType |
new |
old |
pcLayoutMode |
pc |
classic |
| 布局权威 | layout.pc(三栏容器 + fieldRef) |
layout.classicPc(document 映射) |
| 另一侧 | 始终写出空壳 layout.classicPc |
仍写出 layout.pc(可空壳) |
禁止:因 Java Form.showType 字段未赋值时是 old,就把新建表单写成印刷表单。落盘与本技能的创建默认是 new + pc。
约定:
- 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/threeColumnContainer/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 | new | 新建默认拖拽。new=拖拽表单;old=印刷(经典)。仅用户明确要求印刷时用 old |
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":[]}},"classicPc":{"formPanel":{"scope":"formPanel","children":[]}},"mobile":null},"mobileAutoLayout":true,"pcLayoutMode":"pc"}]]></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)。业务键(编码等)需唯一时:在字段validaterule与保存/保存并返回beforeActionScript用queryBySQL查映射物理表 +MAPPINGID排除本行;保存前置用createAlert拦截。细则见 iscript-usage/form.md#字段唯一编码重复、activity.md#映射表单唯一-保存前置。 - **禁止**对映射表单使用
checkFieldUnique/validatelibs: checkFieldUnique_system(会误查TLK_*报错);**禁止**在表单/activity 脚本写$SQL(未定义)。 - **禁止**投递
.create_form_table;建议rebuild-index+clear_cache供热加载。 - 该映射表的 ListView / Select 列:禁止
COLUMN_TYPE_FIELD(列表会空白、表单却有值)。须COLUMN_TYPE_SCRIPT兼容字段名与物理列名;改已有列**保留列 id**。细则见 generate-view-file「硬规则:映射表列表列」。
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,可选 mobileAutoLayout、pcLayoutMode(不再**使用旧根级 { formPanel })。缺省/null/空串的 mobileAutoLayout 视为 true(mobile 沿用 PC 布局);仅显式 false 时才编译 layout.mobile。pcLayoutMode 缺省/pc = 拖拽(新建默认)**;仅 "classic" 编译印刷布局 layout.classicPc。旧键 layout.classic 仅兼容读入,写出用 classicPc。
字段的布局属性只写在 fieldRef 上,禁止写在 fields 的 *Field 上。 Java 编译会丢弃 fields 上的同名残留,不从字段定义兜底。PC / Mobile 可各写一份(例如 PC showTitle:true、Mobile showTitle:false)。
JsonTemplate 不写 style 对象(设计器导出省略;编译器忽略 JSON style,由 padding* / direction / myselfrows 重建 inline style)。布局容器(formPanel / container / threeColumnContainer)的 myselfrows / padding* / direction 仍写在布局节点上,不是 fieldRef。三栏栏容器仍须写出 kebab-case style(width:33.333%),见「三栏布局硬规则」。
fieldRef 布局属性(随 PC/Mobile 布局)¶
| 键 | 默认 | 说明 |
|---|---|---|
myselfrows |
100 | 占父容器宽度百分比(编译为 width/min-width) |
layout |
horizontal |
控件内部排列(如单选/复选方向) |
paddingtop / paddingright / paddingbottom / paddingleft |
5 / 10 / 10 / 10 | 内边距 px |
showTitle |
true |
是否显示标题栏(运行时读包装节点 HTML 属性) |
showfieldtitle |
— | 历史别名;新建只用 showTitle |
width / widthunit |
100 / % |
控件宽度 |
默认 fieldRef(新建字段一律带齐;fieldId 换成实际字段 id):
{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-001",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}
{
"fields": [
{
"inputField": {
"scope": "inputField",
"id": "fld-001",
"name": "title",
"fieldtype": "VALUE_TYPE_VARCHAR",
"texttype": "text"
}
},
{
"dataField": {
"scope": "dataField",
"id": "fld-002",
"name": "startDate",
"fieldtype": "VALUE_TYPE_DATE",
"datepattern": "YMD",
"texttype": "text"
}
},
{
"inputField": {
"scope": "inputField",
"id": "fld-003",
"name": "code",
"fieldtype": "VALUE_TYPE_VARCHAR",
"texttype": "text"
}
}
],
"layout": {
"pc": {
"formPanel": {
"scope": "formPanel",
"id": "fp-001",
"name": "表单1",
"myselfrows": 100,
"containerwidth": 500,
"direction": "row",
"paddingtop": 10,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"children": [
{
"threeColumnContainer": {
"scope": "threeColumnContainer",
"id": "tc-001",
"name": "三栏容器1",
"myselfrows": 100,
"direction": "row",
"children": [
{
"container": {
"scope": "container",
"id": "col-1",
"name": "栏1",
"direction": "row",
"myselfrows": 33,
"children": [
{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-001",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}
]
}
},
{
"container": {
"scope": "container",
"id": "col-2",
"name": "栏2",
"direction": "row",
"myselfrows": 33,
"children": [
{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-002",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}
]
}
},
{
"container": {
"scope": "container",
"id": "col-3",
"name": "栏3",
"direction": "row",
"myselfrows": 33,
"children": [
{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-003",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}
]
}
}
]
}
}
]
}
},
"classicPc": { "formPanel": { "scope": "formPanel", "children": [] } },
"mobile": null
},
"mobileAutoLayout": true,
"pcLayoutMode": "pc"
}
| 项 | 约定 |
|---|---|
| 根键 | fields + layout;可选 mobileAutoLayout(缺省=true)、pcLayoutMode(新建默认 pc=拖拽);禁止旧根级 formPanel |
fields |
数组;元素为 { [scope]: { scope, id, 业务 props... } };**禁止**布局键与 style |
layout.pc |
{ formPanel: formPanelNode };拖拽默认权威;容器与布局关系在此树 |
layout.classicPc |
印刷布局;新建拖拽时写空 formPanel;旧键 layout.classic 只读兼容 |
layout.mobile |
null 或空 formPanel 表示未单独设计;自定义 mobile 时为 { formPanel: ... } 且 mobileAutoLayout: false |
pcLayoutMode |
新建 pc;印刷才 classic |
| 布局内字段 | { fieldRef: { scope, fieldId, 布局键... } };fieldId === fields 中字段 id |
| 节点属性 | 与 propValues 平铺;不写 style |
children |
仅布局节点(formPanel / container / 多栏等);每项 单键对象,键=scope |
tabField |
完整定义在 fields;页签用 relstr,不用 children 装页签体;布局中为 fieldRef |
| 写入 Form | JSON.stringify(对象) 放进 templatecontext |
布局约定(所有表单默认)¶
所有表单默认拖拽三栏布局:权威在 layout.pc,pcLayoutMode=pc,showType=new(普通表单、映射表单、查询表单等均同;仅用户明确要求印刷 / 经典 / 其它栏数时偏离)。下面三栏配方用于拖拽 PC 布局。
默认配方:普通字段一律进三栏容器,禁止把普通 fieldRef 直接挂在 formPanel.children。
| 步骤 | 做法 |
|---|---|
| 1 | formPanel.children 放一个或多个纵向堆叠的 threeColumnContainer |
| 2 | 每个 threeColumnContainer 的 children 固定三个 container(名建议「栏1」「栏2」「栏3」) |
| 3 | 普通字段的 fieldRef **每三个一组**放入三栏(一行三字段 = 一个三栏容器;不足三格的行仍写满三个 container,空栏 children=[]) |
| 4 | 字段较多时继续追加 threeColumnContainer,不要改成单列直挂 |
| 5 | 业务分组:在分组之间插入 分割线组件 splitField(split_f),全宽行;titleScript 写分组标题(如「基本信息」「审批信息」);height 默认 "1"(1px) |
分组约定(硬性)
- 字段超过约一组业务含义,或表单有明确区块(基本信息 / 明细 / 附件 / 审批等)时,**必须**用
splitField分割线分组,勿用labelField、空 container 或注释冒充分组。 fields数组中:split_f(...)插在该组字段**之前**;build_layout会自动把分割线渲成全宽行,其后普通字段继续按三栏排。- 极简单表(≤3 个同质字段、无区块)可不插分割线。
全宽例外(textarea / htmleditor / attachment / splitField 分割线 / includeField / flowhistoryField;以及 独立 viewdialog eventmapping=""):挂在 formPanel.children 下的 单独 container(style.width=100%),不要裸 fieldRef 直挂。已设「跟随控件显示」的 viewdialog 与宿主字段 同栏(见 viewdialogField)。
三栏布局硬规则(设计器对齐;错了会错乱)¶
运行/设计器按下列属性渲染;缺 width:33.333% / flex:1 1 33.333% 或 style 用 camelCase 会导致三栏错乱。
style键必须 kebab-case:flex-direction、padding-top……禁止flexDirection、paddingTop- 栏容器(左中右
container):myselfrows=33;style含width:33.333%、min-width:33.333%、flex:1 1 33.333%、box-sizing:border-box、display:flex threeColumnContainer.style:display:flex、flex-direction:row、flex-wrap:nowrap、width:100%、min-height:60px- 字段
fieldRef:补myselfrows/layout/showTitle/padding*/width/widthunit(见上表默认值);**不要**写在fields或字段style上 formPanel:补direction、padding*、containerHeight、borderwidth、containerwidth
最小可用三栏骨架(与设计器导出一致):
{
"fields": [
{ "inputField": { "scope": "inputField", "id": "fld-a", "name": "a", "fieldtype": "VALUE_TYPE_VARCHAR" } },
{ "inputField": { "scope": "inputField", "id": "fld-b", "name": "b", "fieldtype": "VALUE_TYPE_VARCHAR" } },
{ "inputField": { "scope": "inputField", "id": "fld-c", "name": "c", "fieldtype": "VALUE_TYPE_VARCHAR" } }
],
"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": [
{
"threeColumnContainer": {
"scope": "threeColumnContainer",
"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": 33,
"direction": "row",
"style": {
"display": "flex",
"flex-direction": "row",
"flex-wrap": "wrap",
"width": "33.333%",
"min-width": "33.333%",
"flex": "1 1 33.333%",
"box-sizing": "border-box"
},
"children": [{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-a",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}]
}
},
{
"container": {
"scope": "container",
"id": "col-2",
"name": "栏2",
"myselfrows": 33,
"direction": "row",
"style": { "display": "flex", "width": "33.333%", "min-width": "33.333%", "flex": "1 1 33.333%", "box-sizing": "border-box" },
"children": [{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-b",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}]
}
},
{
"container": {
"scope": "container",
"id": "col-3",
"name": "栏3",
"myselfrows": 33,
"direction": "row",
"style": { "display": "flex", "width": "33.333%", "min-width": "33.333%", "flex": "1 1 33.333%", "box-sizing": "border-box" },
"children": [{
"fieldRef": {
"scope": "fieldRef",
"fieldId": "fld-c",
"myselfrows": 100,
"layout": "horizontal",
"showTitle": true,
"paddingtop": 5,
"paddingright": 10,
"paddingbottom": 10,
"paddingleft": 10,
"width": 100,
"widthunit": "%"
}
}]
}
}
]
}
}
]
}
},
"mobile": null
},
"mobileAutoLayout": true
}
两/四栏容器仅在用户明确要求时使用;未指定时**一律**用三栏。分组用分割线,不用改栏数来「分区」。
公共属性块(存值字段合并此块)¶
记为 BASE(所有字段几乎都有;非存值可省略 fieldtype/校验/值脚本)。**不要**把 myselfrows / showTitle / layout / padding* / width / widthunit / style 写进本块——它们属于 fieldRef。
{
"id": "UUID",
"name": "fieldName",
"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
}
必填控制(硬规则)¶
无条件必填(标题旁红色 *、空保存/提交拦截)把**系统默认校验库**写入字段 validatelibs:
checkEmpty_system 为平台内置,无需 .valid。其它校验按需自定义:落盘见 generate-valid-file,在字段上勾选后把该库根 id 追加进同一 validatelibs 数组。挂接细则见 iscript-usage/form.md#校验库validatelibs。
| 做 | 不做 |
|---|---|
| 存值字段无条件必填时写入上列系统默认库 | **禁止**用 worklimitchecked=true 冒充必填(不渲染星号、不是空值校验) |
可与自定义 .valid、其它系统库(如 checkFieldUnique_system)同数组 |
**禁止**用 validaterule 手写「不能为空」代替系统默认库 |
可复用/条件业务校验:先 .valid 再勾选 |
不要把可复用规则只写在单个字段 validaterule |
Python 工厂:base_props(..., required=True) 或 input_f(..., required=True) 等(写入系统默认空值库)。
OPTIONS(radio/checkbox/select/selectabout/suggest):
{
"optionseditmode": "01",
"optionsscript": "(function(){var opts=$TOOLS.createOptions();opts.add(\"草稿\",\"DRAFT\");opts.add(\"已生效\",\"EFFECTIVE\");return opts;})()",
"module": "",
"dialogview": "",
"optionsvalue": "",
"optionstext": ""
}
optionseditmode=01:脚本选项 →optionsscriptoptionseditmode=00:视图选项 → 填module(模块 id)、dialogview(选择视图 id,不是视图 name)、optionsvalue/optionstext- 存值英文 / 显示中文(硬规则):见上文「枚举/选项存值」;
opts.add("中文显示","EN_CODE");禁止颠倒;是否类用opts.add("是","Y");opts.add("否","N")
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 / layout.classicPc / layout.mobile 树)¶
| scope | 要点 | 节点示例 |
|---|---|---|
| formPanel | **拖拽默认**在 layout.pc 下唯一根 |
见上;children 必有(可 []) |
| fieldRef | 字段占位 + 布局键 | 见「fieldRef 布局属性」;fieldId 指向 fields 中字段 id。定义在 layout,不在 fields |
| container | 列/通用 | direction:row,containerHeight,borderwidth,padding*,children |
| twoColumnContainer | 两栏 | 仅用户明确要求时用;children 两个 container |
| threeColumnContainer | 默认布局 | style.display=flex;children 三个 container;普通字段放列内 |
| fourColumnContainer | 四栏 | 仅用户明确要求时用;同三栏,列数 4 |
三栏 + 字段占位(新建默认形态;每个 fieldRef 须带齐布局键,见「fieldRef 布局属性」,下例省略):
{
"fields": [],
"layout": {
"pc": {
"formPanel": {
"scope": "formPanel",
"id": "fp-001",
"name": "表单1",
"myselfrows": 100,
"children": [
{
"threeColumnContainer": {
"scope": "threeColumnContainer",
"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",
"myselfrows": 33,
"children": [
{ "fieldRef": { "scope": "fieldRef", "fieldId": "字段id1" } }
]
}
},
{
"container": {
"scope": "container",
"id": "UUID",
"name": "栏2",
"direction": "row",
"myselfrows": 33,
"children": [
{ "fieldRef": { "scope": "fieldRef", "fieldId": "字段id2" } }
]
}
},
{
"container": {
"scope": "container",
"id": "UUID",
"name": "栏3",
"direction": "row",
"myselfrows": 33,
"children": [
{ "fieldRef": { "scope": "fieldRef", "fieldId": "字段id3" } }
]
}
}
]
}
}
]
}
},
"mobile": null
}
}
输入类 store=Y(写入 fields)¶
inputField — 单行文本 — 默认 VARCHAR¶
特有:fieldtype、numberPattern(00.00)、defaultValueIsNull(false)、fieldkeyevent(Tabkey)、worklimitchecked(false,不是**必填)、textareamaxlimit、secretText(false)。控件宽 width/widthunit 写在 **fieldRef,不进 fields。
数字:fieldtype=VALUE_TYPE_NUMBER,texttype=number
{
"inputField": {
"scope": "inputField",
"id": "UUID",
"name": "title",
"fieldtype": "VALUE_TYPE_VARCHAR",
"texttype": "text",
"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(控件宽仍写在 fieldRef 的 width/widthunit)
选型: 纯文本多行(备注、说明、无格式正文)。不是 HTML 富文本编辑器,也**不是**公式定义字段。
formulaField — TEXT(公式定义)¶
平台 FormulaField:运行时弹出公式编辑器,将 QLExpress 表达式原文存入本字段。字段**只存不执行**;消费方用 iScript executeQLExpress(expr[, contextOrDoc]) 求值。
特有:与 textarea 类似的 textareaheight / textareawidth;模板标签 <o-formula>。全宽(FULLWIDTH_SCOPES 已含 formulaField)。
必须使用: 需求为「公式定义 / 公式编辑器 / 计算公式字段」→ formula_f。**禁止**用 textareaField 冒充(运行时无可视化编辑器)。
变量名 = 当前表单其它存值字段 name。跨表求值时,消费单入参字段名须与公式变量名对齐,或显式传 JS 对象上下文。
radioField — VARCHAR + OPTIONS¶
特有:选项排列 layout=horizontal|vertical 写在 fieldRef(不进 fields)+ OPTIONS。存值英文码、显示中文(见「枚举/选项存值」)。
checkboxField — TEXT + OPTIONS¶
多值 ; 分隔;+ OPTIONS。存值英文码、显示中文。
selectField — VARCHAR + OPTIONS¶
特有:multiselect(false)、selectleafnodesonly(false)、selectwidth;控件宽写在 fieldRef 的 width/widthunit + OPTIONS。存值英文码、显示中文;例:opts_pairs(("DRAFT","草稿"), ("EFFECTIVE","已生效"))。
dataField — DATE(日期!)¶
特有:datepattern(默认设计器 YM;常用 YMD)、limit(false)、prev_name。控件宽写在 fieldRef。
datepattern:Y|YM|YMD|YMD_HM|YMD_HMS|HMS
{
"dataField": {
"scope": "dataField",
"id": "UUID",
"name": "startDate",
"fieldtype": "VALUE_TYPE_DATE",
"datepattern": "YMD",
"limit": false,
"prev_name": "",
"texttype": "text",
"editmode": "01",
"valuescript": "",
"hiddenscript": "",
"readonlyscript": "",
"mobile": true
}
}
deptField — VARCHAR(遗留;新建勿用)¶
旧版部门控件。特有:relatedfield、selectleafnodesonly(false)、limitbyuser("false")、defaultoptiontype(16)、allowempty(false)、selectwidth。
新建/规划默认禁止:部门/单位/组织一律用下方 treedepartmentField。仅维护已有 deptField 表单时按原控件改,勿顺手改成其它错误类型。
treedepartmentField — TEXT(部门/单位/组织选树)¶
平台 TreeDepartmentField(树形部门选择框):从组织树选部门/单位/组织节点,落库为**部门 id**(TEXT)。
特有:limit(false)、selectleafnodesonly(false;仅叶子时改 true)、width/widthunit(常用 200/px)
必须使用(与 plan-application「部门/单位/组织 → TreeDepartmentField」一致):
用 treedepartmentField |
不用 treedepartmentField |
|---|---|
| 所属部门、申请部门、责任部门、分发部门、编制部门、单位、组织等**选组织节点** | 选人 → userField |
| 需求写「部门 / 单位 / 组织」且界面要点组织树 | 纯文本部门名展示且明确不选树 → 只读 inputField(例外) |
默认当前用户部门(+ valuescript) |
业务自建「组织主数据表」非平台部门树 → 按业务表 + viewdialog/select,备注写明 |
硬规则:
- **禁止**用
inputField手填部门 id/名称冒充选部门 - **禁止**用
userField选部门;**禁止**新建默认使用deptField - 工厂:
ftb.dept_f(name, label, selectleafnodesonly=False)(内部 scope=treedepartmentField) - 落盘后须
.create_form_table(与其它 store=Y 相同)
{
"treedepartmentField": {
"scope": "treedepartmentField",
"id": "短UUID",
"name": "deptId",
"discript": "所属部门",
"fieldtype": "VALUE_TYPE_TEXT",
"limit": false,
"selectleafnodesonly": false,
"mobile": true
}
}
userField — TEXT(人员/用户/员工选人)¶
平台 UserField(用户选择框):从组织用户中选人,落库为**用户 id**(TEXT;多选时平台约定分隔)。
特有:selectmode(single 单选 / multiSelect 多选;业务未说明多人时默认 single)。控件宽写在 fieldRef(常用 200/px)。
必须使用(与 plan-application「人员/用户/员工 → UserField」一致):
用 userField |
不用 userField |
|---|---|
| 申请人、审批人、借阅人、编制人、签收人、执行人、监销人、责任人、经办人等**选人** | 部门/单位/组织 → treedepartmentField |
| 需求写「人员 / 用户 / 员工」且界面要点选组织用户 | 纯文本姓名展示且明确不选人 → 只读 inputField(例外) |
默认当前用户(+ valuescript,须 IIFE) |
角色/岗位选型(非「人」) |
硬规则:
- **禁止**用
inputField手填用户 id/姓名冒充选人 - **禁止**用
userField选部门 - 默认当前用户:
valuescript写(function () { return getWebUser().getId(); })(),**禁止**裸return getWebUser().getId(); - 工厂:
ftb.user_f(name, label, valuescript="", selectmode="single") - 落盘后须
.create_form_table(与其它 store=Y 相同)
{
"userField": {
"scope": "userField",
"id": "短UUID",
"name": "borrowerId",
"discript": "借阅人",
"fieldtype": "VALUE_TYPE_TEXT",
"selectmode": "single",
"mobile": true,
"valuescript": ""
}
}
selectaboutField — VARCHAR + OPTIONS¶
查询表单禁止含此字段。
suggestField — VARCHAR + OPTIONS¶
特有:datamode=local|remote;控件宽写在 fieldRef 的 width/widthunit
surveyField — TEXT¶
特有:questionscript(问卷脚本,必填业务内容)
attachmentField — TEXT(附件上传)¶
平台 AttachmentField(附件上传):界面上传文件,落库为**附件元数据 JSON**(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 必填。
必须使用(与 plan-application「附件 → AttachmentField」一致):
用 attachmentField |
不用 attachmentField |
|---|---|
| 附件、证明材料、发行版文件、草稿附件、外发副本、模板文件、销毁证明等**通用上传** | 仅图片上传 → imageuploadField;拍照 → onlinetakephotoField |
| 需求写「附件 / 上传文件」且界面要点上传 | 知识库专用且需求明确 KM → kmdataField(例外) |
纯备注文本 / 手填路径展示 → textareaField / inputField(例外) |
硬规则:
- **禁止**用
textareaField/inputField存附件 JSON 或文件路径冒充附件控件 - **禁止**用
htmleditorField承载附件 - 工厂:
ftb.attach_f(name, label, limitnumber=10, filetype="00") - 落盘后须
.create_form_table(与其它 store=Y 相同);SCHEMA 列类型为 TEXT
{
"attachmentField": {
"scope": "attachmentField",
"id": "短UUID",
"name": "proofFile",
"discript": "证明材料",
"fieldtype": "VALUE_TYPE_TEXT",
"limitsize": "",
"filetype": "00",
"customizetype": "",
"limitnumber": 10,
"filepattern": "00",
"filecatalog": "",
"previewedit": true,
"openwatermark": false,
"mobile": true
}
}
kmdataField — TEXT¶
类似附件 + supportsorting;默认 watermarksupportmode=preview,print,download。新建业务表单**默认勿用**(通用附件用 attachmentField),仅需求明确知识库/KM 时选用。
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(常用 "100%" / "300")
硬规则(防混用):
| 需求 | 正确 scope | 工厂 | 禁止 |
|---|---|---|---|
| HTML / 富文本编辑器 | htmleditorField |
htmleditor_f |
**禁止**用 textareaField / textarea_f 冒充 |
| 多行纯文本 | textareaField |
textarea_f |
不要写成 htmleditorField |
需求名含「HTML编辑器」「富文本」「htmleditor」→ 必须 htmleditorField;「多行文本框」→ textareaField。二者列类型都是 VALUE_TYPE_TEXT,但运行时控件不同,不可互换。
全宽布局:与 textarea 一样挂 formPanel 下单独 container(FULLWIDTH_SCOPES 已含 htmleditorField)。
{
"htmleditorField": {
"scope": "htmleditorField",
"id": "短UUID",
"name": "多行文本",
"fieldtype": "VALUE_TYPE_TEXT",
"areawidth": "100%",
"areaheight": "300",
"discript": "HTML内容",
"editmode": "01",
"valuescript": "",
"hiddenscript": "",
"readonlyscript": "",
"mobile": true
}
}
展示/按钮(写入 fields)¶
noField — store=Y TEXT 流水号(编号/单号按需)¶
平台 NoField:新建文档时按「前缀 + 可选年月日 + 序号位数」自动出号并落库。
特有:headText(前缀)、digit(序号位数,常用 4)、isYear/isMonth/isDay(是否拼入日期段;默认均可 true)、width(200)、widthunit(px)、calculateonrefresh(true)
按需选用(与 plan-application「编号/单号 → NoField」一致):
用 noField |
不用 noField(改用其它控件) |
|---|---|
| 需求:单据号/单号/流水号由**系统自动生成** | 用户手输、Excel/接口传入 |
| 无独立「编号规则」主数据/规则引擎,仅前缀+流水 | 按业务规则表/脚本拼号 → inputField + valuescript |
典型:orderNo、borrowNo、distNo、outNo、申请单号 |
档案编码、物料编码、从他表挑选的已有编号 |
硬规则:
- **禁止**凡见
*No/*Code就套noField;以需求是否「自动出号」为准 - **禁止**用只读空
inputField假装会出号 - 工厂:
ftb.no_f(name, label, head="NO", digit=4, is_year=True, is_month=True, is_day=True) - 落盘后须
.create_form_table(与其它 store=Y 字段相同)
{
"noField": {
"scope": "noField",
"id": "短UUID",
"name": "borrowNo",
"discript": "借阅单号",
"fieldtype": "VALUE_TYPE_TEXT",
"headText": "BOR",
"digit": 4,
"isYear": true,
"isMonth": true,
"isDay": true,
"calculateonrefresh": true,
"mobile": true
}
}
splitField(分割线)— store=N¶
用途:表单业务分组的默认手段(见「布局约定」分组约定)。界面显示为全宽分割线,可带分组标题。
特有:height(默认 "1",即 1px)、titleScript(分组标题,如「基本信息」)、hiddenscript/hiddenvalue。布局走**全宽行**(FULLWIDTH_SCOPES)。程序化用 split_f(name, title="", height="1")——title 非空时写入 titleScript。
{
"splitField": {
"scope": "splitField",
"id": "UUID",
"name": "split1",
"height": "1",
"titleScript": "(function(){return \"基本信息\";})()",
"hiddenscript": "",
"hiddenvalue": "",
"mobile": true
}
}
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",
"label": "保存",
"acttype": "34",
"colorType": "default",
"hiddenscript": "",
"readonlyscript": ""
}
}
viewdialogField — store=N(仅快速选择填充,不存值)¶
必填:module(模块 id)、dialogview(选择视图 id)。特有:caption、maximization、divwidth/divheight、selectone、mutilselect、allowviewdoc、mapping([])、eventmapping(跟随控件显示)、okscript、callbackscript、isshowpic(no)、icontype/icon/imgpath/iconpath。showTitle 写在 fieldRef:跟随模式 false;独立按钮可 true 并设 caption。
存值关系(与规划一致): viewdialogField 不建动态列、不持久化。业务值必须落在其它 store=Y 字段上;本控件只负责打开视图并把选中行列值经 mapping 写入那些字段。规划/落盘时须同时有目标 inputField/selectField/…,**禁止**仅用一个 viewdialog 冒充业务存值字段(见 plan-application「视图选择框」)。
业务引用键(硬规则,与 plan-application / design-database-schema 一致):
在业务实现过程中,避免使用 XXID,而应用由业务含义的 XX编码。ID 通常为无意义的非重复序列号。
- mapping **优先**回写
*Code/ 车牌 / 文号等业务键(及名称快照) - **禁止**默认把隐藏「ID」列(
COLUMN_TYPE_SCRIPT+doc.getId()/MAPPINGID)作为唯一业务外键回写到*Id - 扣减库存、冲突校验、关联查询等 iScript:按 CODE/业务键(必要时再 NAME)查
MD_*,勿只信文档/表主键 id
eventmapping(跟随控件显示)硬规则 — 有视图选择框时优先设置:
| 场景 | eventmapping 取值 |
说明 |
|---|---|---|
| 默认(优先) | 宿主存值字段 name(通常取 mapping 第一个目标,如 categoryCode / suppliesCode) |
选择按钮挂在该控件旁;宿主须为 inputField / textareaField / selectField / dataField / suggestField / calctextField |
| 独立大按钮 | ""(空**字符串**) |
仅当确需单独按钮时;配合 fieldRef showTitle=true + caption;布局走全宽行 |
- 禁止
eventmapping: [](空数组 → HTMLeventmapping="[]",控件异常) - 有明确存值宿主时,**不要**默认留空独立按钮;
viewdlg_f(...)在未传参时会自动取 mapping 首目标 - 布局:
build_layout会把已跟随的 viewdialog 与宿主 同栏;独立按钮仍全宽
dialogview 硬规则(落盘前必查):
| 正确 | 错误(运行时打不开选择框) |
|---|---|
视图根元素 id(如 __isoVCategorySelectxx,与 url.index / .view 根属性 id 一致) |
视图 name / 文件名(如 CategorySelect) |
运行时 ViewDialogField / 前台弹窗均按 id 调 doView(applicationId, dialogView);写成 name → 视图找不到 → 选择框空白/无效。设计器「模块+视图」下拉写入的也是 viewId。
落盘示例(跟随 categoryCode;mapping 键必须是视图列 id,值是本表单字段 name):
{
"viewdialogField": {
"scope": "viewdialogField",
"id": "短UUID",
"name": "pickCategory",
"caption": "",
"module": "__7N8ceyhQJFmdbZPEBh0",
"dialogview": "__isoVCategorySelectxx",
"eventmapping": "categoryCode",
"maximization": "default",
"selectone": true,
"mutilselect": false,
"allowviewdoc": false,
"mapping": [{ "__70786e098b084b28acc1": "categoryCode" }],
"isshowpic": "no"
}
}
程序化生成:viewdlg_f(name, label, module_id, view_id, mapping, ...)——第 4 参必须是 视图 id;mapping 用 map_cols(("本表字段name", "视图列id"), ...)(列 id 从该视图目录下 *.column 根属性读取,可用 load_column_field_map(view_dir))。禁止**把列 fieldName(如 code)或列显示名当作 mapping 键。默认自动设置 eventmapping;独立按钮传 eventmapping="" 且 show_title=True(写入 **fieldRef.showTitle,不进 fields)。
禁止用 base_props / 勿抄 inputField 属性(不要写 discript/texttype/editmode/valuescript/fieldtype/opentype/validatelibs 等)。
| 属性 | 正确值 | 错误值(会导致前台不渲染按钮 / 弹窗失败 / 选中不回写) |
|---|---|---|
dialogview |
视图 id(__+短 UUID) |
视图 name(如 CategorySelect) |
module |
视图所属模块 id | 模块 name |
eventmapping |
宿主字段 name(优先);或独立按钮时 ""(字符串) |
[];宿主 name 不存在;指向另一 viewdialog |
maximization |
"default"(字符串) |
false / true |
showTitle(写在 fieldRef) |
跟随模式 false;独立按钮可 true 并设 caption |
跟随模式仍 true 造成多余标题栏(非致命);写在 fields 上会被 Java 丢弃 |
mapping 项 |
{视图列 id: 本表**存值**字段 name},如 {"__Lc3GlJjkeR1t2gdPc45":"categoryCode"};map_cols(("存值字段","列id"));目标字段须已存在且为 store=Y |
{"code":"categoryCode"}(键写成列 fieldName);目标指向 viewdialog 自身或其它 store=N;键序反了 |
运行时前台用选中行的 列 id 与 mapping[i].id 比对后回写 mapping[i].value(表单字段 name);键不是列 id → 选中无回写。eventmapping 非空时,运行时把 viewdialog 的 name 反向挂到宿主字段的 eventMapping,由 o_input 等渲染旁侧选择按钮。
推荐用 scripts/form_template_builder.py 的 viewdlg_f(...)。
包含类 store=N(写入 fields)¶
includeField¶
必填:module。特有:includetype(0)、viewid、relate(true)、fixation(false)、fixationheight(0)、includeelwidth、includepercentage(px)
{
"includeField": {
"scope": "includeField",
"id": "UUID",
"name": "detailView",
"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",
"fieldtype": "VALUE_TYPE_VARCHAR",
"texttype": "text",
"editmode": "01",
"valuescript": "",
"hiddenscript": "",
"readonlyscript": "",
"validatelibs": [],
"refreshfields": [],
"mobile": true
}
},
{
"dataField": {
"scope": "dataField",
"id": "f2",
"name": "startDate",
"fieldtype": "VALUE_TYPE_DATE",
"datepattern": "YMD",
"texttype": "text",
"mobile": true
}
},
{
"selectField": {
"scope": "selectField",
"id": "f3",
"name": "status",
"fieldtype": "VALUE_TYPE_VARCHAR",
"optionseditmode": "01",
"optionsscript": "(function(){var opts=$TOOLS.createOptions();opts.add(\"草稿\",\"DRAFT\");opts.add(\"已提交\",\"SUBMITTED\");return opts;})()",
"multiselect": false,
"mobile": true
}
}
],
"layout": {
"pc": {
"formPanel": {
"scope": "formPanel",
"id": "fp-001",
"name": "表单1",
"myselfrows": 100,
"containerwidth": 500,
"children": [
{
"threeColumnContainer": {
"scope": "threeColumnContainer",
"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",
"myselfrows": 33,
"children": [
{ "fieldRef": { "scope": "fieldRef", "fieldId": "f1", "myselfrows": 100, "layout": "horizontal", "showTitle": true, "paddingtop": 5, "paddingright": 10, "paddingbottom": 10, "paddingleft": 10, "width": 100, "widthunit": "%" } }
]
}
},
{
"container": {
"scope": "container",
"id": "col-2",
"name": "栏2",
"direction": "row",
"myselfrows": 33,
"children": [
{ "fieldRef": { "scope": "fieldRef", "fieldId": "f2", "myselfrows": 100, "layout": "horizontal", "showTitle": true, "paddingtop": 5, "paddingright": 10, "paddingbottom": 10, "paddingleft": 10, "width": 100, "widthunit": "%" } }
]
}
},
{
"container": {
"scope": "container",
"id": "col-3",
"name": "栏3",
"direction": "row",
"myselfrows": 33,
"children": [
{ "fieldRef": { "scope": "fieldRef", "fieldId": "f3", "myselfrows": 100, "layout": "horizontal", "showTitle": true, "paddingtop": 5, "paddingright": 10, "paddingbottom": 10, "paddingleft": 10, "width": 100, "widthunit": "%" } }
]
}
}
]
}
}
]
}
},
"mobile": null
}
}
(上例:title / startDate / status 各占一栏;不足三格的行仍写满三个 container,空栏 children=[]。)
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 |
排序 |
parentForm |
**必填**所属表单 id(与 parentId 同值) |
editMode |
编辑模式,默认 0 |
contextMenu |
是否出现在右键/「更多」菜单,工具栏按钮默认 true |
showInToolbar |
是否显示在表单工具栏,默认 true(缺省会导致 type=13 等按钮不显示) |
硬性规则(对齐 demo-basic.application):write_activity_file / render_activity_xml 生成的表单 Activity **必须**包含 parentForm、editMode、showInToolbar、contextMenu;仅当业务明确要求放入「更多」菜单时才设 showInToolbar=false 且 contextMenu=true。
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": "",
"editMode": "0",
"parentForm": "FORM_UUID",
"contextMenu": "true",
"showInToolbar": "true",
"orderno": 1
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<activity id="ACT_UUID">
<name>保存</name>
<parentId>FORM_UUID</parentId>
<applicationid>APP_UUID</applicationid>
<type>34</type>
<icontype>font</icontype>
<fontUrl>fa fa-save</fontUrl>
<colorType>default</colorType>
<orderno>1</orderno>
<beforeActionScript><![CDATA[]]></beforeActionScript>
<afterActionScript><![CDATA[]]></afterActionScript>
<readonlyScript><![CDATA[]]></readonlyScript>
<hiddenScript><![CDATA[]]></hiddenScript>
<editMode>0</editMode>
<parentForm>FORM_UUID</parentForm>
<contextMenu>true</contextMenu>
<showInToolbar>true</showInToolbar>
</activity>
自定义按钮(type=13)示例:
<activity id="ACT_UUID">
<name>复核通过</name>
<parentId>FORM_UUID</parentId>
<applicationid>APP_UUID</applicationid>
<type>13</type>
<icontype>font</icontype>
<fontUrl>fa fa-check</fontUrl>
<colorType>default</colorType>
<orderno>3</orderno>
<beforeActionScript><![CDATA[]]></beforeActionScript>
<afterActionScript><![CDATA[]]></afterActionScript>
<readonlyScript><![CDATA[]]></readonlyScript>
<hiddenScript><![CDATA[return status != 'PENDING_REVIEW']]></hiddenScript>
<editMode>0</editMode>
<parentForm>FORM_UUID</parentForm>
<actionSelection>0</actionSelection>
<actionScript><![CDATA[// iScript]]></actionScript>
<actionType>0</actionType>
<contextMenu>true</contextMenu>
<showInToolbar>true</showInToolbar>
</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("borrowNo", "借阅单号", head="BOR", digit=4), # 需求:系统自动单号
ftb.input_f("docNo", "文件编号"), # 业务档案编号:手输/挑选,勿用 noField
ftb.split_f("splitBasic", "基本信息"), # 分割线分组(全宽),height 默认 "1"(1px)
ftb.input_f("title", "标题"),
ftb.input_f("code", "编码"),
ftb.split_f("splitExtra", "补充说明"),
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_pairs(("DRAFT","草稿"), …) → JS opts.add("草稿","DRAFT")(平台 add=显示中文, 存值英文);opts(*labels) 仅当显示=存值同一字符串;**禁止**颠倒参数 |
| 字段工厂 | input_f, select_f, radio_f, textarea_f, split_f(分割线分组,height 默认 "1"), formula_f(公式定义→formulaField), htmleditor_f, data_f, user_f(人员/用户/员工), dept_f(部门/单位/组织→treedepartmentField), attach_f(附件→attachmentField), viewdlg_f(第 4 参=视图 id), no_f(**按需**自动单号/流水号), number_f;无条件必填传 required=True(写入 checkEmpty_system) |
| 视图映射 | map_cols(("formField","视图列id"), ...) → [{列id: formField}];load_column_field_map(view_dir) → {fieldName: 列id} |
| 布局 | build_layout(fields), build_template(fields)(含空壳 classicPc 与 pcLayoutMode=pc), 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. 硬规则清单¶
- [ ] **所有表单**默认**拖拽三栏**:`showType=new`、`pcLayoutMode=pc`;`layout` 含 `pc` + 空壳 `classicPc` + `mobile`;仅用户明确要求印刷时才 `showType=old` 且 `pcLayoutMode=classic`
- [ ] Form.name 非空唯一;type 正确;JsonTemplate 时 showType=new(印刷例外见上条)
- [ ] templatecontext 为字符串;parse 后根键为 fields + layout(可选 mobileAutoLayout、pcLayoutMode);禁止旧根级 formPanel
- [ ] fields 只存字段业务定义(**禁止** myselfrows/layout/padding*/showTitle/width/widthunit/style);layout.pc.formPanel 存拖拽布局;layout.classicPc 新建写空壳;layout.mobile 缺省 null;mobileAutoLayout 缺省 true;pcLayoutMode 缺省 pc
- [ ] 布局内字段用 fieldRef(fieldId 指向 fields 中 id,并带齐布局键);勿在 layout 内嵌完整 *Field;PC/Mobile fieldRef 可不同
- [ ] 默认布局:普通字段经 threeColumnContainer→左中右 container(栏1/栏2/栏3,myselfrows=33)→fieldRef;勿把普通 fieldRef 直挂 formPanel.children(全宽用 100% container)
- [ ] **分组**:有业务区块时用 **分割线** `splitField`(`split_f`,全宽,height 默认 `"1"`)分隔;勿用 labelField/空行冒充分组
- [ ] 三栏禁止:`style.flexDirection`(须 `flex-direction`)、栏容器缺 `width:33.333%`/`flex:1 1 33.333%`(会导致布局错乱)
- [ ] 每节点 scope=包裹键;children 单项为单键对象(仅布局节点有 children)
- [ ] **根元素 id**(表单根 `<Form id>` / `.activity` 根)= `__`+短 UUID;**其它 id**(字段 id / 布局节点 id)= 短 UUID(无 `__`);字段 name 英文;同表单 name/id 不重复
- [ ] store=Y 字段设 fieldtype;onlyCalculate=true 则不建列
- [ ] 日期=dataField;通用附件上传=`attachmentField`(`attach_f`);仅图片才用 imageuploadField;KM 专用才用 kmdataField;禁止 textarea/input 冒充附件
- [ ] 选项类填 OPTIONS;**存值英文码、界面显示中文**(`opts.add("中文","EN")` / `opts_pairs(("EN","中文"))`);禁止颠倒;查询表单不用 selectaboutField
- [ ] includeField 填 module(模块 id)+ viewid(视图 **id**);viewdialogField 为 store=N(**不存值**),须另有存值字段;dialogview=视图 id;**mapping 键=视图列 id、值=存值字段 name**;**优先**设 `eventmapping`=宿主存值字段 name(跟随控件显示),勿默认空独立按钮;禁止 `eventmapping: []`
- [ ] **业务引用用编码不用 XXID**:跨表挑选回写 `*Code`(及名称),勿默认回写 `*Id`;关联/校验 SQL 按业务编码查主数据(平台 user/dept/`PARENT`/`MAPPINGID` 除外)
- [ ] 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;**其列表/选择视图列禁止 FIELD**(见 generate-view-file「映射表列表列」)
- [ ] **无条件必填**:`validatelibs` 含系统默认 `core.dynaform.form.formfield.validate.checkEmpty_system`;禁止 `worklimitchecked=true` 或手写空值 `validaterule` 代替;可复用/条件校验用 `{软件}.application/valid/*.valid` 勾选后追加进 `validatelibs`