4. 自定义Widget开发入门:Widget开发环境搭建、Widget结构(HTML/CSS/JS)、资源文件组织

自定义 Widget 是 ThingsBoard 数据可视化能力的核心扩展点。通过开发自定义 Widget,你可以将任何前端图表库、业务逻辑或交互模式嵌入仪表板。本章将系统讲解 Widget 的开发环境搭建、三文件结构(HTML/CSS/JS)以及资源文件的组织规范。

4.1 开发环境搭建

ThingsBoard 自定义 Widget 的开发不需要本地编译工具链,所有代码均在 ThingsBoard 平台内完成编辑和调试。但为了提升开发效率,建议按以下步骤准备环境:

步骤 操作 说明
1 登录 ThingsBoard 实例 使用租户管理员或系统管理员账号登录
2 进入 Widgets Library 主菜单 → Dashboards → Widgets Library
3 创建 Widget Bundle 点击 "+" → 输入 Bundle 名称(如 "MyCustomWidgets")
4 添加 Widget 在 Bundle 内点击 "+" → 选择 "Create new widget"
5 选择类型 根据需求选择 "Timeseries"、"Latest values"、"Control" 或 "Static"
6 进入编辑器 系统打开分栏编辑器:左侧为代码区,右侧为实时预览区
开发建议: 推荐使用 Chrome 或 Edge 浏览器进行开发,利用其开发者工具(F12)调试 Widget 的 CSS 样式和 JavaScript 逻辑。ThingsBoard 的 Widget 编辑器本身不提供断点调试功能,因此浏览器控制台是主要的调试手段。

4.2 Widget 结构:HTML / CSS / JS

每个自定义 Widget 由三个独立的代码文件组成,分别对应结构、样式和行为。ThingsBoard 在渲染 Widget 时,会按顺序加载这三个部分。

4.2.1 HTML 模板

HTML 部分定义 Widget 的 DOM 结构。ThingsBoard 使用 Angular 的模板语法,支持数据绑定和指令。所有数据变量通过 $scope 或 ctx(上下文对象)注入。

<!-- 示例:一个简单的时序值显示 Widget -->
<div class="my-widget-container">
  <div class="widget-header">
    <span>{{ctx.widgetTitle}}</span>
  </div>
  <div class="widget-body">
    <div class="value-display">
      <span class="value">{{ctx.value | number:2}}</span>
      <span class="unit">{{ctx.units}}</span>
    </div>
    <div class="timestamp">
      <span>{{ctx.date | date:'yyyy-MM-dd HH:mm:ss'}}</span>
    </div>
  </div>
</div>

4.2.2 CSS 样式

CSS 部分控制 Widget 的视觉呈现。ThingsBoard 会自动为每个 Widget 的根元素添加一个唯一的类名(如 .tb-widget-xxx),避免样式污染。建议所有样式都使用类选择器,并加上自定义前缀。

.my-widget-container {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 8px;
  padding: 16px;
  box-sizing: border-box;
}

.widget-header {
  color: #ffffff;
  font-size: 14px;
  font-weight: 600;
  margin-bottom: 12px;
  opacity: 0.9;
}

.value-display {
  flex: 1;
  display: flex;
  align-items: baseline;
  justify-content: center;
}

.value {
  font-size: 48px;
  font-weight: 700;
  color: #ffffff;
  line-height: 1;
}

.unit {
  font-size: 18px;
  color: rgba(255, 255, 255, 0.8);
  margin-left: 8px;
}

.timestamp {
  text-align: center;
  color: rgba(255, 255, 255, 0.6);
  font-size: 12px;
  margin-top: 8px;
}

4.2.3 JavaScript 逻辑

JavaScript 部分是 Widget 的核心,负责数据获取、处理、渲染和交互。ThingsBoard 提供丰富的 ctx(上下文)API,用于访问数据源、订阅遥测、设置参数等。

// 示例:Widget JavaScript 逻辑
self.onInit = function() {
  // 初始化:设置默认值、绑定事件
  ctx.$scope.widgetTitle = ctx.settings.widgetTitle || '默认标题';
  ctx.$scope.units = ctx.settings.units || '';
  ctx.$scope.value = 0;
  ctx.$scope.date = new Date();

  // 订阅数据更新
  ctx.subscription = ctx.telemetrySubscription.subscribe(
    function(data) {
      // data 包含最新的遥测数据
      if (data && data.length > 0) {
        var latest = data[data.length - 1];
        ctx.$scope.value = latest.value;
        ctx.$scope.date = latest.ts;
        ctx.$scope.$digest();  // 触发 Angular 脏检查更新视图
      }
    },
    function(error) {
      console.error('数据订阅失败:', error);
    }
  );
};

