当前位置: 首页 > news >正文

thingsboard 通过Entities hierarchy部件实现左边菜单点击,右边的表格按左边的分类型进行过滤筛选数据源

在 ThingsBoard 中,要让“Entities hierarchy”部件(左侧树形导航)与右侧的数据表格实现联动——即点击左侧某个节点后,右侧表格立刻按该节点对应的实体类型/层级进行过滤——需要把“数据源别名(Alias)+ 仪表板状态(State)+ 部件动作(Action)”三件事串起来做。下面给出可直接落地的完整步骤(PE/CE 通用)。


一、准备数据模型

  1. 给所有需要被分类的实体(设备/资产)打上统一的属性关系
    例如:

    • 属性 region: “华北”、“华南”

    • 关系 Contains:Asset → Device

  2. 在“资产”里建一个树形结构,如
    根 Asset → 区域 Asset → 子区域 Asset → 设备 Device。


二、做左侧“Entities hierarchy”部件

  1. 进入仪表板编辑模式 → 添加部件 → Cards → Entities hierarchy

  2. 数据源选“实体列表”或“资产查询”别名(见下一步),让它把整个资产树一次性展开。

  3. 在部件设置 → Actions(动作) → 添加动作

    • 触发源:On node selected

    • 动作类型:Update current dashboard state

    • 状态参数:

      entityId: ${entityId}
      entityType: ${entityType}
      entityName: ${entityName}

    保存。


三、做右侧“Entity table”部件

  1. 添加部件 → Cards → Entities table

  2. 关键在数据源别名

    • 打开“Entity aliases” → 新建别名

    • 类型选 Relations query(关系查询)

    • 起点:Dashboard state entity(即左侧点击的节点)

    • 方向:From

    • 关系类型:Contains

    • 最大层级:按需要(一般 1 层即可)

    • 目标实体过滤:可再按类型(Device/Asset)或属性过滤
      保存别名后回到表格部件,数据源选择刚创建的别名。

  3. 表格列配置完后保存。


四、测试

  1. 退出编辑,刷新仪表板。

  2. 在左侧树形列表点任意节点 → 右侧表格立即只显示该节点“包含”的设备或子资产。

  3. 若要多级联动(点区域 → 子区域 → 设备),把层级设成 2 或 3 即可。

ngOnInit(): void {this.ctx.$scope.entitiesTableWidget = this;this.settings = this.ctx.settings;this.widgetConfig = this.ctx.widgetConfig;this.subscription = this.ctx.defaultSubscription;this.initializeConfig();this.updateDatasources();this.ctx.updateWidgetParams();if (this.displayPagination) {this.widgetResize$ = new ResizeObserver(() => {this.ngZone.run(() => {const showHidePageSize = this.elementRef.nativeElement.offsetWidth < hidePageSizePixelValue;if (showHidePageSize !== this.hidePageSize) {this.hidePageSize = showHidePageSize;this.cd.markForCheck();}});});this.widgetResize$.observe(this.elementRef.nativeElement);}function decodeState(raw: string | null): any {if (!raw) return null;try {// 1️⃣ URL 解码const urlDecoded = decodeURIComponent(raw);// 2️⃣ BASE64 解码 → Latin-1 字符串const latin1 = atob(urlDecoded);// 3️⃣ Latin-1 → UTF-8const utf8 = new TextDecoder('utf-8').decode(Uint8Array.from(latin1, c => c.charCodeAt(0)));// 4️⃣ JSON 解析return JSON.parse(utf8);} catch (e) {console.error('state 解码失败', e);return null;}}var collectParamObj=null;// 监听路由参数变化(不刷新页面)this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe(params => {const state = decodeState(params['state']);//console.log('监听路由state=', JSON.stringify(state));if (!state) return ;if (state.length > 0 && state[0]?.params) {//const stateId = state[0].id;collectParamObj = state?.[0]?.params?.collect_param;if (collectParamObj) {// 把整个 collect_param 对象传进去this.handleUrlParamsChange(collectParamObj);}}});}private handleUrlParamsChange(collectParam:any) {// 根据参数重新加载数据或更新过滤条件// 示例:更新 pageLink 的过滤条件//this.pageLink.textSearch = params.entityId || null;//console.log('filter entityId=', params);if (collectParam){const entityId = collectParam?.entityId?.id;const entityType = collectParam?.entityId?.entityType;const entityName = collectParam?.entityName;const entityLabel = collectParam?.entityLabel;const param={entityType,entityId,entityName,entityLabel};// 延后一帧再执行setTimeout(() => this.updateData(param));}}private buildKeyFiltersFromQueryParams(params: Params): KeyFilter[] {const filters: KeyFilter[] = [];if (!params) {return filters;   // 返回空数组而不是 null,避免下游遍历出错}const entityType = params['entityType'];const entityId = params['entityId'];const entityName = params['entityName'];const entityLabel = params['entityLabel'];if (entityType =="ENTITY_VIEW"){if (entityLabel) {filters.push({key: {type: EntityKeyType.ENTITY_FIELD,key: 'label'},valueType: EntityKeyValueType.STRING,predicate: {type: FilterPredicateType.STRING,operation: StringOperation.EQUAL,value: {defaultValue: entityLabel,dynamicValue: null},ignoreCase: false // ✅ 添加这一行}});}}else if (entityType =="DEVICE"){if (entityName) {filters.push({key: {type: EntityKeyType.ENTITY_FIELD,key: 'name'},valueType: EntityKeyValueType.STRING,predicate: {type: FilterPredicateType.STRING,operation: StringOperation.EQUAL,value: {defaultValue: entityName,dynamicValue: null},ignoreCase: false // ✅ 添加这一行}});}}//console.log("filters",filters);return filters;}

http://www.dtcms.com/a/342144.html

相关文章:

  • 什么是Qoder?如何下载?如何体验?Qoder和其他 AI IDE 什么区别?
  • ZStack Zaku替代VMware Tanzu:六项对比、构建虚拟机+容器一体化架构
  • C# 编写一个XmlToDota的转换工具
  • 关于CentOS7无法使用使用
  • 在Java项目中去理解通用工具为什么能通用,以及如何写出类似的工具类
  • 实践题:智能化风控体系升级方案
  • 医疗器械注册证识别技术:实现从图像到结构化数据的智能转化,提升行业效率与准确性
  • 深度解析游戏引擎中的相机:视图矩阵
  • 【数据结构】深入解析选择排序与堆排序:从基础到高效实现的完全指南
  • 如何在Docker配置中启用实验性模式
  • 对实验室管理而言,LIMS系统究竟有无作用
  • 【STM32】HAL库中的实现(八):I2C通信(以 AT24C02 为例)
  • CentOS系统安装Git全攻略
  • 面试准备革命:面试汪 vs 传统方法,谁更胜一筹?
  • 「数据获取」《中国环境统计年鉴》(1998-2024)(获取方式看绑定的资源)
  • Linux命令大全-userdel命令
  • awk 命令的使用
  • 《P2700 逐个击破》
  • Design Compiler:逻辑库名与逻辑库文件名及其指定方式
  • 自学嵌入式第二十四天:数据结构(4)-栈
  • JetBrains Mono字体
  • Django ModelForm
  • 用 Python 写的自动化测试 WPF 程序的一个案例
  • Jmeter接口测试之文件上传
  • XXL-Job REST API 工具类完全解析:简化分布式任务调度集成
  • WebSocket和跨域问题
  • Android为ijkplayer设置音频发音类型usage
  • 如何用 SolveigMM Video Splitter 从视频中提取 AAC 音频
  • CMake3: CMake的嵌套使用与自定义库
  • Spring Event 企业级应用