Skip to content

第 4 章:面板、按钮与定时器

前面几章的插件都偏“后台逻辑”。这一章开始讲插件怎样和用户交互。

最常见的三个入口是:

  • quickActions
  • onUserAction(...)
  • onTimer(...)

以及一条 UI 数据通道:

  • ctx.ui.send(...)

先分清三种用途

quickActions

适合“让用户点一个按钮触发插件行为”。

例如:

  • 手动清空缓存
  • 手动触发一次外部同步
  • 手动开始一次临时扫描

onTimer(...)

适合“让插件自己定时做事”。

例如:

  • 每 30 秒检查一次状态
  • 每 5 分钟推送一次统计
  • 一段时间后自动停止某个行为

ctx.ui.send(...)

适合“让插件把数据推给前端面板”。

例如:

  • 当前统计
  • 最近事件列表
  • 外部接口状态

一个最小 quickAction 例子

ts
import type { PluginDefinition } from '@tx5dr/plugin-api';

const plugin: PluginDefinition = {
  name: 'button-demo',
  version: '1.0.0',
  type: 'utility',
  quickActions: [
    { id: 'ping', label: 'pingAction' },
  ],
  hooks: {
    onUserAction(actionId, _payload, ctx) {
      if (actionId !== 'ping') {
        return;
      }
      ctx.log.info('plugin quick action clicked', {
        actionId,
        operatorId: ctx.operator.id,
      });
    },
  },
};

export default plugin;

这类场景的重点是:用户显式点击,插件再响应。这和自动起呼 proposal 完全是两种设计语义。

一个最小 timer 例子

ts
import type { PluginDefinition } from '@tx5dr/plugin-api';

const plugin: PluginDefinition = {
  name: 'timer-demo',
  version: '1.0.0',
  type: 'utility',
  onLoad(ctx) {
    ctx.timers.setInterval('heartbeat', 30_000);
  },
  onUnload(ctx) {
    ctx.timers.clear('heartbeat');
  },
  hooks: {
    onTimer(timerId, ctx) {
      if (timerId !== 'heartbeat') {
        return;
      }
      ctx.log.info('heartbeat tick', {
        operatorId: ctx.operator.id,
      });
    },
  },
};

export default plugin;

heartbeat-demo 就是这一模式的参考实现。

一个最小面板数据推送例子

ts
import type { PluginDefinition } from '@tx5dr/plugin-api';

const plugin: PluginDefinition = {
  name: 'panel-demo',
  version: '1.0.0',
  type: 'utility',
  panels: [
    { id: 'stats', title: 'statsPanel', component: 'key-value', width: 'full' },
  ],
  hooks: {
    onDecode(messages, ctx) {
      ctx.ui.send('stats', {
        decodeCount: messages.length,
        timestamp: Date.now(),
      });
    },
  },
};

export default plugin;

这样前端面板就可以订阅这份数据并渲染。

这里顺便出现了一个新字段:width

  • width: 'half'(默认)表示沿用宿主的常规紧凑布局
  • width: 'full' 表示“这个面板更重要,希望给它更宽的展示空间”
  • 当前操作员卡片 host 会把 full 解释为桌面端跨整行显示;但 automation 之类的 host 可以按自己的布局策略忽略它,所以它是声明式 hint,而不是绝对布局命令

两个内置案例

heartbeat-demo

它展示的是:

  • 生命周期
  • timer
  • button quickAction

qso-session-inspector

它展示的是:

  • 广播 Hook 监听
  • operator-scope 存储
  • 面板数据推送

什么时候不要用 onUserAction(...)

onUserAction(...) 只适合插件自己的交互语义。像这些系统核心控制,不建议绕进去:

  • 操作员自动化状态切换
  • 策略运行时内部状态推进
  • 标准起呼 / 发射控制

这些应该继续走各自明确的宿主机制。

需要更灵活的界面?

本章介绍的结构化面板(key-valuetablelogchart)适合展示简单数据。但如果你需要:

  • 自定义的表单输入
  • 交互式图表
  • 多步向导
  • 完全定制的布局

可以使用 iframe 面板 —— 插件提供自己的 HTML/CSS/JS 页面,由宿主在沙箱 iframe 中渲染。iframe 面板支持双向通信:tx5dr.invoke() 向服务端请求数据,tx5dr.onPush() 接收服务端推送。

详见 第 6 章:自定义 UI 与 iframe 面板

这一章你应该学会什么

  • 按钮是 quickActions + onUserAction(...)
  • 定时逻辑是 timers + onTimer(...)
  • 面板数据是 ctx.ui.send(...)
  • 需要自定义交互时,用 iframe 面板(第 6 章)

下一章进入最复杂、也最强大的部分:自己写一个 strategy 插件。

TX-5DR 官网、文档与下载入口