self.onDestroy = function() {
  // 清理:取消订阅、释放资源
  if (ctx.subscription) {
    ctx.subscription.unsubscribe();
  }
};

self.onResize = function() {
  // 响应式:当 Widget 尺寸变化时触发
  // 可用于重新绘制图表或调整布局
};
关键 API 速查:
  • ctx.telemetrySubscription — 订阅时序数据流
  • ctx.attributeSubscription — 订阅属性数据
  • ctx.settings — 读取 Widget 配置参数
  • ctx.datasources — 获取数据源配置
  • ctx.$scope.$digest() — 手动触发视图更新

4.3 资源文件组织

当 Widget 需要引入外部库(如 Chart.js、D3.js、ECharts)或自定义资源文件时,必须通过 ThingsBoard 的资源管理器进行管理。资源文件分为三类:

资源类型 文件扩展名 加载时机 使用场景
JavaScript 库 .js Widget 初始化前 引入第三方图表库、工具库
CSS 样式表 .css Widget 初始化前 引入第三方 UI 框架样式
JSON 配置 .json 按需加载 存储静态数据、映射表、主题配置

资源文件的组织遵循以下规范:

  1. 统一存放: 所有资源文件上传至 ThingsBoard 的 Resources 模块(主菜单 → Resources)。
  2. 命名规则: 使用语义化命名,如 echarts-5.4.3.min.js、my-widget-theme.json。
  3. 版本管理: 在文件名中包含版本号,避免缓存问题。
  4. 引用方式: 在 Widget 的 JavaScript 代码中使用 ctx.$injector 或 ctx.loadResource() 动态加载。
// 动态加载外部资源示例
self.onInit = function() {
  // 加载 ECharts 库
  ctx.loadResource('echarts-5.4.3.min.js').then(function() {
    // 库加载完成后初始化图表
    var chartDom = ctx.$container[0].querySelector('.chart-container');
    var myChart = echarts.init(chartDom);
    // ... 配置图表
  }).catch(function(error) {
    console.error('资源加载失败:', error);
  });

  // 加载 JSON 配置文件
  ctx.loadResource('my-widget-theme.json').then(function(config) {
    ctx.$scope.themeConfig = config;
    ctx.$scope.$digest();
  });
};
注意事项:
  • 资源文件大小建议控制在 2MB 以内,过大的文件会影响 Widget 加载速度。
  • 不要直接在 HTML 中使用 <script src="..."> 或 <link href="..."> 引用外部 URL,必须通过资源管理器上传。
  • 多个 Widget 共享同一资源时,只需上传一次,各 Widget 通过资源名称引用即可。

4.4 Widget 文件结构总览

下图展示了自定义 Widget 的完整文件结构及其在 ThingsBoard 平台中的组织关系:

自定义 Widget 文件结构总览 Widget Bundle 自定义 Widget HTML 模板 CSS 样式 JavaScript 逻辑 资源文件(Resources):JS 库 / CSS 样式表 / JSON 配置 仪表板实时预览 开发流程:创建 Bundle → 添加 Widget → 编写 HTML/CSS/JS → 上传资源 → 预览调试 所有代码在 ThingsBoard 平台内完成,无需本地开发环境

4.5 开发工作流总结

自定义 Widget 的开发遵循“编辑-预览-调试”的循环流程。以下是推荐的开发步骤:

  1. 规划: 确定 Widget 类型(时序/最新值/控制/静态)和数据需求。
  2. 搭建骨架: 在 Widget 编辑器中编写 HTML 结构,使用占位数据测试布局。
  3. 美化样式: 编写 CSS,确保 Widget 在不同尺寸下响应式适配。
  4. 注入逻辑: 编写 JavaScript,实现数据订阅、处理和交互。
  5. 加载资源: 上传并引用外部库或配置文件。
  6. 调试优化: 使用浏览器开发者工具检查错误、优化性能。
  7. 保存发布: 将 Widget 保存到 Bundle 中,供仪表板使用。
进阶提示: ThingsBoard 支持 Widget 的 版本历史 功能。每次保存时,系统会自动记录版本快照。你可以在 Widget 编辑器的“History”标签页中查看和回滚到任意历史版本,这为开发过程中的试错提供了安全保障。