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 | 进入编辑器 | 系统打开分栏编辑器:左侧为代码区,右侧为实时预览区 |
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 尺寸变化时触发
// 可用于重新绘制图表或调整布局
};
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 | 按需加载 | 存储静态数据、映射表、主题配置 |
资源文件的组织遵循以下规范:
- 统一存放: 所有资源文件上传至 ThingsBoard 的 Resources 模块(主菜单 → Resources)。
- 命名规则: 使用语义化命名,如
echarts-5.4.3.min.js、my-widget-theme.json。 - 版本管理: 在文件名中包含版本号,避免缓存问题。
- 引用方式: 在 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 平台中的组织关系:
4.5 开发工作流总结
自定义 Widget 的开发遵循“编辑-预览-调试”的循环流程。以下是推荐的开发步骤:
- 规划: 确定 Widget 类型(时序/最新值/控制/静态)和数据需求。
- 搭建骨架: 在 Widget 编辑器中编写 HTML 结构,使用占位数据测试布局。
- 美化样式: 编写 CSS,确保 Widget 在不同尺寸下响应式适配。
- 注入逻辑: 编写 JavaScript,实现数据订阅、处理和交互。
- 加载资源: 上传并引用外部库或配置文件。
- 调试优化: 使用浏览器开发者工具检查错误、优化性能。
- 保存发布: 将 Widget 保存到 Bundle 中,供仪表板使用。