首页/新闻资讯/正文详情

Ajenti Dashboard Widget 开发指南:从 Widget 接口到 Angular 模板的完整实战

发布时间:2026/9/26 17:21:46 来源:云帆数科 栏目:资讯中心
Ajenti Dashboard Widget 开发指南:从 Widget 接口到 Angular 模板的完整实战
后端运维【免费下载链接】ajentiAjenti Core and stock plugins项目地址https://gitcode.com/gh_mirrors/aj/ajenti点击查看免费下载本文是一份面向 Ajenti 插件开发者的实战指南讲解如何通过扩展aj.plugins.dashboard.api.Widget抽象类为 Ajenti 控制面板Dashboard提供可配置、可动态刷新的自定义 Widget。读完本文你将掌握 Widget 的类定义、模板与配置对话框的编写、Angular 控制器的数据流以及 Ajenti 内置 Widget 的实现范式可直接动手写出自己的第一个面板组件。说明本文基于当前仓库Ajenti Core and stock plugins实际源码撰写。原文档docs/source/dev/dash-widgets.rst中的示例代码来自官方 demo 插件仓库demo_5_widget本仓库不包含该示例插件但仓库内置的 5 个 Dashboard 组件提供了完全对等的参考实现可对照阅读。Ajenti Dashboard 面板用户在此添加、配置并查看各类 Widget 的实时数值一、Dashboard 插件与 Widget 接口概览Ajenti 的 Dashboard 插件位于 plugins/dashboard其职责在 plugins/dashboard/README.md 中有明确说明This plugin manages and displays all available widgets registered in the plugins. It defines a widget interfaceWidgetin order to facilitate the definition and integration of a new widget.即Dashboard 插件负责统一管理并展示所有插件注册的 Widget并定义了Widget接口以简化新 Widget 的定义与集成。任何插件包括第三方插件都可以注册自己的 Widget 上架到 Dashboard 页面用户可以在面板中自由添加、配置并实时查看数据。Widget 的抽象基类定义在 plugins/dashboard/api.pyfrom jadi import interface interface class Widget(): Base interface for dashboard widgets. id None name None Display name template None Angular view template URL config_template None Configuration dialog template URL def __init__(self, context): self.context context def get_value(self, config): Override this to return the widget value for the given config dict. raise NotImplementedError可见Widget是一个通过interface声明的 jadi 接口需要开发者实现的核心部分有三块类属性id、name、template、config_template与方法__init__、get_value。get_value的默认实现直接抛出NotImplementedError因此任何自定义 Widget 都必须覆写它。二、定义你的第一个 Widget类结构与注册插件通过继承Widget抽象类并提供给 jadi 容器来注册 Widget。原文档给出了完整示例其结构如下component(Widget) class RandomWidget(Widget): id random # display name name Random # template of the widget template /demo_5_widget:resources/partial/widget.html # template of the configuration dialog config_template /demo_5_widget:resources/partial/widget.config.html def __init__(self, context): Widget.__init__(self, context) def get_value(self, config): # generate value based on widgets config if bytes not in config: return Not configured return os.urandom(int(config[bytes])).encode(hex)其中几个关键点需要展开说明component(Widget)来自jadi的装饰器将RandomWidget注册为Widget接口的实现。Dashboard 插件启动时会通过Widget.all(self.context)枚举全部注册项见 plugins/dashboard/views.py因此无需任何额外的注册表或配置文件声明即注册。idWidget 的唯一标识用于前端区分不同类型的 Widget。必须唯一且在 API 请求中作为typeId传递见下文第三节。name显示名称将出现在 Dashboard 的添加菜单与 Widget 头部。templateWidget 主体视图的 Angular 模板 URL采用/{插件名}:resources/...的资源定位语法。config_template配置对话框的模板 URL。该属性是可选的如果未提供则用户添加该 Widget 时不会弹出配置界面内置 Widget 大多未提供配置模板见第六节。__init__(self, context)构造时接收当前请求上下文context必须显式调用父类构造器Widget.__init__(self, context)。注意get_value(config)的入参config这是一个Python dict由用户在配置对话框中填写、随每次刷新请求提交见第四节。示例中RandomWidget根据配置中的bytes字段生成随机字节串若配置中不含bytes则返回字符串Not configured——这展示了配置驱动取值的典型模式。三、数据获取契约get_value 与 HTTP API 的调用链get_value(config)是 Widget 唯一必须实现的方法Dashboard 前端会周期性调用它来刷新数值。其底层调用链可以从 plugins/dashboard/views.py 中完整还原GET /api/dashboard/widgets返回全部可用 Widget 的元数据id、name、template、config_template前端据此渲染添加 Widget菜单return [ { id: w.id, name: w.name, template: w.template, config_template: w.config_template, } for w in self.widgets.values() ]POST /api/dashboard/widgets-values接收前端提交的请求列表每项含id、typeId、config对每个请求调用对应 Widget 的get_value(rq[config])并返回结果data http_context.json_body() return [ { id: rq[id], data: self.widgets[rq[typeId]].get_value(rq[config]), } for rq in data if rq[typeId] in self.widgets ]从这段实现可以提炼出两条重要的设计约束Widget 必须无状态Handler.__init__中self.widgets {x.id: x for x in Widget.all(self.context)}表明每个类型的 Widget只实例化一次服务所有同类型 Widget 的请求原文档也强调If user creates multiple widgets of same type, a single instance will be created to service their requests。因此你的 Widget 类不应保留任何跨请求状态所有可变数据都应存放在config中或每次在get_value内重新获取。get_value的返回值自由但需自洽它可以是标量如 uptime 的秒数、列表如 cpu 的每核百分比、或 dict如 memory 的used/free/total只要与你提供的模板中的渲染逻辑一致即可。四、模板编写CSS 类与 Angular 控制器Widget 主体模板template使用 AngularJS 编写。原文档给出了标准样式的模板骨架并指出有一些现成的 CSS 类可供使用以保持标准外观div ng:controllerDemo5WidgetController div classwidget-header Random /div div classwidget-value {{value || Unknown}} /div /div可用的标准 CSS 类来自 Dashboard 插件的前端样式包括CSS 类用途widget-headerWidget 标题栏widget-valueWidget 主体数值区域widget-container/widget等容器布局由 Dashboard 页面统一提供模板对应的 Angular 控制器需要监听widget-update事件以接收后端推送的数据angular.module(ajenti.demo5).controller Demo5WidgetController, ($scope) - # $scope.widget is our widget descriptor here $scope.$on widget-update, ($event, id, data) - if id ! $scope.widget.id return $scope.value data这里的事件契约非常关键$scope.widget当前 Widget 的描述对象含id等元数据由 Dashboard 框架注入widget-update框架周期性广播的事件回调收到(id, data)控制器必须按id过滤只有id与自身$scope.widget.id一致时才更新$scope.value。因为页面上可能同时存在多个不同类型的 Widget它们共享同一事件总线。五、配置对话框config_template 与初始化流程若你的 Widget 需要用户配置如RandomWidget的bytes参数则需提供config_template并配套实现配置控制器angular.module(ajenti.demo5).controller Demo5WidgetConfigController, ($scope) - # $scope.configuredWidget is our widget descriptor here # some defaults $scope.configuredWidget.config.bytes ? 4配置对话框的控制器的关键点是$scope.configuredWidget正在被配置的 Widget 描述对象其config属性是一个 dict直接与后端get_value收到的config参数对应在控制器中可为未设置的配置项提供默认值如$scope.configuredWidget.config.bytes ? 4CoffeeScript 的?仅在值为空时赋值。原文档还明确了整个 Widget 的生命周期起始流程Initially, dashboard will create your widget with an empty ({}) config and show the configuration dialog you provided.即当用户在 Dashboard 添加一个 Widget 时框架会以空配置{}创建它并立即弹出你提供的配置对话框。这意味着你的get_value必须能够安全处理config缺失某个键甚至为空 dict的情况——RandomWidget中if bytes not in config: return Not configured正是为此设计的健壮写法。六、内置 Widget 实现范式直接可读的参考源码如果你希望看到一个真实、完整的 Widget 实现仓库内置的 5 个 Widget 是最佳范本它们全部位于 plugins/dashboard/widgets均遵循继承Widgetcomponent注册 覆写get_value的统一范式1. 主机名 Widgethostname.py——最简单的标量型 Widgetcomponent(Widget) class HostnameWidget(Widget): id hostname name _(Hostname) template /dashboard:resources/partial/widgets/hostname.html def __init__(self, context): Widget.__init__(self, context) def get_value(self, config): return platform.node()2. 运行时长 Widgetuptime.py——同样返回标量def get_value(self, config): return time.time() - psutil.boot_time()3. CPU 使用率 Widgetcpu.py——返回列表每核百分比归一化到 0~1def get_value(self, config): return [x / 100.0 for x in psutil.cpu_percent(interval0, percpuTrue)]4. 内存使用率 Widgetmemory.py——返回 dict便于模板分别渲染多项数据def get_value(self, config): v psutil.virtual_memory() return { used: v.total - v.available, free: v.available, total: v.total }5. 负载均值 Widgetloadavg.py——仓库中唯一消费config的内置 Widget是理解配置驱动取值的最佳案例def get_value(self, config): k 1.0 if config and config.get(divide, False): k / multiprocessing.cpu_count() if os.path.exists(/proc/loadavg): return [float(open(/proc/loadavg).read().split()[x]) * k for x in range(3)] tokens subprocess.check_output([uptime]).decode().split() return [float(x.strip(,).replace(,, .)) * k for x in tokens[-3:]]它通过config.get(divide, False)决定是否将负载均值除以 CPU 核数同时兼容/proc/loadavg与uptime两种取值来源。注意它对config的防御性写法if config and config.get(divide, False)——空 dict 也能安全通过。七、前端资源注册plugin.yml 中的声明Widget 的模板、控制器与依赖服务需要通过插件的 plugin.yml 声明为资源Ajenti 前端才会加载它们。以 Dashboard 插件自身的配置为模板name: dashboard version: 0.42 title: Dashboard icon: bar-chart dependencies: - !PluginDependency { plugin_name: core } resources: - resources/js/module.es - resources/js/routing.es - resources/js/controllers/index.controller.es - resources/js/controllers/cpuWidget.controller.es - resources/js/controllers/hostnameWidget.controller.es - resources/js/controllers/loadAverageWidget.controller.es - resources/js/controllers/memoryWidget.controller.es - resources/js/controllers/uptimeWidget.controller.es - resources/js/services/dashboard.service.es - resources/css/styles.less - resources/partial/index.html - resources/partial/widget.html - resources/partial/widgets/cpu.html - resources/partial/widgets/hostname.html - resources/partial/widgets/loadavg.html - resources/partial/widgets/memory.html - resources/partial/widgets/uptime.html - ng:ajenti.dashboard从中可以总结出编写自定义 Widget 插件时需遵循的资源声明规范每个 Widget 的模板文件resources/partial/widgets/*.html都要列入resources这解释了template属性中/dashboard:resources/partial/widgets/cpu.html路径的由来每个 Widget 的Angular 控制器resources/js/controllers/*Widget.controller.es同样需要注册通过ng:ajenti.dashboard声明依赖的 Angular 模块如果你的 Widget 使用独立模块则在template/config_template中引用对应插件的模块路径如示例中的/demo_5_widget:resources/...。八、开发 Checklist编写一个可用的 Dashboard Widget综合原文档与仓库源码开发一个自定义 Widget 的完整步骤为继承并注册创建类继承aj.plugins.dashboard.api.Widget用component(Widget)注册定义唯一的id与显示用的name实现get_value(config)从config读取配置、获取实时数据并返回标量/列表/dict 均可务必对空配置{}与缺失键做防御不得在类中保留跨请求状态编写主体模板使用widget-header、widget-value等标准 CSS 类通过ng:controller挂载控制器将template指向/{插件名}:resources/partial/xxx.html编写控制器在widget-update事件中按id过滤后更新$scope.value可选提供配置能力编写config_template对应的配置对话框控制器在$scope.configuredWidget.config上设置默认值声明资源在插件的plugin.yml中注册模板与控制器资源在面板中验证进入/view/dashboard通过侧边栏的 Dashboard 入口见 plugins/dashboard/main.py 的SidebarItemProvider添加你的 Widget 并观察周期性刷新是否正常。完成上述步骤后你的 Widget 就会与内置的 CPU、内存、负载等组件一样出现在 Dashboard 的添加菜单中供用户随时添加与配置。赞分享后端运维【免费下载链接】ajentiAjenti Core and stock plugins项目地址https://gitcode.com/gh_mirrors/aj/ajenti点击查看免费下载相关推荐Ajenti Dashboard Widget API 深度解析从内置组件到自定义插件的完整开发指南Ajenti Dashboard Widget API 深度解析从内置组件到自定义插件的完整开发指南 Dashboard 是 Ajenti 管理面板默认的落地后端运维gridstack.js 官方 Angular 封装指南从动态 Widget 到 ngFor 模板的双轨实战gridstack.js 官方 Angular 封装指南从动态 Widget 到 ngFor 模板的双轨实战 Gridstack.js 是快速构建交互式仪表盘前端UI组件Seelen UI Widget 开发指南从 metadata.yml 到运行时 JS API 的完整实战Seelen UI Widget 开发指南从 metadata.yml 到运行时 JS API 的完整实战 Seelen UI 的 Widget部件本质上桌面应用前端插件系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Claude Code 快捷键全图谱:从操作模型到效率提升的完整指南
Claude Code 快捷键全图谱:从操作模型到效率提升的完整指南

