示例集
四个覆盖不同能力面的完整例子。参考实现是仓库里的 modules/手机状态监测/,实际生产代码可以直接翻它。
最小模块
一个方法 + 一个页面,验证环境用。
hello/
├── manifest.json
├── main.py
└── ui/index.html{ "name": "hello", "version": "1.0.0", "entry": "main",
"description": "最小示例", "ui": "ui/index.html" }from jade_framework import JadeModule
class Hello(JadeModule):
def setup(self, ctx):
ctx.register_handler("hi", self._hi)
def _hi(self, who: str = "world") -> str:
return f"你好,{who}!"
MODULE = Hello<script src="/jade-bridge.js"></script>
<button onclick="alert(await jade.call('hello', 'hi', '模块'))">点我</button>轮询推送模块(无界面)
不写页面,纯后台轮询,数据全靠卡片和底部字段条展示。这个形态适合监控类模块。
import threading
from jade_framework import JadeModule
class Counter(JadeModule):
def setup(self, ctx):
ctx.register_handler("stats", self._stats)
def on_enable(self):
self._interval = max(1.0, float(self.ctx.config.get("interval", 2.0)))
self._stop = threading.Event()
self._thread = threading.Thread(target=self._loop, daemon=True)
self._thread.start()
self._last = {}
def on_disable(self):
self._stop.set()
self._thread.join(timeout=self._interval + 5)
self.ctx.set_card(None) # 清卡片
self.ctx.push_fields({}) # 清底部字段
def _loop(self):
while not self._stop.is_set():
try:
self._last = self._collect()
# 卡片:摘要;字段条:两三个关键指标
self.ctx.set_card(self._last)
self.ctx.push_fields({"轮询间隔": f"{self._interval}s",
"状态": "运行中"})
except Exception as e:
self.ctx.log.warning("轮询异常: %s", e)
self._stop.wait(self._interval) # 可被立即唤醒
def _collect(self) -> dict:
# 真实逻辑放这里:调外部命令、读接口……
return {"样本": 1}
def _stats(self) -> dict:
return self._last
MODULE = Counter要点:线程可停(Event + join)、循环异常不退出、停用时清干净推送。
带标题栏按钮的操作模块
在轮询的基础上给标题栏注册按钮,用户不用打开页面就能执行操作:
def on_enable(self):
...
self.ctx.push_titlebar({
"设备": self._count,
"_buttons": [
{"label": "刷新", "rpc": {"module": "counter", "method": "refresh"}},
{"label": "清零", "rpc": {"module": "counter", "method": "reset"}},
],
})
def on_disable(self):
...
self.ctx.push_titlebar({}) # 记得清按钮点击直接执行对应方法,和页面无关。所有 rpc.method 指向的方法都要在 setup 里注册过。
数据存储模块
用 KV 存状态、SQL 存记录、事务合并写:
def on_enable(self):
self.ctx.store.execute(
"CREATE TABLE IF NOT EXISTS records ("
" id INTEGER PRIMARY KEY, tag TEXT, ts REAL)")
# 上次运行的状态从 KV 恢复
self._last_tag = self.ctx.store.get("last_tag", "")
def _save(self, tag: str):
with self.ctx.store.transaction(): # KV 与 SQL 合并一次落盘
self.ctx.store.set("last_tag", tag)
self.ctx.store.execute("INSERT INTO records(tag, ts) VALUES(?, ?)",
(tag, time.time()))数据结构跨版本变更的迁移写法见 持久化存储。
Vue 3 界面模块
页面技术栈不限,Vue 完整写法(含离线打包 vendor 的注意事项)见 界面开发。React 的 UMD 写法也在那一页。
打包验证
每个示例交付前都跑一遍同一套流程:
python tools/encrypt_module.py encrypt --in modules/hello --out modules/hello.jmod --key-id default
python tools/encrypt_module.py inspect --in modules/hello.jmod然后把 .jmod 放进宿主 modules/ 重启,重复验证一遍。完整规则见 打包与交付。