# 开发速查手册

以下是系统开发中常用的客制化注入变量、返回结果及数据格式速查。每一节只列出要点,完整说明请点击各节中的链接查看对应文档。

# 目标读者

本文档的目标读者为:本系统的开发和实施人员

# 表单 Hook

表单级的联动、默认值、校验、选项过滤等客制化,统一通过表单上的表单 Hook(formHook)和数据 Hook(dataHook)实现,注入变量与返回结果详见 表单客制化(Form Hook)。

提示

1.0 以前按字段配置的字段级联、字段搜索、字段校验、字段默认值(Field Hook)1.0 起已移除(改用表单 Hook)。

# 对象创建 Hook

详细信息请参考 对象客制化(Object Hook)。

# 注入变量

变量名称 变量类型 描述
object <? extends GormEntity> 待创建的对象实例
requestData Map 前端提交的原始数据
userContext tech.muyan.api.security.MuyanAuthentication 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure 打印执行日志,内容保存到执行记录
hookType tech.muyan.enums.ObjectHookType 当前执行的客制化类型

# 返回结果与异常处理

创建、更新、删除类客制化的返回值只会记录到执行记录中,平台不会用返回值修改对象。需要修改数据时,在"保存前"的客制化中直接修改 object(创建)或 newObject(更新)实例,修改会随本次操作一起保存:

// BEFORE_CREATE:新客户默认信用额度
if (object.creditLimit == null) {
  object.creditLimit = 10000
}
1
2
3
4

客制化代码可以通过抛出异常来中断操作或给出警告。平台按异常的实际类名判断,不认子类:

抛出的异常 结果
tech.muyan.exception.CustomLogicInterruptException 操作被中断,事务回滚,异常消息作为错误显示给用户。执行记录的状态为 SUCCESS(主动中断不算执行失败)
tech.muyan.exception.CustomLogicWarningException 操作继续执行,异常消息作为警告显示给用户。执行记录的状态为 SUCCESS_WITH_WARNING
其他任何异常(包括上面两个类的子类) 操作被中断,事务回滚,执行记录的状态为 FAILED;显示给用户的错误消息包含原异常的类名和消息
// BEFORE_DELETE:已审核的单据不允许删除
if (object.status == 'APPROVED') {
  throw new tech.muyan.exception.CustomLogicInterruptException("已审核的单据不能删除")
}
1
2
3
4

# 对象更新 Hook

详细信息请参考 对象客制化(Object Hook)。

# 注入变量

变量名称 变量类型 描述
oldObject <? extends GormEntity> 更新前的对象,在独立的 Session 中从数据库重新读取
newObject <? extends GormEntity> 按提交数据更新后的对象实例
requestData Map 前端提交的原始数据
userContext tech.muyan.api.security.MuyanAuthentication 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure 打印执行日志,内容保存到执行记录
hookType tech.muyan.enums.ObjectHookType 当前执行的客制化类型

# 返回结果与异常处理

创建、更新、删除类客制化的返回值只会记录到执行记录中,平台不会用返回值修改对象。需要修改数据时,在"保存前"的客制化中直接修改 object(创建)或 newObject(更新)实例,修改会随本次操作一起保存:

// BEFORE_CREATE:新客户默认信用额度
if (object.creditLimit == null) {
  object.creditLimit = 10000
}
1
2
3
4

客制化代码可以通过抛出异常来中断操作或给出警告。平台按异常的实际类名判断,不认子类:

抛出的异常 结果
tech.muyan.exception.CustomLogicInterruptException 操作被中断,事务回滚,异常消息作为错误显示给用户。执行记录的状态为 SUCCESS(主动中断不算执行失败)
tech.muyan.exception.CustomLogicWarningException 操作继续执行,异常消息作为警告显示给用户。执行记录的状态为 SUCCESS_WITH_WARNING
其他任何异常(包括上面两个类的子类) 操作被中断,事务回滚,执行记录的状态为 FAILED;显示给用户的错误消息包含原异常的类名和消息
// BEFORE_DELETE:已审核的单据不允许删除
if (object.status == 'APPROVED') {
  throw new tech.muyan.exception.CustomLogicInterruptException("已审核的单据不能删除")
}
1
2
3
4