用 Claude Code 大半年,我最大的体会是:这个工具的上限不是模型决定的,而是你的键盘决定的。AI 写代码再快,如果你的手一直在鼠标和键盘之间来回倒腾,效率天花板很快就会出现。今天整理一份我自己每天都在用的 Claude … · 2026/9/26 17:21:46

Windows彻底卸载Node.js指南:清理残留、注册表与Path防坑手册
Windows彻底卸载Node.js指南:清理残留、注册表与Path防坑手册

如果你曾经在 Windows 的控制面板里点过 “Node.js” 的卸载,然后兴致勃勃地去装一个新版本,结果安装程序给你弹出一句“已安装”或者装完后发现旧版的全局命令还在,那么恭喜你,你已经踩到了 Windows 卸载 Node.js 最经典的坑——… · 2026/9/26 17:21:30

餐饮管理系统双端联调实战:从Spring Boot后端到Android客户端的关键避坑指南
餐饮管理系统双端联调实战:从Spring Boot后端到Android客户端的关键避坑指南

简介:溢香园餐饮管理系统是一套面向高校毕业设计与课程设计的完整项目,兼顾Web管理端与Android客户端,适合需要完成餐饮类选题的学生参考。压缩包共960个文件,约77MB,文件类型涵盖125个Java源码、37个JSP页面、89个XML… · 2026/9/26 17:21:23

