容器与复用组件
group-panel/data-repeater(数据循环)/component-instance(母版-实例)/layout-section+nav-views(视图容器与导航)/window-instance(画面组件)。 返回目录:index.md;全量选项字典(最权威):widget-schema.generated.json。
5. group-panel —— 分组面板(容器)
- type:
group-panel默认尺寸:[400,300]默认名: "分组面板" - 编辑器插入/
addWidget预置 options:{ background:false, border:false }。⚠️ 这是meta.defaultOptions,手写 JSON 载入不补——按蓝图回落background:true(#01011362深色半透底)+border:true(圆角 15、线型 none);不要面板底就显式写"background": false, "border": false。 - 子组件放节点的
widgets: [],其position相对面板左上角。
特有 options:
| name | 类型 | 默认 | 说明 |
|---|---|---|---|
children-no-events |
boolean |
false |
内部组件穿透 |
child-state-follow |
boolean |
false |
内部组件状态跟随 |
child-select-exclusive |
boolean |
false |
内部组件选中互斥 |
canvas-grow |
select |
reflow |
页面开 fill-auto-expand 时:reflow 吃满剩余空间(子项重排) / scale 保持设计比例。布局容器同款。⚠️ scale 只管尺寸不管位置:摆在画布增长那一侧(贴右/贴底)会留背景空白,应放在靠里一侧让主视觉吸收增量,或改用 responsive-x/y 锚点贴边(见 data/布局/适配/18) |
show-scrollbar |
boolean |
false |
显示滚动条 |
scrollbar-size/scrollbar-color/scrollbar-bg-color |
— | 3/#a5a5a5/transparent |
滚动条样式 |
无组件特有绑定字段。
5b. data-repeater —— 数据循环容器(单元模板 × 数据行)
- type:
data-repeater默认尺寸:[900,240]默认名: "数据循环容器" - 默认 options:
{ container-layout:'fluid', flex-direction:'row' }(单元平铺成流式网格、自动换行)。 - 自定义触发器:
itemClicked(点击某项时,payload = 该行数据)。 - 结构(关键差异):模板不放
widgets,而是放在节点顶层的unitTemplate(与options平级)——一个group-panel节点;运行期按item-fields数据行把它派生成 N 份(派生实例不持久化,工程 JSON 只存这一份模板)。
{
"type": "data-repeater", "uid": "w_rep", "name": "卡片墙",
"options": {
"position": [80,200], "size": [1760,800],
"container-layout": "fluid", "flex-direction": "row",
"unit-size": [330,170], "fluid-gap-type": "fixed", "fluid-gap-value": 18, "padding": [10,10,10,10],
"max-items": 200, "overflow": "scroll-y", "empty-text": "暂无数据", "click-child-as-item": true,
"item-fields": [["conn_x","tbl_x","名称"]],
"row-key-field": [["conn_x","tbl_x","名称"]]
},
"unitTemplate": {
"type": "group-panel", "uid": "w_rep__unit", "name": "单元模板",
"options": { "position":[0,0], "size":[330,170], "background": true, "border": true },
"widgets": [
{ "type":"basic-text", "uid":"tpl_name",
"options": { "position":[22,18], "size":[286,32], "value-field": [["conn_x","tbl_x","名称"]] } }
]
}
}
特有 options:
| name | 类型 | 默认 | 说明 |
|---|---|---|---|
unit-size |
vector<宽,高>(px,min=1) |
[180,96] |
每个单元的宽高;非正/缺省回落默认 |
max-items |
number(min=0) |
200 |
最大派生单元数;0=不限制(防数据过多导致单元爆炸) |
overflow |
select |
scroll-y |
单元总尺寸超出容器时的处理:scroll-y(卡片换行后纵向滚动条)、scroll-x(单行排列横向滚动条)、hidden(裁剪隐藏)。编辑模板态恒裁剪不滚动 |
empty-text |
string |
暂无数据 |
无数据且非编辑态时的占位文案 |
click-child-as-item |
boolean |
true |
true=点单元内任意子组件都触发「单元点击」携带该行数据;false=只触发子组件自身事件(点单元空白仍触发) |
fluid-gap-type/fluid-gap-value/fluid-align-items/fluid-justify-content/padding |
— | — | 流式网格排布(同容器流式布局键,见基类/§布局) |
绑定字段:
| name | 数量 | 说明 |
|---|---|---|
item-fields |
≥1 | 要重复的数据(决定行数;其所在表即「行作用域」覆盖范围) |
row-key-field |
0~1 | 行稳定标识;数据刷新/排序时按此键复用单元实例、保留交互状态(缺省用行序号) |
行作用域(核心机制):unitTemplate 内的子组件用普通字段绑定(文本 value-field、图表 metric-fields/category-field、表格 table-data-fields 等)即可——只要绑定的表落在 item-fields/row-key-field 所在表内,取数会自动落到「当前行」(类比 Vue v-for 的行作用域),无须为每行单独配置;绑定别的表则照常全局取数(老组件零回归)。
推荐视觉/布局方案:
- 页面/外层主区用
container-layout:"static"(绝对定位position+size),流式只放在data-repeater内部(卡片墙 =flex-direction:"row"+ 固定unit-size+overflow:"scroll-y")。这是最稳的“静态画布内嵌流式”配方:标题/卡片区/详情区各给明确position+size,互不挤压,绝不拉伸或越界。 - ⚠️ 慎用根页面
container-layout:"fluid"+flex-direction:"column":fluid 布局的flex-wrap由fluid-wrap选项控制、默认"wrap"——纵向流式内容高于视口时会换到第二列,表现为“内容整体跑到最右边、几乎看不见”。确需纵向流式时把fluid-wrap显式设为"nowrap";否则优先静态画布,或用单行row且子项总宽可控。 - 卡片固定
unit-size(如[300,186]),靠overflow:"scroll-y"出滚动条;详情面板用固定position+size并在其内部也用 static 给子项排位,避免纵向流式的换列风险。 - 深色主题推荐背景
#0B1220、卡片#101B2D、详情#0F172A,主色#38BDF8、数值色#22C55E、警示/按钮#FBBF24、次级文字#8EA3C2。
项级事件交互(三种绑定,均逐项独立派发):
| 想绑在哪 | 怎么配 | payload / 目标 |
|---|---|---|
| 整项(点任意卡片) | 在 data-repeater 自身 options.interactions 配 itemClicked |
payload = 本行整行数据(键=字段 uid),公式里 {:字段} 可取任意字段(如 {:设备}、{:负载})。click-child-as-item:true 时点卡片内任意子组件都会触发 |
| 项内某元素(如卡内按钮/标签) | 在 unitTemplate 内该子组件 options.interactions 配它自己的触发器(click/mouseEnter…),动作目标选自身(element: []) |
随模板克隆到每张卡片、各自独立触发。⚠️子组件自身的 click 无行数据 payload;要带本行数据请用容器的 itemClicked |
| 每项整体(如整卡悬停高亮) | 在 unitTemplate 根面板 options.interactions 配 mouseEnter/mouseLeave,动作目标=自身(element: []) |
行包裹继承模板根 options(含 interactions),逐卡片独立 |
⚠️ 项内/根面板的自指动作
element必须留空数组[](=自身)——不要写模板里的 uid,否则派生后会指向模板 uid 而非各派生实例,命不中。要联动容器外组件(如详情面板)则照常用[{ "targetPath":["screenUid","widgetUid"], "refType":"element" }]。 触发器多在预览/播放态才生效。完整可导入示例:data/组件/入门/26-数据循环容器-事件交互-data-repeater.vpproj.json。
5bb. component-instance —— 组合组件实例(母版-实例,对照「有身份的组件」)
- type:
component-instance默认尺寸:[400,300]默认名: 取母版名 / "组合组件" - 默认 options:
{ background:false, border:false }(外观取自母版派生子项)。 - 用途:把「多组件相互调用 + 数据 + 交互」整组沉淀成母版(VpComponentMaster),在大屏上以实例形式复用;改母版
version+1,实例据masterVersion判过期可同步。区别于group-panel「快照图章」:实例是有身份的引用(masterId),可批量同步、可传 props。 - 结构(关键差异):实例不放
widgets;母版子树放在节点顶层的master.node(一个group-panel,与options平级的master对象内)。运行期把母版按props/overrides展开为具体子树,内部 uid 派生为实例uid::母版子uid(确定性、与其它实例天然隔离,组内 element 引用随派生 uid 自动改写)。派生实例不持久化,工程 JSON 只存母版快照 + props/overrides。
{
"type": "component-instance", "uid": "inst_kpi", "name": "在线用户(实例)",
"masterId": "master_kpi_card", // 母版 id(库记录主键)
"masterVersion": 1, // 实例记录的母版版本(< 母版 version 即过期)
"options": { "position": [300,380], "size": [460,320], "background": false, "border": false },
"props": { // 输入参数:键=master.propsSchema[].key
"title": "在线用户数",
"unit": "人",
"valueField": [["conn_kpi","tbl_kpi","在线用户"]] // 数据输入 prop:指向**消费工程**的连接三元组
},
"overrides": { "m_title": { "text-font": { "size": 20, "color": "#00FFC6" } } }, // 按母版原 uid 打 option 补丁(props 之后)
"master": { // 内嵌母版快照(离线/导出/SDK 渲染用;编辑器发布时由库回灌)
"masterId": "master_kpi_card", "name": "KPI 卡片", "version": 1,
"propsSchema": [
{ "key": "title", "label": "标题", "type": "string", "target": { "uid": "m_title", "path": ["text"] }, "default": "指标标题" },
{ "key": "valueField", "label": "数值字段", "type": "field", "target": { "uid": "m_value", "path": ["value-field"] } }
],
"eventsSchema": [{ "key": "alerted", "label": "已置告警", "sourceUid": "m_btn" }],
"node": { "type": "group-panel", "uid": "m_root", "options": { "position":[0,0], "size":[460,320] }, "widgets": [ /* m_title / m_value(绑 value-field) / m_btn(switchState 目标=组内 m_value) … */ ] }
}
}
- propsSchema:把母版内某子节点的某 option 暴露为对外参数。
target.uid=母版原始子 uid,target.path=该节点options内路径(如["text"]、["value-field"])。实例props[key]缺省时回落default。数据输入 prop(type:"field")的值是三元组,指向消费工程连接(不随派生改写)。 - 母版内部引用:组内
switchState/cross-filter 等element.targetPath写母版子 uid(如["screen_main","m_value"]);派生时 widget 段自动改成实例uid::m_value,故各实例切的是各自的卡。 - 同步/解除:母版
version+1后,过期实例可「同步最新 / 保留本地 overrides」;「解除关联」把实例展开为普通group-panel(核心纯函数:isInstanceStale/syncInstanceNode/unlinkInstanceToGroup)。 - 作者流(纯函数骨架,UI 可直接调):
buildMasterFromNode(node,opts,body)选中分组→母版(按需打包依赖)、buildInstanceFromMaster(master,opts)母版→实例、bumpMasterVersion(master)升版。 - 完整可导入示例:
data/组件/组合/04-组合组件-母版与实例.vpproj.json(一母版派生「在线用户 / CPU」两实例,props 各异、组内按钮切各自的卡)。
5c. layout-section —— 视图容器(多视图 · 一次一显,对照 arcgis ExB Section)
- type:
layout-section默认尺寸:[640,400]默认名: "视图容器" - 结构:每个视图 = 一个
group-panel子面板(放在节点widgets: []里,面板name即视图名),面板统一position:[0,0]、size=容器尺寸;运行时只显示activeIndex对应的视图,其余隐藏不卸载。每视图背景/边框直接配在对应 group-panel 上(独立背景)。 sections选项(逗号/换行分隔标题)与子面板全量同步:改名/增删标题即同步面板;省事写法是sections与面板 name 一一对应。- 自定义触发器:
viewChanged(payload{index,label},任何来源切换都派发)、itemClicked。 - 自定义动作(任意组件可指向本容器执行):
openView(options 带view: 目标视图序号)、nextView/prevView(无参,循环切换)。
{
"type": "layout-section", "uid": "sec_main", "name": "经营视图容器",
"options": {
"position": [80,176], "size": [1080,640],
"sections": "经营总览,实时趋势,告警明细", // 视图标题(=子面板 name)
"default-active": 0, "transition": "slide-h", "transition-duration": 0.4,
"arrows-visible": true, "dots-visible": true, "dots-position": "bottom",
"auto-play": false // true 时配 play-interval/play-loop/hover-pause
},
"widgets": [
{ "type": "group-panel", "uid": "p_view1", "name": "经营总览",
"options": { "position": [0,0], "size": [1080,640], "background": true, "background-color": "linear-gradient(135deg,#0d1b33,#10264a)" },
"widgets": [ /* 该视图内容 */ ] },
{ "type": "group-panel", "uid": "p_view2", "name": "实时趋势", "options": { "position": [0,0], "size": [1080,640] }, "widgets": [] },
{ "type": "group-panel", "uid": "p_view3", "name": "告警明细", "options": { "position": [0,0], "size": [1080,640] }, "widgets": [] }
]
}
特有 options:
| name | 类型 | 默认 | 说明 |
|---|---|---|---|
sections |
section-list(item=视图) |
视图一,视图二,视图三 |
视图标题;值仍是逗号/换行分隔字符串(编辑器渲染为可视化视图列表管理器),与子面板全量同步 |
default-active |
number(min=0) |
0 |
初始视图序号(从 0 起) |
transition / transition-duration |
select / number(秒) |
fade / 0.3 |
切换过渡:none/fade/slide-h/slide-v |
lazy-render |
boolean |
false |
非激活视图首次激活才渲染(已访问保持挂载) |
arrows-visible + arrows-direction/arrows-size/arrows-offset/arrows-color/arrows-hover-color/arrows-bg |
— | 显示=true | 内置上一/下一箭头;play-loop=false 时到端禁用 |
dots-visible + dots-position/dots-size/dots-offset/dots-gap/dots-color/dots-active-color |
— | 显示=false | 内置指示点(位置 bottom/top/left/right),点击可切换 |
auto-play + play-interval/play-loop/hover-pause |
— | 关 | 自动播放(秒级间隔;悬停暂停;不循环则播到最后停止) |
无组件特有绑定字段(视图内容各自绑定)。
5d. nav-views —— 视图导航(链接视图容器,对照 arcgis ExB Views Navigation)
- type:
nav-views默认尺寸:[360,44]默认名: "视图导航" - 必须放在视图容器之外,经
link-section(element 引用)链接同工程任一layout-section;点击导航直接驱动容器切视图,容器被任何来源切换(内置箭头/自动播放/openView 动作)时导航高亮即时跟随(双向同步)。 - 自定义触发器:
viewChanged、itemClicked。 - 未链接容器时编辑态显示虚线占位,播放态渲染空。
{
"type": "nav-views", "uid": "nav_tabs", "name": "导航·选项卡",
"options": {
"position": [1220,208], "size": [620,48],
"link-section": [{ "targetPath": ["screen_main","sec_main"], "refType": "element" }], // 链接的视图容器
"nav-style": "tabs", // tabs 选项卡 / symbol 符号 / arrows 翻页按钮 / slider 滑块
"tab-variant": "underline", // 仅 tabs:default / underline / pill
"views-mode": "auto", // auto=全部视图;custom 时配 "views-subset": "0,2"
"item-align": "flex-start", "item-gap": 10
}
}
特有 options:
| name | 类型 | 默认 | 说明 |
|---|---|---|---|
link-section |
element(types=layout-section) |
[] |
链接的视图容器(必配;形态同动作 element 引用) |
nav-style |
select |
tabs |
样式族:tabs/symbol/arrows/slider |
tab-variant |
select |
default |
仅 tabs:default/underline(下划线)/pill(药丸) |
orientation / item-gap / item-align |
— | horizontal/6/center |
排布(slider 不分方向) |
views-mode + views-subset |
select+string |
auto/`` |
custom 时按序号子集显示(如 "0,2",保序去重) |
nav-font-size、nav-color/nav-active-color/nav-hover-color、nav-bg/nav-active-bg/nav-hover-bg、nav-radius、underline-width |
— | — | 三态文字/背景样式(underline 厚度仅下划线变体) |
symbol-shape/symbol-size/symbol-color/symbol-active-color/symbol-tooltip |
— | dot/10/—/—/true |
仅 symbol:dot/square/diamond,悬停 title 显示视图名 |
prev-text/next-text/show-page-no/arrows-loop/nav-disabled-color/nav-border-color |
— | 上一个/下一个/true/false/—/— | 仅 arrows:分页数字「2 / 5」;不循环则到端禁用 |
slider-thumb/slider-active-color/slider-track-color |
— | true/—/— | 仅 slider:拖动按比例换算视图序号 |
无组件特有绑定字段。完整可导入示例(容器+四种导航+动作+触发器):data/组件/入门/27-视图容器与视图导航-layout-section-nav-views.vpproj.json。
5e. window-instance —— 画面组件(页面内嵌画面)
- type:
window-instance默认尺寸:[400,300]默认名: "画面组件" - 用途:把工程里的某个画面(窗口,
project.windows[]顶层实体,见 envelope.md §2b)嵌入页面复用(画面改一处、各嵌入点同步)。区别于switchWindow动作(弹出式打开),这是静态嵌入。
| name | 类型 | 默认 | 说明 |
|---|---|---|---|
window |
window 引用 |
[] |
目标画面:[{ "targetPath": ["窗口uid"], "refType": "element" }] |
content-fit |
select |
stretch |
画面内容缩放:stretch/contain/cover/none |
show-window-bg |
boolean |
false |
是否显示画面自身背景 |
无组件特有绑定字段(画面内容各自绑定)。
VP-TSX 写法(本家族的非平凡映射)
本文所有 JSON 形态按 vptsx.md 机械映射规则转 TSX;本家族三处不走 children、走顶层保留 props:
// ① data-repeater:模板放 unitTemplate prop(值=一个 group-panel JSX 节点),不是 children
<data-repeater uid="w_rep" unit-size={[330, 170]} item-fields={bind('conn_x', 'tbl_x', '名称')}
unitTemplate={<group-panel uid="w_rep__unit" position={[0, 0]} size={[330, 170]}>…</group-panel>} />
// ② component-instance:masterId/masterVersion/props/overrides/master 全是顶层 props
<component-instance uid="inst_kpi" masterId="master_kpi" masterVersion={1}
props={{ title: '在线用户数' }} overrides={{ m_title: { 'text-font': { size: 20 } } }} master={masterDef} />
// ③ layout-section / group-tabs:视图=子面板,正常写 children(第 N 个 group-panel = 第 N 个视图)
<layout-section uid="sec_main" sections="总览,趋势" default-active={0}>
<group-panel uid="p_v1" name="总览" position={[0, 0]} size={[1080, 640]}>…</group-panel>
<group-panel uid="p_v2" name="趋势" position={[0, 0]} size={[1080, 640]} />
</layout-section>