# 对象删除 Hook

详细信息请参考 对象客制化(Object Hook)。

# 注入变量

变量名称 变量类型 描述
object <? extends GormEntity> 待删除的对象
userContext tech.muyan.api.security.MuyanAuthentication 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure 打印执行日志,内容保存到执行记录
hookType tech.muyan.enums.ObjectHookType 当前执行的客制化类型

# 返回结果与异常处理

创建、更新、删除类客制化的返回值只会记录到执行记录中,平台不会用返回值修改对象。需要修改数据时,在"保存前"的客制化中直接修改 object(创建)或 newObject(更新)实例,修改会随本次操作一起保存:

// BEFORE_CREATE:新客户默认信用额度
if (object.creditLimit == null) {
  object.creditLimit = 10000
}
1
2
3
4

客制化代码可以通过抛出异常来中断操作或给出警告。平台按异常的实际类名判断,不认子类:

抛出的异常 结果
tech.muyan.exception.CustomLogicInterruptException 操作被中断,事务回滚,异常消息作为错误显示给用户。执行记录的状态为 SUCCESS(主动中断不算执行失败)
tech.muyan.exception.CustomLogicWarningException 操作继续执行,异常消息作为警告显示给用户。执行记录的状态为 SUCCESS_WITH_WARNING
其他任何异常(包括上面两个类的子类) 操作被中断,事务回滚,执行记录的状态为 FAILED;显示给用户的错误消息包含原异常的类名和消息
// BEFORE_DELETE:已审核的单据不允许删除
if (object.status == 'APPROVED') {
  throw new tech.muyan.exception.CustomLogicInterruptException("已审核的单据不能删除")
}
1
2
3
4

# 动态创建权限

对象客制化类型为 CREATE 时,用于在运行时判断用户能否创建某类对象,详细信息请参考 对象权限控制。

# 注入变量

变量名称 变量类型 描述
objectType Class<?> 当前操作的对象类型
userContext tech.muyan.api.security.MuyanAuthentication 当前用户
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure<?> 用于打印执行日志的 log 闭包

# 返回结果

// 允许用户创建该对象,create 必须放在 result 中
// Allow user to create this object, "create" must be wrapped in "result"
return [result: [create: true]]
1
2

# 动态修改和删除权限

对象客制化类型 UPDATE_DELETE 当前版本不生效:对象能否修改、删除只由领域模型的 updateRoleRequirement、deleteRoleRequirement 决定。需要在运行时判断时,可以为这两个角色要求(RoleRequirement)配置自定义逻辑(customLogic),详见 对象权限控制。

注意

内置的 USER、DEVELOPER、ADMIN 等角色要求被大量模型、表单、菜单共用,直接给它们配置自定义逻辑会影响所有引用它们的地方。请为需要运行时判断的模型新建一个专用的角色要求,再把领域模型的 updateRoleRequirement 或 deleteRoleRequirement 指向它。

角色要求的自定义逻辑注入以下变量,返回 [result: true] 表示满足要求。配置了自定义逻辑后,系统不再自动检查 hasPermissionRoles 中的角色,需要时在逻辑中自行判断:

变量名称 变量类型 描述
object Object 正在判断权限的对象,可能为 null,见下方说明
hasPermissionRoles List<HierarchyRole> 该角色要求中配置的角色
user tech.muyan.api.security.MuyanAuthentication 当前用户

object 可能为 null

只有前端按行查询权限(POST /api/permissions/<领域模型>/,据此决定每一行是否显示「修改」「删除」按钮)时,object 才是具体的对象。后端处理修改、批量修改、删除请求(PUT、DELETE /api/data/...)时,检查的是“能否操作这类对象”,传入的 object 为 null。

因此逻辑必须先处理 object == null,否则会抛出空指针异常或返回不满足,所有修改、删除请求都会被拒绝。例如:

boolean isSales = user.authorities*.authority.contains('ROLE_SALES')
if (object == null) {
  // 接口层的检查:只按角色判断
  return [result: isSales]
}
// 按对象判断:已关闭的记录不显示修改、删除按钮
return [result: isSales && object.status != '已关闭']
1
2
3
4
5
6
7

