关于 tailoring-overlay
tailoring-overlay 是用于 Datex Studio 的技能,可通过配置覆盖扩展核心库组件,或将其转为独立的 custom 变体。它指导开发者选择继承或扁平化,并管理配置编辑、校验及与基础组件的契约。
主要功能
- 01用 baseConfiguration 覆盖扩展组件
- 02添加定向修改和补充数据源
- 03将覆盖扁平化为独立 custom 变体
- 04获取、编辑、校验并推送配置
- 05跟踪继承项与基础契约变化
适用场景
- 01为客户定制网格列或流程
- 02基于基础组件扩展表单、编辑器或中心
- 03将覆盖转换为独立的 custom 组件
- 04推送前检查配置变更
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ datex/ skills --skill tailoring-overlay安装后在 Claude Code 中输入/tailoring-overlay确认已加载。
附带文件references/tailoring.md
详细介绍
定制覆盖
通过 baseConfiguration 覆盖机制扩展现有的核心库 Datex Studio 组件(最常见的是网格,但此模型同样适用于表单、编辑器和中心),或将定制覆盖扁平化为独立的 custom_<base> 变体。定制组件通过 baseConfiguration 引用基础组件,继承其完整契约,并在其上叠加有针对性的覆盖项——新增列、新流程、针对现有流程的自定义钩子、额外的次级数据源、屏蔽项。覆盖层会随基础组件的演进而更新;当客户需求的差异足够大,使继承不再有益时,可将其扁平化为完全自主的副本。
参考资料
- ../datex-studio-shared/branch-setup.md — 分支/连接选择(各技能通用)
- references/tailoring.md — 权威的定制参考:来源变体、定制组件的结构(
baseConfiguration、fromBaseConfiguration: true影子标记、onCustomization<Slot>FlowConfig钩子对、removed: true屏蔽)、次级增强数据源、扁平化为自定义组件的 12 步配方、预检清单 - ../datex-studio-conventions/file-format.md —
configurationTypeId表和 TypeScript 表达式编码规则(定制不是独立类型——类型标识符取决于定制组件本身,最常见的是grid) - ../datex-studio-conventions/naming-conventions.md —
tailored_/custom_前缀规则;tailored_<base>_grid命名 - ../datex-studio-runtime/runtime-globals.md — 定制流程代码中可用的平台注入全局变量(
$grid、$row、$flows、$apis、$utils,……)——与基础组件所属层级相同 - ../grid-creator/references/grids.md — 网格编写参考(最常见的定制组件类型)——五处规则、命令式单元格 API、内嵌数据源封装
- ../datasource-creator/references/flow-datasources.md — 定制次级(
tailored_ds_<base>)增强数据源的流程类型数据源结构 - ../datasource-creator/references/odata-datasources.md — 扁平化为新增 OData 字段授权的自定义组件时,需进行架构预检
- ../component-wiring-check/references/component-wiring.md — 变量必须声明规则适用于定制流程代码和扁平化后的自定义流程
依赖项
requirements-gathering技能 — 如果当前对话上下文中尚无需求简报,则调用此技能生成一份grid-creator技能 — 当定制组件是网格时调用;网格编写规则(文件结构、五处规则、命令式单元格 API、嵌入式数据源信封)同样适用于定制网格datasource-creator技能 — 当定制覆盖层的辅助增强数据源规模大到值得单独保存为文件,或在扁平化时将辅助数据源合并到主数据源时调用(仍须遵循五处规则)schema-explorer技能 — 当扁平化步骤首次依据 Footprint 架构授权 OData 字段时调用(扁平化流程第 10 步中的 OData 架构预检)component-wiring-check技能 — 用于审查定制覆盖层上的引用约定和 vars 必须声明规则(扁平化为自定义组件后还需再次审查)grid-validator技能 — 每次编辑定制网格后(以及扁平化为自定义网格后)都必须调用;重新运行网格预检清单,检查五处规则是否偏移、辅助数据源连线是否遗漏,以及扁平化后仍存在的 partial-lookupcode/id 同步问题component-validator技能 — 用于非网格定制组件(表单、编辑器、枢纽),适用于grid-validator不适用的情况
CLI 生命周期
定制不是独立的配置类型——定制覆盖层本身就是基础配置类型的一个实例(通常是网格)。CLI 接口与基础配置相同:针对定制变体的 referenceName 执行 dxs configuration get / dxs configuration upsert <type>。传递给 dxs configuration 的类型标识符应与基础配置的 ConfigurationEndpoints.normalize_type 输出一致——典型情况为 grid(对应 configurationTypeId: 3);较少见的情况则为 form、editor 或 hub。
创建新的定制覆盖层(网格示例):
# 1. Fetch the base body to identify referenceName / moduleId / columns / flows / datasources / inParams / outParams (read-only — used as source material for shadow copies)
dxs configuration get grid <base_referenceName> -b <branchId> -O base-envelope.json
jq .json base-envelope.json > base.json
# 2. Build body.json for the tailored overlay from scratch (see references/tailoring.md → Pre-Flight Checklist — Authoring a Tailored Overlay)
# - referenceName = tailored_<base_name>_grid
# - baseConfiguration = { configId: <base_referenceName>, moduleId: <base_package>, isOwned: null }
# - every inherited element carries fromBaseConfiguration: true (shadow copies; must match base exactly)
# - new elements carry fromBaseConfiguration: null
# - onCustomization<Slot>FlowConfig + ...ExecutionBehaviorType paired
# 3. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate grid -b <branchId> -D body.json
# 4. Create
dxs configuration upsert grid -b <branchId> -D body.json
编辑现有定制覆盖层:
# 1. Fetch — note the envelope wrapper
dxs configuration get grid <tailored_referenceName> -b <branchId> -O envelope.json
# 2. EXTRACT THE INNER BODY (round-trip footgun guard — see "Round-trip rule" below)
jq .json envelope.json > body.json
# 3. Edit body.json (respect shadow-copy rules — do not hand-edit fromBaseConfiguration: true elements)
# 4. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate grid -b <branchId> -D body.json
# 5. Push
dxs configuration upsert grid -b <branchId> -D body.json
将定制覆盖层扁平化为独立的自定义配置(网格示例):
# 1. Fetch the tailored overlay body
dxs configuration get grid <tailored_referenceName> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# 2. Walk the 12-step recipe in references/tailoring.md → Conversion Recipe: Flattening a Tailored Component
# - rename to custom_<base>; drop baseConfiguration; flip every fromBaseConfiguration: true -> null;
# cut every removed: true entry; collapse onCustomization* hooks per behavior;
# consolidate datasources; resolve overlapping columns; etc.
# 3. Validate — gates the push. Exit 1 = errors found (read validation_errors, fix, re-run), not a broken CLI
dxs configuration validate grid -b <branchId> -D body.json
# 4. Create the custom (the flattened body is a brand-new component)
dxs configuration upsert grid -b <branchId> -D body.json
# 5. Decide whether to keep or remove the original tailored overlay (often the custom replaces it)
往返规则(关键)
编辑现有配置时,绝不能直接将 envelope.json 通过管道传给 dxs configuration upsert——这会在不报错的情况下破坏配置内容。上面更正后的步骤(先用 jq 提取内部 .json 再编辑)是任何往返操作的必备步骤。关于规范的往返流程和底层错误,请参阅 ../datex-studio-shared/configuration-roundtrip.md。
拒绝更新由软件包拥有的引用名称。 在 dxs ≥ 0.4.19 中,如果请求体中的裸 referenceName 指向某个被引用软件包拥有的配置,且此分支没有该配置的自有副本,dxs configuration upsert 会拒绝操作,而不是悄悄将其分叉到应用程序的命名空间中。tailored_<base> / custom_<base> 覆盖项是由你的分支拥有的新引用名称,因此正常的定制流程不会触发此情况。只有当你按其自身名称编辑基础配置本身时才会遇到——而定制的目的正是避免这种情况。如果你确实需要这样做,--allow-external 会有意进行分叉;请先说明原因并征得用户同意。对于任何不是由你在此分支上创建的配置,请先执行预检:dxs source explore config <ref> -b <branch>(会报告 isExternal、applicationReferenceName 和 owner 块)。请参阅 ../datex-studio-shared/configuration-roundtrip.md。
定制后的请求体必须包含基础配置中的所有插槽——columns、datasourceConfig、嵌入的 datasources[]、flows / rowFlows、topToolbar / toolbar、filters、inParams / outParams / vars / rowVars——以及三个定制专用的可变部分(baseConfiguration、fromBaseConfiguration: true 影子标记、onCustomization<Slot>FlowConfig 钩子对,以及 removed: true 抑制标记)。往返规范(获取 → 用 jq 提取 → 编辑 → 验证 → 推送)不可妥协;覆盖项与基础配置之间的影子差异会在导入时表现为“Outdated contract”错误——这是最常见的定制失败模式。
工作流
[Phase 1: Setup + Requirements]
Follow branch-setup.md for branch/connection selection
|
[requirements brief in context?]
+-----+-----+
| |
YES NO -> invoke `requirements-gathering`
| |
+-----+------+
|
[Phase 2: Decide overlay vs flatten]
Consult references/tailoring.md → Provenance Variants and When to Flatten:
- core base + customer-specific tweaks tracking base evolution
-> author a tailored overlay (Phase 3)
- existing tailored overlay whose base evolution is no longer
relevant, or whose maintenance burden outweighs the benefit
-> flatten to standalone custom (Phase 5)
- net-new customer-specific component with no base relationship
-> stop; this is a from-scratch custom; invoke `grid-creator`
(or form-/editor-/hub-creator) directly
|
[Phase 3: Author tailored overlay]
Build body.json:
- referenceName = tailored_<base_name>_grid (analog for form/editor/hub)
- baseConfiguration = { configId, moduleId, isOwned: null } pointing at
the core base; moduleId is the BASE's package, not the overlay's
- Every inherited element copied with fromBaseConfiguration: true
(top-level params, columns, toolbar entries, flows, rowFlows, primary
datasource and its nested shape, dynamicFilters, configParameters);
shadow copies must match the base exactly — drift surfaces as
"Outdated contract" at import
- New elements (columns, toolbar buttons, flows) authored with
fromBaseConfiguration: null
- The three moving parts:
* fromBaseConfiguration: true vs null — shadow vs new
* onCustomization<Slot>FlowConfig + ...ExecutionBehaviorType
(before / after / replace) — hook pair; placeholder behavior on
null configs keeps the pair contractually shaped
* removed: true — suppress an inherited entry without deleting
the shadow
- Secondary (enrichment) datasource: tailored_ds_<base> embedded
alongside the inherited primary in datasources[]; invoke from
an `after` customization on on_data_loaded; batch-fetch by Id;
populate via Imperative Cell API; tailored-specific columns
carry empty declarative bindings (the one legitimate case)
- Vars / rowVars: every $grid.vars.<id> / $row.vars.<id> the
tailored flows write is declared at the overlay's top-level
vars[] / rowVars[]
- Invoke `component-wiring-check` to audit the tailored overlay's
reference contracts
|
[Phase 4: Validate + push]
dxs configuration validate <type> -b <branchId> -D body.json
|
+----+----+
| |
CREATE MODIFY-EXISTING
| |
| use the corrected round-trip
| (get -O envelope -> jq .json -> body)
| |
+----+----+
|
v
dxs configuration upsert <type> -b <branchId> -D body.json
(upsert creates or updates by referenceName — one command for both)
|
[Phase 5: (Optional) Flatten to standalone custom]
When the decision in Phase 2 was "flatten" (or you've reached the
point where the overlay no longer pays for itself), walk the
12-step recipe in references/tailoring.md:
1. Rename to custom_<base>; rename file; update embedded ids
2. Drop baseConfiguration -> null
3. Unshadow everything: flip every fromBaseConfiguration: true -> null
4. Cut every removed: true entry from JSON
5. Collapse onCustomization* hooks into the base slot per behavior;
null out onCustomization* fields; delete merged tailored_* flows
6. Consolidate datasources (preferred: merge secondary into primary;
touch queryOptions.selects AND all five type-metadata locations
AND dynamicFilters / dynamicOrderBys on BOTH sites) OR keep as
custom_ds_<base> if it queries a different entity set
7. Resolve overlapping columns (drop base's, rename tailored to
canonical id, update $row.cells.<col> references)
8. Update imperative populations — empty declarative bindings
replaced with $row.entity.<field> where the field is now
authoritative on the custom
9. Repackage — update $shell.<Package>.* / $types.<Package>.* /
$flows.<Package>.* to the custom's package
10. OData schema pre-flight via `schema-explorer` for newly-
authoritative OData field additions
11. Re-declare vars / rowVars at the custom's top level
12. Verify descriptions <= 100 chars
|
[Validate with `grid-validator` (mandatory for tailored grids and
flattened custom grids) — generic `component-validator` for non-grid
tailored components]
|
[invoke `post-edit-verification` for description/JSON/schema hygiene]
阶段详情
阶段 1:设置 + 需求
- 按照 ../datex-studio-shared/branch-setup.md 选择分支和连接。绝不要假定分支 ID——请向用户确认。
- 检查对话上下文中是否已有需求概要(由
requirements-gathering或其他调用技能生成)。- 有概要——直接使用。概要应明确要定制的基础组件(其
referenceName、软件包及其功能)、所需的客户专属调整(新增列 / 字段、新增工具栏操作、要通过before/after/replace行为挂接的基础流程、要抑制的基础元素)、驱动使用次级数据增强数据源需求的基础数据源选择列表中未包含的字段,以及目标是编写覆盖项还是将现有覆盖项扁平化为自定义配置。 - 没有概要——先调用
requirements-gathering技能。尽早确定选择定制还是自定义,可避免客户的实际需求与基础配置差异过大、以扁平化为更佳路径时,还得从头重写文件。
- 有概要——直接使用。概要应明确要定制的基础组件(其
阶段 2:决定采用覆盖还是扁平化
编写前请参阅 references/tailoring.md → Provenance Variants 和 Conversion Recipe: Flattening a Tailored Component。此决定会影响你是编写覆盖配置,还是将其合并为独立配置。
在以下情况下编写定制覆盖配置:
- 存在一个核心基础配置,且其中包含你所需的大部分行为。
- 客户专属调整的范围有限:新增少量列、新增工具栏操作、在基础流程中添加
before/after钩子、抑制继承的条目,或为基础配置的选择列表中没有的字段添加数据源进行丰富。 - 基础配置仍在积极演进,客户可以自动继承持续改进。
在以下情况下将定制覆盖配置扁平化为独立自定义配置:
- 客户的需求已发生较大偏离,覆盖后基础配置的行为所剩无几。
- 基础配置已弃用、被替换,或不再朝客户关注的方向演进。
- 维护覆盖配置的负担(跟踪基础配置的变更、保持影子副本同步、调试“过期合约”错误)已超过自动获取基础配置更新的收益。
在以下情况下停止并改用其他技能:
- 要编写的组件确实是全新组件,与任何基础配置都无关——直接调用
grid-creator(或form-creator/editor-creator/hub-creator)。custom_前缀仅用于已扁平化的覆盖配置,或有意表明“无上游”的客户专属组件;它不是每个从零开始编写的组件都应采用的默认命名约定。 - 对基础配置所做的更改足够通用,应当直接加入基础配置——请针对核心包提出更改,而不是创建覆盖配置。
阶段 3:编写定制覆盖配置
按照 references/tailoring.md → Pre-Flight Checklist — Authoring a Tailored Overlay 中的步骤构建 body.json。要点如下:
-
先读取基础配置的正文。 找出其
referenceName、moduleId(包)、列、流程、数据源、inParams 和 outParams。基础配置正文为只读内容——定制时切勿修改它。通过dxs configuration get <type> <base_referenceName>获取配置并提取其中的.json,即可得到用于创建影子副本的源材料。 -
文件名和
referenceName。 文件名须符合tailored_<base_name>_<type>-<type>.json模式(例如,网格的文件名为tailored_contact_addresses_grid-grid.json)。referenceName为 snake_case 格式,并以类型后缀结尾(tailored_contact_addresses_grid)。包可以与基础配置所属的包不同。支持跨包覆盖。请参阅 references/tailoring.md → Provenance Variants。 -
baseConfiguration指向基础配置。 这是顶层字段,必须存在且非空。configId= 基础配置的referenceName。moduleId= 基础配置所属的包(基础配置所在的包,而非覆盖配置所属的包)。对于交叉引用的基础配置,isOwned: null。将moduleId设为覆盖配置所属的包是最常见的编写错误之一——平台会根据{ moduleId, configId }解析基础配置,包名错误会导致无法找到基础配置,而且不会报错。请参阅 references/tailoring.md →baseConfiguration— pointer to the base。 -
影子副本带有
fromBaseConfiguration: true。 每个继承的元素——顶层inParams[]/outParams[]、columns[]、topToolbar[](以及嵌套的buttonConfig)、flows[]/rowFlows[]、datasources[]、datasources[0].queryOptionsObjectTypeDef[]、datasources[0].outParams[].objectTypeDef[]、datasources[0].inParams[]、datasources[0].dynamicFilters[]、datasources[0].configParameters[].parameter——都带有fromBaseConfiguration: true。影子内容必须与基础配置完全一致。 导入时的偏差会表现为针对实体结构或基础配置契约的“契约已过期”错误。不要手动编辑影子内容;如果基础配置自复制后发生了变化,请从基础配置重新拉取。请参阅 references/tailoring.md →fromBaseConfiguration: true— 影子标记。 -
新元素带有
fromBaseConfiguration: null。 在覆盖配置中新增的定制列、新工具栏按钮、新工作流 / 行工作流,其值均为null。将所有值从true批量改为null是扁平化步骤,不是常规的编写操作。 -
定制钩子对。 对于要挂接的每个基础工作流槽位(
onInitFlowConfig、onDataLoadedFlowConfig、onSelectionChangedFlowConfig、onInitNewRowFlowConfig、onSaveNewRowFlowConfig、onSaveExistingRowFlowConfig、onRowDataLoadedFlowConfig、onExcelImportFlowConfig、onExcelExportFlowConfig;表单 / 编辑器 / Hub 中也有类似配置),请在匹配的onCustomization<Slot>FlowConfig中填入{ flowId, flowParameters },指向覆盖配置flows[]/rowFlows[]中的工作流(通常命名为tailored_<slot>),并将onCustomization<Slot>FlowConfigExecutionBehaviorType设为before/after/replace。即使配置为 null,...ExecutionBehaviorType字段也会存在——它会携带一个占位值(通常为"after"),以确保这对字段始终符合契约结构。按钮也有自己的字段对:buttonConfig.clickFlowConfig(由基础配置定义)和buttonConfig.onCustomizationClickFlowConfig。请参阅 references/tailoring.md →onCustomization<Slot>FlowConfig+...ExecutionBehaviorType— 钩子对。 -
removed: true会抑制条目,而不删除它。 将其应用于继承的(fromBaseConfiguration: true)条目,可在运行时抑制该条目,同时保留影子副本。它会出现在工具栏条目(及嵌套的buttonConfig)中,并在支持的集合中采用相同的结构。不要从 JSON 中删除被抑制的条目——平台需要此影子副本来与基础配置协调。请参阅 references/tailoring.md →removed: true— 抑制标记。 -
次级(增强)数据源——标准模式。 如果需要基础数据源
select列表中没有的字段(UDF、外部系统字段、FK 显示解析),请在覆盖配置的datasources[]数组中嵌入一个tailored_ds_<base>数据源,并与继承的主数据源(fromBaseConfiguration: true)并列。然后,在针对on_data_loaded的after定制中,按Id in ${ids}对次级数据源进行批量获取,并通过 命令式单元格 API 将增强数据写入每一行。定制专属列的声明式displayControl/editControl配置中应包含空的value字符串——这些值会通过命令式方式填充。这是允许声明式绑定为空的情况之一;不要将其“修正”为$row.entity.<field>。请参阅 references/tailoring.md → 次级(增强)数据源。 -
已声明 Vars / rowVars。 定制流程写入的任何
$grid.vars.<id>/$row.vars.<id>都必须在 overlay 顶层的vars[]/rowVars[]数组中声明。基类可能已声明一些变量;overlay 写入的新变量应全新定义,并将fromBaseConfiguration设为null。请参阅 ../component-wiring-check/references/component-wiring.md。 -
调用
component-wiring-check。 推送前,审查 overlay 的引用契约以及变量必须声明的规则。如果定制组件是网格,还要确认任何新建的次级数据源都符合内部网格 → 嵌入式数据源契约。
第 4 阶段:验证并推送
# Validate the body locally against the branch (type identifier matches the tailored component's base type — `grid` in the typical case).
# Exit 1 = validation found errors (read validation_errors, fix body.json, re-run) — not a broken CLI. Do not push on exit 1.
dxs configuration validate grid -b <branchId> -D body.json
# For a new tailored overlay
dxs configuration upsert grid -b <branchId> -D body.json
# For modify-existing (round-trip — never skip the jq extract)
dxs configuration get grid <tailored_referenceName> -b <branchId> -O envelope.json
jq .json envelope.json > body.json
# ... edit body.json ...
dxs configuration upsert grid -b <branchId> -D body.json
验证会在推送前发现缺失的必填字段、格式错误的参数描述符结构、未定义的流程 ID 引用以及基类解析错误。它不会发现 overlay 与基类之间的影子漂移(导入时会显示为“契约过期”)、未成对的 onCustomization<Slot>FlowConfig / ...ExecutionBehaviorType 配置、次级数据源在五处定义中的漂移、定制列上不完整的 lookupcode/id 同步,或意外从 JSON 中删除的 removed: true 条目。推送前,请逐项检查 references/tailoring.md → 预检清单,并始终调用 grid-validator(针对定制网格)或 component-validator(针对非网格定制组件)。
第 5 阶段:(可选)扁平化为独立定制组件
扁平化会将 overlay 合并为权威副本。一旦识别出所有定制面,扁平化操作就是机械性的,但它会改动整个文件——请明确规划 12 个步骤,避免遗漏。请从头到尾按照 references/tailoring.md → 转换步骤:扁平化定制组件 中的流程操作。概要:
- 重命名。 将
referenceName→custom_<base>;重命名文件;更新内嵌 ID(tailored_ds_<base>→custom_ds_<base>,tailored_on_<slot>→custom_on_<slot>或合并到现有项中)。 - 移除
baseConfiguration。 将其设为null。 - 取消所有继承覆盖。 将文件中所有
fromBaseConfiguration: true改为null。所有元素都变为权威定义。 - 删除被抑制的项。 从 JSON 中删除所有
removed: true的条目(不再有基础配置可供抑制)。 - 合并自定义钩子。 对每一对已填充的
onCustomization<Slot>FlowConfig,根据其行为将定制流程合并到基础槽位(before前置,after后置,replace覆盖)。将所有onCustomization*字段置为null。流程主体合并后,从flows[]/rowFlows[]中删除独立的tailored_*流程(除非仍被其他流程调用)。 - 整合数据源。 首选方案:将次要数据源的字段合并到主要数据源。请检查以下所有位置,漏掉任何一处都会导致静默失败:
datasources[0].queryOptions.selects(OData$select子句,即运行时数据结构;若没有此项,即使所有类型元数据位置都正确,$row.entity上的字段仍为undefined;嵌套字段位于expands[].queryOptions.selects);grids.md → 数据源绑定——五处必须保持同步 中列出的全部五处类型元数据位置;如果合并后的字段可筛选或可排序,还要检查datasources[0].dynamicFilters/dynamicOrderBys和datasourceConfig.dynamicFilters/dynamicOrderBys。然后彻底删除次要数据源,并移除加载后的数据增强调用。替代方案:如果次要数据源查询不同的实体集,或无法合并到单个查询中,则保留它,并将其命名为custom_ds_<base>。 - 解决重叠列。 如果定制新增了平行列(
tailored_country覆盖继承的country),则删除基础配置中的列,并将定制列重命名为规范 ID。更新流程代码中的所有$row.cells.<col>引用。 - 更新命令式赋值。 现在由数据源绑定的单元格:将空的声明式
value替换为$row.entity.<field>;从on_data_loaded中移除匹配的row.cells.<col>.displayControl.text = ...代码行。仍需命令式赋值的单元格(外键显示解析、计算文本)则保留。 - 重新打包。 自定义组件通常位于定制组件所在的包中,而非基础组件所在的包中。更新跨组件引用(
$shell.<Package>.*、$types.<Package>.*、$flows.<Package>.*),使其与之匹配。 - OData 架构预检。 新增的权威 OData 字段会通过
schema-explorer技能触发架构预检,参见odata-datasources.md→ 预检。可通过 OData 查询、但未包含在导出架构中的 UDF 字段可以接受;请在变更说明中记录这些字段。 - 重新声明 vars / rowVars。 被扁平化流程修改的任何
$grid.vars.<id>/$row.vars.<id>,都必须在自定义组件顶层的vars[]/rowVars[]中声明。它们可能在覆盖配置中隐式继承,但在独立配置中必须显式声明。 - 确认描述不超过 100 个字符。
完成此流程后,自定义组件将不含 baseConfiguration、fromBaseConfiguration: true 标记、onCustomization* 值或 removed 标记——其结构与从零创建的自定义网格(或表单 / 编辑器 / hub)无异。然后,将其作为全新组件推送(dxs configuration upsert <type> -b <branchId> -D body.json);扁平化后的主体是全新创建,而非更新覆盖配置的 ID。
发布前检查清单
推送前,请完整检查 references/tailoring.md → Pre-Flight Checklist — Authoring a Tailored Overlay(对于扁平化,还要从头到尾检查 Conversion Recipe)。快速版:
编写定制覆盖层:
- 已设置
baseConfiguration,其中configId= 基础配置的referenceName,moduleId= 基础配置的程序包(不是覆盖层的程序包),isOwned: null。 - 每个继承元素都带有
fromBaseConfiguration: true——顶层参数、列、工具栏条目、流、rowFlows、主数据源、嵌套实体结构、动态筛选条件/排序条件、configParameters。 - 新增元素带有
fromBaseConfiguration: null。 - 自定义钩子正确配对。 每个
onCustomization<Slot>FlowConfig非空的钩子,都有一个已填充的对应...ExecutionBehaviorType;引用的流存在于flows[]/rowFlows[]中。 - 影子项与基础配置完全一致。 如果基础配置自复制后发生了变更,请重新拉取影子项——导入时的差异会显示为“Outdated contract”。
- 已声明 Vars / rowVars。 定制流写入的每个
$grid.vars.<id>/$row.vars.<id>,都在覆盖层上有顶层声明。 - 通过辅助数据源以命令式方式填充的扩充列,带有空的声明式绑定。 不要将其“修复”为
$row.entity.<field>。 removed: true会保留影子项——不要从 JSON 中删除被抑制的条目。
扁平化为自定义配置:
baseConfiguration→null;referenceName→custom_<base>;重命名文件。- 将所有
fromBaseConfiguration: true→null。 - 从 JSON 中删除所有
removed: true条目。 - 根据对应行为,将每组成对的
onCustomization<Slot>FlowConfig折叠到基础槽位;所有onCustomization*字段设为 null。 - 合并辅助数据源(或将其保留为
custom_ds_<base>并更新引用)。 - 对于合并到 OData 的字段:更新
queryOptions.selects(嵌套字段还要更新expands[].queryOptions.selects),并更新全部五处类型元数据位置。这是扁平化后最常见的静默故障。 - 解决重叠列问题;更新所有
$row.cells.<col>引用。 - 在适当情况下,为命令式填充绑定数据源;将空的声明式绑定替换为
$row.entity.<field>。 - 在自定义配置的顶层重新声明 Vars / rowVars。
- 将跨组件引用(
$shell.<Package>.*、$types.<Package>.*、$flows.<Package>.*)更新为自定义配置的程序包。 - 完成 OData 架构预检;记录 UDF 缺失情况。
- 描述 ≤ 100 个字符。
常见错误
| 错误 | 修正方法 |
|---|---|
将 baseConfiguration.moduleId 设置为覆盖配置所属的包,而非基配置所属的包 | 平台通过 { moduleId, configId } 查找基配置。包名错误会导致无法找到基配置,覆盖配置无法协调影子条目,并导致导入失败。moduleId 始终是基配置所属的包;通过此字段可支持跨包覆盖。 |
手动编辑 fromBaseConfiguration: true 影子条目的内容 | 定义由基配置拥有。影子条目偏离基配置会在导入时触发“Outdated contract”错误。如果基配置已更改,请从基配置重新拉取影子条目;不要手动编辑。 |
已填充 onCustomization<Slot>FlowConfig,但 ...ExecutionBehaviorType 仍为默认占位值,或反之 | 这两个字段在契约中始终成对出现,但只有非空配置会触发执行。请同时填充两者:将配置设为 { flowId, flowParameters },并根据意图将行为设为 before / after / replace。 |
onCustomization<Slot>FlowConfig 引用的定制流程不存在于覆盖配置的 flows[] / rowFlows[] 中 | 验证会捕获部分情况,其余情况会在运行时发现。在填充钩子之前,请先在相应的 flows[] / rowFlows[] 数组中创建 fromBaseConfiguration: null 的定制流程。 |
从 JSON 中删除继承条目,而不是设置 removed: true | 影子条目必须保留,以便平台与基配置协调。删除条目会导致“Outdated contract”,因为基配置仍声明了该条目。将 removed: true 设为隐藏条目,可保留影子条目并在运行时隐藏该条目。 |
当新定制列由次级数据源以命令式方式填充时,将其“修正”为 $row.entity.<field> | 声明式 value 为空是有意为之——单元格由 on_data_loaded 上的 after 定制逻辑填充。所谓的“修正”会在每次渲染时覆盖命令式填充值。请参阅 grids.md → Empty Declarative Bindings Are Legitimate。 |
| 遗漏次级数据增强源;定制列显示空值 | 标准模式是让 datasources[] 同时包含继承的主数据源(设置 fromBaseConfiguration: true)和新的 tailored_ds_<base>(设置 fromBaseConfiguration: null)。没有次级数据源,就无法获取额外字段。 |
在定制流程代码中使用 $grid.vars.<id> = ... 或 $row.vars.<id> = ...,但未在覆盖配置中声明 | 导入错误:Property 'vars' does not exist on type 'IGrid' / IRow。请在顶层 vars[] / rowVars[] 中声明定制流程写入的每个 var / rowVar。继承基配置不会延伸到覆盖配置新增的 vars。 |
扁平化:合并 OData 后端的次级数据源到主数据源时,遗漏了 queryOptions.selects | 静默失败——类型元数据表明字段存在,但运行时数据不包含该字段。$row.entity.<field> 的值为 undefined。除了五个类型元数据位置外,还要将该字段添加到 datasources[0].queryOptions.selects(嵌套字段则添加到相应的 expands[].queryOptions.selects)。这是最常见的扁平化失败原因。 |
扁平化:部分元素仍保留 fromBaseConfiguration: true | 定制配置应从头到尾拥有权威定义。任何残留的 fromBaseConfiguration: true 都意味着平台仍会尝试解析基配置,但 baseConfiguration 为 null,因此解析会失败。请一次性将所有此类值改为 null。 |
扁平化:忘记删除流程主体已合并到基配置插槽的 tailored_* 流程 | 该流程现在是死代码;文件中其他位置对它的引用也可能已过时。流程主体合并后,请从 flows[] / rowFlows[] 中删除独立条目。(如果仍作为辅助流程调用,则保留。) |
| 扁平化:未在定制配置中重新声明从基配置继承的 vars / rowVars | 覆盖配置可能隐式继承了基配置中的部分 vars。对于独立的定制配置,每个 var / rowVar 都必须显式声明。检查扁平化后的流程代码中每个 $grid.vars.<id> / $row.vars.<id> 引用,并确认其都有顶层声明。 |
将 custom_<base> 当作从零构建组件的默认命名约定 | custom_ 前缀表示“扁平化定制覆盖配置的结果”,或“有意表明不存在上游关系”。对于没有基配置历史记录的真正全新组件,惯用名称不带 custom_ 前缀。不要默认使用 custom_。 |
直接将 dxs configuration get -O envelope.json 的输出传入 dxs configuration upsert -D envelope.json | 这会静默破坏配置内容。编辑前务必先运行 jq .json envelope.json > body.json。请参阅上文的“往返规则”。 |
使用 --allow-external 绕过 upsert 拒绝 | 拒绝意味着引用名称归被引用的包所有,而当前分支没有该名称的自有副本——此标志会创建分叉,并导致后续包更新无法流入。请使用新的 tailored_<base> 名称进行定制,或在其所属包中修复并重新固定版本。只有在用户明确同意时才创建分叉。 |
覆盖配置或扁平化后的定制配置中,description 超过 100 个字符 | SQL 列长度限制——推送将无法通过验证。请缩短描述。 |
推送定制网格修改时未调用 grid-validator | 通用的 component-validator 无法发现次级数据源五处定义不一致、遗漏 OData selects、动态筛选器注册镜像不一致,或定制列的 lookupcode/id 部分同步问题。定制网格必须调用 grid-validator。 |
编辑完成后,请调用 post-edit-verification 检查描述、JSON 和架构规范。如果你调整了网格,还必须调用 grid-validator(强制要求——网格存在信封/文本显示方面的问题)。对于非网格的定制组件,请调用 component-validator。 |