跳转至

表单定义编写指南

目标: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.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

新建最低配置:

  1. .form:id、name、type=1、showType=new(拖拽表单)、非空 templatecontext
  2. templatecontext:合法 JsonTemplate(根键 fields + layout + pcLayoutMode=pc,可选 mobileAutoLayout);所有表单普通字段默认三栏容器布局;业务分组用 分割线 splitField(见「布局约定」)
  3. 常用:至少一个保存操作 type=34 的 .activity
  4. 落盘后收尾(见「动态表同步触发」):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 收尾步骤(按序)

  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":[]}},"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。

步骤

  1. 确认目标表已存在,且非 auth_ / tlk_ / parm_ 前缀表;记下表名与主键列名(常见 ID)。
  2. 写 .form:type=65536、showType=new、非空 templatecontext(字段/布局与普通表单相同)。
  3. 写 mappingStr(CDATA JSON,见下);缺此字段或主键映射不全则设计器校验失败。
  4. 常用:至少一个保存类 .activity(如 type=34)。业务键(编码等)需唯一时:在字段 validaterule 与保存/保存并返回 beforeActionScript 用 queryBySQL 查映射物理表 + MAPPINGID 排除本行;保存前置用 createAlert 拦截。细则见 iscript-usage/form.md#字段唯一编码重复、activity.md#映射表单唯一-保存前置。
  5. **禁止**对映射表单使用 checkFieldUnique / validatelibs: checkFieldUnique_system(会误查 TLK_* 报错);**禁止**在表单/activity 脚本写 $SQL(未定义)。
  6. **禁止**投递 .create_form_table;建议 rebuild-index + clear_cache 供热加载。
  7. 该映射表的 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 会导致三栏错乱。

  1. style 键必须 kebab-case:flex-direction、padding-top……禁止 flexDirection、paddingTop
  2. 栏容器(左中右 container):myselfrows=33;style 含 width:33.333%、min-width:33.333%、flex:1 1 33.333%、box-sizing:border-box、display:flex
  3. threeColumnContainer.style:display:flex、flex-direction:row、flex-wrap:nowrap、width:100%、min-height:60px
  4. 字段 fieldRef:补 myselfrows/layout/showTitle/padding*/width/widthunit(见上表默认值);**不要**写在 fields 或字段 style 上
  5. 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:

"validatelibs": [
  "core.dynaform.form.formfield.validate.checkEmpty_system"
]

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:脚本选项 → optionsscript
  • optionseditmode=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: [](空数组 → HTML eventmapping="[]",控件异常)
  • 有明确存值宿主时,**不要**默认留空独立按钮;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 根):

.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("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_")

批量生成收尾

  1. 运行应用内 _gen_forms.py(或等价脚本)落盘全部 .form / .activity
  2. type∈{1,3,16} 向 workspace/.sync/ 投递 *.create_form_table;type=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. 硬规则清单

- [ ] **所有表单**默认**拖拽三栏**:`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`