按对象的判断目前只能控制界面上的按钮,直接调用接口仍然可以修改、删除这条记录,不能作为数据安全的保证。详见 对象权限控制。

# 定时任务核心逻辑

更多信息请参考 定时任务。

# 注入变量

变量名称 变量类型 描述
triggerDatetime java.time.OffsetDateTime 本次触发的计划时间
task tech.muyan.task.DynamicTask 当前任务,自定义参数可通过 task.parameters(JSON 字符串)读取
log Closure 打印执行日志,内容保存到执行记录
application grails.core.GrailsApplication 当前的 grails 应用上下文

# 返回结果

return [
  //执行结果,类型为文本
  //Execution result, type is text
  execResult: 'OK, Result' 
]
1
2
3
4

提示

定时任务没有启用逻辑,是否执行由任务的「是否启用」、生效时间和失效时间决定。

# Dynamic Action 显示逻辑

对象动作的启用逻辑(类型为 DYNAMIC_ACTION_ENABLE_LOGIC 的动态逻辑)决定动作对哪些记录可用。更多信息请参考 对象动作。

# 注入变量

变量名称 变量类型 描述
userContext tech.muyan.api.security.MuyanAuthentication 当前操作的用户信息
action tech.muyan.dynamic.action.DynamicAction 当前动作定义
form tech.muyan.dynamic.form.DynamicForm 动作所在的表单
objects List 待判断的记录列表
domainClass tech.muyan.DomainClass 记录的对象类型信息
log Closure 打印执行日志

# 返回结果

返回可用记录的 id 列表,不使用 [result: true/false] 结构:

return [
  enableIds: objects.findAll { it.accountLocked }.collect { it.id }
]
1
2
3
  • 返回结果中没有 enableIds(或为 null)时,所有记录都可用。
  • 启用逻辑执行时抛出异常,所有记录都不可用,错误写入后端日志。

# Dynamic Action 核心逻辑

更多信息请参考 对象动作。

# 注入变量

变量名称 变量类型 描述
userContext tech.muyan.api.security.MuyanAuthentication 当前操作的用户信息
action tech.muyan.dynamic.action.DynamicAction 当前动作定义
form tech.muyan.dynamic.form.DynamicForm 触发动作的表单
objects List 选中的记录列表。OBJECT_SINGLE 模式下也是列表,只有一个元素;CLASS_LEVEL 模式下为空列表
objectIds List<Long> 选中记录的 id 列表
domainClass / objectType tech.muyan.DomainClass 记录的对象类型信息,两个变量是同一个值
parameters Map<String, Object> 参数表单中用户填写的值,已按字段类型转换
rawParameters Map<String, Object> 参数表单中用户填写的原始值
searchConditions Map 执行时列表页面当前的搜索条件,见 按搜索条件批量处理
log Closure 打印执行日志,内容保存到执行记录的"执行日志"
application grails.core.GrailsApplication 当前的 grails 应用上下文

# 返回结果

[
  // 执行结果文字,保存到执行记录并显示给用户
  // Execution result text, saved to the execution record and shown to the user
  execResult: "执行的结果/Execution result",
  // 类型为 tech.muyan.storage.StorageFieldValue,前端执行完成后自动下载该文件
  // Type is tech.muyan.storage.StorageFieldValue, the frontend downloads it automatically
  download: storageFieldValue,
  // 以下字段只保存到执行记录,当前前端不使用
  // Following fields are only saved to the execution record, not used by the current frontend
  displayType: "markdown",
  execResultInfo: "补充信息/Extra info",
  redirect: "/some/page"
]
1
2
3
4
5
6
7
8
9
10
11
12

注意

动态逻辑引擎 OS_COMMAND(调用外部命令)在 1.0.0-beta18 中不可用:引擎执行时拿不到要运行的命令内容。

提示

动作的后处理逻辑、表单字段组的显示逻辑、向导(Wizard)的处理逻辑 1.0 起已移除。

# Widget 是否显示逻辑

更多信息请参考 仪表盘。

# 注入变量

