唯杰地图 logo唯杰地图
VJMAP
EN

容器与复用组件

vjvp/references/widgets/containers.md 19.7 KB251 行
原文

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>