前任skill安装教程:用TaoToken统一Key跑通node与git依赖链
前任skill安装教程:用TaoToken统一Key跑通node与git依赖链

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 18:27:53

公众号无限回调登录:用中转服务突破网页授权域名限制
公众号无限回调登录:用中转服务突破网页授权域名限制

简介:2024最新公众号无限回调登录接口源码,面向未完成ICP备案却需接入公众号登录能力的开发者,解决正规接口申请门槛高、回调受限的痛点。资源共7个文件、约7.77MB,内含PHP源码、MySQL数据库备份(gz)、HTML… · 2026/9/26 18:27:53

金融技术服务:概念、原理与典型应用场景解析
金融技术服务:概念、原理与典型应用场景解析

我无法根据当前输入生成符合要求的博文。原因在于:您提供的输入内容中,项目标题仅为“financial-services”这一宽泛英文词组,且未提供任何项目正文、关键词列表、摘要描述等必要信息。同时,相关热搜词、网络热词及搜索内容部分全… · 2026/9/26 18:27:53

Postman官方安装与企业级安全配置指南
Postman官方安装与企业级安全配置指南

我不能提供任何关于软件破解、绕过授权机制、汉化包分发或规避正版验证的技术内容。这不仅违反《计算机软件保护条例》及《中华人民共和国著作权法》,也违背我作为专业内容创作者的职业底线与平台合规要求。 Postman 是一款广受开发者信赖的 API 开发协作工具&… · 2026/9/26 18:27:41

Arthas命令详解:不重启诊断Java线上问题与性能瓶颈
Arthas命令详解:不重启诊断Java线上问题与性能瓶颈

简介:Arthas 3.7.2 是一款开源 Java 诊断工具的生产级资源包,面向需要在线定位问题、分析性能瓶颈的 Java 后端开发者,也适合用于毕业设计论文中的运行时行为研究、计算机案例解析及系统软件二次开发。包内收录完整源码、官方文档与辅助脚本&… · 2026/9/26 18:27:41

Range-Only EKF定位与SLAM实战:原理、ROS节点与调参避坑
Range-Only EKF定位与SLAM实战:原理、ROS节点与调参避坑

简介:这是一套基于ROS的Range-Only无线传感器网络扩展卡尔曼滤波定位与SLAM学习项目,面向机器人导航、传感器融合方向的课程设计、毕业设计及研究者。资源围绕TurtleBot3仿真平台组织,涵盖定位与建图所需完整源码和项目说明,可帮助… · 2026/9/26 18:27:41

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码