变量名称 变量类型 描述
userContext tech.muyan.api.security.MuyanAuthentication 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
widget tech.muyan.dynamic.form.DynamicDashboardWidget 小组件对象

# 返回结果

// 表示该仪表盘小组件是否启用(对象动作的启用逻辑返回 [enableIds: [...]],不使用本结构)
// Indicates whether this dashboard widget is enabled (dynamic action enable logic returns [enableIds: [...]] instead)
[result: true | false]
1
2

# Widget 核心逻辑

# 注入变量

与 Widget 是否显示逻辑相同。

# 返回结果

Widget 核心逻辑返回结果的结构根据 widget 类型不同而不同,请参考 仪表盘 中各类 widget 的说明。

# Webhook

接收外部系统调用的 Webhook 在「系统集成 > Webhook」中配置,详细信息请参考 系统集成。1.0 以前的传入、传出系统集成(DynamicIntegration)1.0 起已移除(改用 Webhook 或动态服务)。

# 动态服务

更多信息请参考 动态服务。

# 服务定义

界面显示 属性名 类型 说明
Name name String 服务名称,租户内唯一,创建后不可修改;调用地址中使用这个名称
Logic logic tech.muyan.dynamic.DynamicLogic 服务的执行逻辑,逻辑类型为 DYNAMIC_SERVICE_CORE_LOGIC
Active active Boolean 是否激活
Enable Anonymous enableAnonymous Boolean 是否允许未登录调用,默认关闭
Enable Log enableLog Boolean 是否记录执行记录,默认关闭;只有开启后才会生成 执行记录
Body Type bodyType 枚举 请求体的解析方式:JSON、XML、INPUT_STREAM;不填时按 JSON 处理
Exec Records execRecords 该服务的执行记录

# 注入变量

变量名称 变量类型 描述
user tech.muyan.api.security.MuyanAuthentication 调用者。未登录调用时是匿名身份对象,不是 null
method String 请求的 HTTP 方法,例如 GET、POST
params Map<String, Object> URL 参数。只有一个值的参数直接是字符串,有多个值的参数是列表
body Object 请求体,按 Body Type 解析:JSON 为 JSON 对象,XML 为 XML 对象,INPUT_STREAM 为原始输入流
log Closure<?> 用于打印执行日志的 log 闭包,开启 Enable Log 后日志写进执行记录

# 动态过滤条件定义

动态过滤的条件定义格式如下所示,更多信息请参考 动态过滤。

// 下面的动态过滤条件的说明:
// 1. 状态字段等于 SUCCESS
// 2. type 是 FINDER, UPDATE 中的一个
// Below is the description of the dynamic filter conditions:
// 1. The status field is equal to SUCCESS
// 2. type is one of FINDER, UPDATE
{
  // key 是列名称: status 
  // key is the column name: status 
  "status": {  
    // 过滤的目标列
    // The target column to filter
    "columnKey": "status", 
    // 匹配规则:等于
    // Match rule: equal
    "matchMode": "=",      
    // 匹配的目标值: SUCCESS
    // Matching target value: SUCCESS
    "value": "SUCCESS"
  },
  "type": { // key 是列名称: type
    // 过滤的目标列, 与上一行的 key 相同
    // The target column to filter, same as the key in the previous line
    "columnKey": "type",           
    // 过滤的匹配规则:isOneOf (是其中某一个)
    // Filter matching rule: isOneOf (is one of them)
    "matchMode": "isOneOf",
    // 过滤的目标值: [FINDER, UPDATE]
    // Matching target value: [FINDER, UPDATE]
    "value": ["FINDER", "UPDATE"]  
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

# 动态匹配条件列表

如下列出了可在动态过滤中使用的、运行时动态渲染的匹配条件,更多信息请参考 动态过滤。

占位符 替换结果
${currentHour} 当前小时零分零秒的时间
${currentDay} 当前日期零点的时间
${currentWeek} 当前星期的星期一零点
${currentMonth} 当前月的第一天零点
${currentQuarter} 当前季度第一天的零点
${currentYear} 当前年的一月一日零点
${currentUserGroups} 当前用户拥有的角色名称列表,格式为 ['ROLE_USER','ROLE_ADMIN']
${currentUsername} 当前登录用户的用户名,见下方说明
${currentUserId} 当前登录用户的 id,见下方说明

# 获取动态配置

更多详细信息请参考 系统配置。

# 前端获取配置

import { getConfig } from "@muyantech/frontend-lib";

// 调用 GET /api/config/{key},返回 { key, value },value 为字符串
const config: any = await getConfig('websocket.heartbeatInterval');
const heartbeatInterval = Number(config?.value ?? 20);
1
2
3
4
5

# 后台获取配置

  1. 在插件的 Service 或其他 Spring Bean 中,直接注入 tech.muyan.api.config.DynamicConfigService。

  2. 在动态逻辑中,可以通过静态字段 tech.muyan.ConfigHelper.dynamicConfigService 获取服务:

import tech.muyan.ConfigHelper

Integer interval = ConfigHelper.dynamicConfigService
  .getByKeyOrDefault("websocket.heartbeatInterval", 20)
1
2
3
4
  1. 插件组件(实现 tech.muyan.api.MuyanPlatformComponent 的类)的字段上可以使用注解 @tech.muyan.api.annotations.DynamicConfig("配置 key")。插件加载时,平台通过该字段的公开 setter 写入配置值,配置变更后自动再次调用 setter 更新:
import tech.muyan.api.annotations.DynamicConfig

class HeartbeatSettings implements tech.muyan.api.MuyanPlatformComponent {
  @DynamicConfig("websocket.heartbeatInterval")
  Integer heartbeatInterval
}
1
2
3
4
5
6

# 全局配置或数据共享

在动态逻辑执行之间共享全局配置、连接或缓存数据的方法,详细信息请参考 常见问题。

当前系统提供了 tech.muyan.helper.RegistryHelper 类来进行全局共享数据的管理,可以通过

  • RegistryHelper.memoryGet(key) 来获取缓存数据,
  • RegistryHelper.memoryPut(key, value) 来设置缓存数据,返回该 key 之前保存的值(之前没有则返回 null),
  • RegistryHelper.memoryRemove(key) 来移除缓存数据。
注意 上述方法共享的全局数据保存在内存中,故只支持单服务器实例内的数据共享,不支持在多服务器实例之间进行数据共享。

# extInfo 速查

提示

详述配置中的 xxx?: 中的 ? 表示该配置是可选的,如果不配置则使用默认值,在 extInfo 字段的内容中,不应包含 ? 字符。

# DomainClass 定义中

{
  // 用户快捷搜索时,使用 name 和 label 两个字段进行搜索匹配
  // When users perform quick search, use the name and label fields for search matching
  "inlineSearchColumns": ["name", "label"],
  // 界面上显示 Object 控件时,显示其 label 字段的值作为标识
  // When displaying Object controls on the interface, show the value of its label field as identifier
  "labelField": "label",
  // 导入 CSV 数据时,当前 Domain 会在 DynamicLogic 和 User 之后加载
  // When importing CSV data, the current Domain will be loaded after DynamicLogic and User
  "loadAfter": ["DynamicLogic", "User"],
  // CSV 中关联到当前 Domain 的列未指定查询字段时,按 name 字段查找
  // When a CSV column referencing this Domain has no query field, look up by the name field
  "queryField": "name"
}
1
2
3
4
5
6
7
8
9
10
11
12
13

更多信息请参考 动态领域模型。

# DomainClassField 属性定义中

{
  // 适用于 Decimal 类型的字段,设置小数位数为 2
  // Applicable to Decimal type fields, set the number of decimal places to 2
  "scale": 2,
  // 适用于 Decimal 类型的字段,设置精度为 10
  // Applicable to Decimal type fields, set the precision to 10
  "precision": 10,
  // 指定 ENUM / ENUM_LIST 类型的字段的 Java 枚举类型定义
  // Specify the Java enum type definition of the ENUM / ENUM_LIST type field
  // 该枚举类的定义可以放在动态插件中
  // The enum class definition can be in a dynamic plugin
  "enumClass": "tech.muyan.mes.enums.WorkTaskStatusEnum",
  // 对于 MAPPED_DOMAIN_OBJECT/MAPPED_DOMAIN_OBJECT_COLLECTION 类型的字段,必须要设置其反向引用字段
  // For MAPPED_DOMAIN_OBJECT/MAPPED_DOMAIN_OBJECT_COLLECTION type fields, set its reverse reference field
  "mappedBy": "b"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

更多信息请参考 动态领域模型。

# Dynamic Action 定义中

{
  /** 参数表单的显示方式:drawer 抽屉(默认)或 modal 弹窗 */
  /** How the parameter form is shown: drawer (default) or modal */
  "layout"?: "drawer" | "modal",
  /** 参数表单提交按钮的文字,默认为"提交" */
  /** Text of the submit button of the parameter form */
  "submitButtonText"?: string,
  /** 结果展示方式:toast 消息提示,inContainer 显示在弹窗/抽屉中。
      没有参数表单时默认 toast,有参数表单时默认 inContainer */
  /** How the result is shown: toast, or inContainer (inside the modal/drawer).
      Defaults to toast without a parameter form, inContainer with one */
  "resultType"?: "toast" | "inContainer",
  /** 弹窗样式,当前只使用 width(layout 为 modal 时生效) */
  /** Modal style, only width is used (when layout is modal) */
  "style"?: { "width"?: number | string }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

更多信息请参考 对象动作。

# Dynamic Form 定义中

更多详细信息请参考 基础表单。

# 通用属性

{
    /** 表单标题。仅在表单的 label(显示名称)为空时使用;两者都为空时显示对象名称的翻译 */
    /** Form title. Used only when the form's label is empty; if both are empty, the translated domain name is shown */
    "domainTitle"?: string;
    /** 表单内字段是否横向排列(标签与输入框在同一行) */
    /** Whether fields are laid out horizontally (label and input on the same line) */
    "horizontal"?: boolean;
}
1
2
3
4
5
6
7

# List form 属性

{
    /** 数据刷新模式。当前只有 realtime 生效:列表会订阅数据变化并实时刷新;其他取值等同不设置 */
    /** Data refresh mode. Only realtime takes effect: the list subscribes to data changes and refreshes in real time; other values are the same as not setting it */
    "dataRefreshMode"?: "realtime";

    /** 点击列表右上角「创建」按钮时使用的创建表单名称,不设置时使用该对象的 CREATE 表单 */
    /** Name of the create form used by the "Create" button of the list; defaults to the CREATE form of the domain */
    "createFormName"?: string;

    /** 点击行内「修改」时使用的编辑表单名称,不设置时使用该对象的 UPDATE 表单 */
    /** Name of the update form used by the row "Edit" link; defaults to the UPDATE form of the domain */
    "updateFormName"?: string;

    /** 列表上方搜索区使用的查询表单名称,不设置时使用该对象的 FINDER 表单 */
    /** Name of the finder form used by the search panel; defaults to the FINDER form of the domain */
    "finderFormName"?: string;

    "listForm"?: {
      /** 是否隐藏列表上方的搜索区 */
      /** Whether to hide the search panel above the list */
      "disableSearchPanel"?: boolean;
      /** 默认过滤条件,格式与动态过滤的 conditions 相同;用户在搜索区输入同名条件时会覆盖这里的条件 */
      /** Default filter conditions, same format as dynamic filter conditions; a search condition with the same key entered by the user overrides it */
      "searchConditions"?: {
        "fieldName": {
          "columnKey": "fieldName",
          "matchMode": "=",
          "value": xxx
        }
      };
    };

    /** 行内编辑时是否按表单字段的 decides 刷新其他列(decides 当前版本不生效) */
    /** Whether inline editing refreshes the columns listed in the field's decides (decides does not work in the current version) */
    "enableRefreshColumn"?: boolean;

    /** 操作列宽度,默认 120 */
    /** Width of the operations column, default 120 */
    "operationsColumnWidth"?: number;

    /** 覆盖列表的按钮权限,只影响按钮是否显示,后端仍按对象权限校验;create 为 false 时隐藏「创建」按钮 */
    /** Overrides list button permissions; only affects button visibility, the backend still checks domain permissions; create=false hides the "Create" button */
    "permissions"?: {
      "create"?: boolean;
      "view"?: boolean;
      "update"?: boolean;
      "delete"?: boolean;
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# MasterDetail Form 属性

{
  /** MASTER_DETAIL_LIST 表单当前没有可配置的 extInfo 属性。 */
  /** MASTER_DETAIL_LIST forms currently have no configurable extInfo properties. */
  /** 左侧固定为对象的树,右侧固定为该对象的 UPDATE / CREATE 表单。 */
  /** The left side is always the object tree, the right side is always the UPDATE / CREATE form of the object. */
  /** 旧版本的 detailFormType、detailField、detailUpdatable 已不再生效。 */
  /** The legacy detailFormType, detailField and detailUpdatable are no longer used. */
}
1
2
3
4
5
6
7

# Dashboard Form 属性

{
  /** DASHBOARD 表单当前没有可配置的 extInfo 属性,旧版本的 refreshInterval 已不再生效。 */
  /** DASHBOARD forms currently have no configurable extInfo properties; the legacy refreshInterval is no longer used. */
}
1
2
3

# Dynamic Form Field 定义中

Dynamic Form Field 可用的 extInfo 与字段的类型关联,更多详细信息请参考 基础表单。

# 通用属性

所有类型的字段,其 extInfo 中均可使用的扩展属性如下

{
  // 隐藏字段的标签,只显示控件本身
  // Hides the field label and shows only the control
  "hideLabel"?: boolean;

  // 字段标签与只读值的 CSS 样式,例如 {"color": "#cf1322"}
  // CSS styles of the field label and of the read-only value, e.g. {"color": "#cf1322"}
  "labelStyle"?: object;
  "valueStyle"?: object;

  // meta 用于覆盖系统自动生成的表单字段的元数据,或者补充某一些属性,如 title, dataIndex, editable, updatable, elementType 等
  // meta is used to override the metadata of automatically generated form fields or to supplement certain properties such as title, dataIndex, editable, updatable, elementType, etc.
  // 在运行时,系统会将 meta 中的属性覆盖或者补充到系统自动生成的表单字段的元数据中
  // At runtime, the system will override or supplement the metadata of automatically generated form fields with the properties in meta
  "meta"?: {
      // 字段名
      // Field name
      "key": string;
      // 字段显示名
      // Field display name
      "title": string;
      // 字段在表单中的名字,应该和 key 一致
      // The name of the field in the form, should be the same as key
      "dataIndex": string;
      // 在创建表单中是否可编辑,false 时显示为只读
      // Whether the field is editable in create forms; false renders it read-only
      "editable"?: boolean;
      // 在编辑表单中是否可编辑,false 时显示为只读
      // Whether the field is editable in update forms; false renders it read-only
      "updatable"?: boolean;
      ...
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# file 字段

type 为 file 或 tech_muyan_storage_StorageFieldValue 时,可使用如下的 json 来设定控件的相关属性

{
  /** file、fileList、image、video 控件当前不读取 extInfo。 */
  /** The file, fileList, image and video controls currently do not read extInfo. */
  /** 旧版本的 accept、maxSizeMB、maxCount、totalMaxSizeMB 已不再生效。 */
  /** The legacy accept, maxSizeMB, maxCount and totalMaxSizeMB are no longer used. */
}
1
2
3
4
5

# code 字段

type 为 code 时,可以通过如下的 extInfo 设定高亮语法

{
  /** 代码编辑器控件显示时的高亮语法 */
  /** Syntax highlighting when displaying the code editor control */
  /** 不设置时使用字段的显示类型(code、json、markdown 等) */
  /** Defaults to the field's display type (code, json, markdown, ...) */
  "codeLanguage"?: "css" | "javascript" | "markdown" | "groovy" .....,
}
1
2
3
4
5
6

# object 字段

对于对象选择控件,可以使用如下的 extInfo 定义来设定其选择控件中的默认选项

{
    /** 候选项的过滤条件
     * 控件加载时用该条件查询前 20 条作为默认选项;输入关键字搜索时也会带上该条件
     */
    /** Filter conditions of the candidate options
     * Used to query the first 20 records as default options when the control loads, and also applied to keyword search
     */
    "defaultOptionsCondition"?: {
        "fieldName" : {
            /** 匹配的值 */
            /** Matching value */
            "value": xxxx,
            /** 匹配的字段名称, 支持 dot(.) 方式引用关联字段的某字段
             * 如 organization.name 引用 organization 字段的 name
             */
            /** Matching field name, supports referencing associated fields using dot (.) notation
             * For example, organization.name refers to the name field of the organization
             */
            "columnKey": "xxx",
            /** 匹配规则 */
            /** Matching rule */
            "matchMode": matchMode
        }
    },
    /** 输入关键字搜索时最多返回的候选项数量,默认 20 */
    /** Maximum number of candidates returned by keyword search, default 20 */
    "objectOptionsLimit"?: number,
    /** 在下拉框底部显示「创建」「编辑」按钮,用于直接新建被关联的对象。当前版本只有单选控件的「创建」可用 */
    /** Shows "Create" and "Edit" buttons at the bottom of the dropdown. Currently only "Create" of single-object controls works */
    "enableObjectOperations"?: boolean,
    /** 通过上述「创建」按钮打开的创建表单中,各字段的默认值查询条件;key 为被关联对象的对象类型字段名,取查询到的第一条 */
    /** Default value conditions for the create form opened by the "Create" button; each key is an object field of the target domain, the first match is used */
    "createFormDefaultValueConditions"?: {
        "fieldName": { "columnKey": "xxx", "matchMode": "=", "value": xxx }
    },
    /** 覆盖上述创建表单中字段的属性,例如 {"owner": {"disabled": true}} */
    /** Overrides field properties in that create form, e.g. {"owner": {"disabled": true}} */
    "overwriteCreateFormFieldProps"?: object
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 一对多对象字段

type 为 array 时(一对多的对象字段),可以使用如下的 extInfo 定义来设定其关联列表的显示

{
  /** 显示关联对象列表时使用的表单名称,不设置时使用关联对象的 LIST 表单 */
  /** Name of the form used to display the associated objects; defaults to the LIST form of the associated domain */
  "displayForm"?: "Form used to display the list of objects"
}
1
2
3
4

# 子表字段

// 1. 子表字段(表单字段)的 extInfo
// 1. extInfo of the sub-table form field
{
  /** 子表使用的表单名称(决定子表显示哪些列),不设置时使用子对象的 LIST 表单 */
  /** Name of the form rendering the sub-table (decides the columns); defaults to the LIST form of the child domain */
  "displayForm"?: "UserGroup Sub Table Form For User",
  /** 仅 relativeSubTable 使用:从当前对象出发的一对多字段路径,用点号分隔 */
  /** Only for relativeSubTable: path of a one-to-many field starting from the current object, separated by dots */
  "subTable"?: {
    "relativeNamePath"?: "customer.contacts"
  }
}

// 2. displayForm 指向的那个表单的 extInfo(行操作权限等写在这里,不是写在字段上)
// 2. extInfo of the form referenced by displayForm (row permissions etc. go here, not on the field)
{
  "subTable"?: {
    /** 是否可修改、新增、删除行;设置后覆盖默认值(默认可修改;带 mappedBy 的一对多子表要求主对象已保存才可删除,且当前不显示「创建」按钮) */
    /** Whether rows can be updated, created, deleted; overrides the defaults (updatable by default; for one-to-many with mappedBy, delete requires the owner to be saved and the "Create" button is currently not shown) */
    "updatable"?: true | false,
    "creatable"?: true | false,
    "deletable"?: true | false,
    /** 是否可拖拽排序 */
    /** Whether rows can be reordered by drag and drop */
    "dragSort"?: true | false,
    /** 为 true 时「创建」按钮放在表格底部(默认在顶部);可拖拽排序的子表中新增行也追加到末尾 */
    /** When true the "Create" button is placed at the bottom (top by default); in drag-sortable sub-tables new rows are appended */
    "asc"?: true | false,
    /** 在操作列中提供行级对象动作,只读状态下也显示操作列 */
    /** Offers row-level object actions in the operations column, which is shown even when read-only */
    "enableActions"?: true | false
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Last Updated: 2026/9/24 14:27:35