面板 UI、tools 工具包与面板私有能力
本章覆盖三块「面板层为主」的能力:
wadPanelLayer.ui——UI 组件库、壳交互与onOpen / onClose生命周期;wadPanelLayer.storage——面板私有持久化;tools——三层同一实现的工具包。
本层 API 已拍平:直接
wadPanelLayer.ui.toast('…')、wadPanelLayer.storage.get('…'),不存在wadPanelLayer.panelLayer.ui.toast()。logicLayer/renderLayer只作为跨层代理出现。
本页你将学到
- UI 组件库与壳交互的全部方法;
onOpen / onClose(open/close 特判已移除);- toast 在三层的不同效果;
- tools 工具包的自检锚点。
UI 构建器
面板固定亮色主题(开发者只需写一套样式)。宿主在插件 <head> 最前面注入 /ui/plugin-base.css,不写任何 CSS 也有一致的控件外观。
ui.el(tag, attrs?, children?)
通用元素构建。
- 签名:
el(tag, attrs?, children?) → Element attrs里onClick / onInput / onChange特判为事件绑定;text / html / style / class快捷属性。
js
var U = wadPanelLayer.ui;
var tip = U.el('span', { text: '就绪', style: 'color:#0a9f5c' });ui.button(label, onClick, opts?)
按钮。
- 签名:
button(label, onClick, opts?) → Element opts.ghost / small / danger / block四个样式开关。
js
U.button('开始监听', function () { /* … */ }, { block: true });ui.card(opts) / ui.field(label, control) / ui.badge / ui.row / ui.col
容器与排版组件。
| 方法 | 签名 | 说明 |
|---|---|---|
card({title?, desc?, children?, actions?}) | 卡片容器 | |
field(label, control) | → Element | 带标签的字段行 |
badge(text, gray?) | → Element | 徽标 |
row(children?, cls?) | → Element | 横排 |
col(children?) | → Element | 竖排 |
js
U.card({ title: '请求监听', children: [U.row([U.badge('待捕获', true)])] });ui.input(opts) / ui.select(opts)
表单控件。
| 方法 | 签名 | 说明 |
|---|---|---|
input({type?, placeholder?, value?, onInput?, onChange?}) | → Element | 输入框 |
select({options, value?, onChange?}) | → Element | 下拉(options: string 或 {value,label}) |
js
var kw = U.input({ placeholder: 'URL 关键字' });
var method = U.select({ options: ['GET', 'POST'], value: 'GET' });ui.mount(element)
挂到面板 body,一行创建 UI。
js
U.mount(U.card({ title: '面板', children: [kw] }));壳交互
ui.toast(text)
宿主 Toast(面板发起)。
js
wadPanelLayer.ui.toast('已复制');ui.setTitle(text) / ui.close() / ui.setSize(w, h)
| 方法 | 说明 |
|---|---|
setTitle(text) | 改面板壳标题 |
close() | 关闭浮窗 |
setSize(w, h) | 调整面板尺寸(0 = 不限) |
onOpen(fn) / onClose(fn)
面板壳生命周期。on('open') / on('close') 特判已移除,统一走这两个钩子,都返回解绑函数。
js
// 面板层
wadPanelLayer.onOpen(function () {
// 面板打开(= ready 之后触发一次),适合做初始化/拉取数据
wadPanelLayer.log('panel open', wadPanelLayer.instanceId);
});
wadPanelLayer.onClose(function () {
// 面板关闭(pagehide),把该收的收掉
});storage.*(面板宿主持久化)
面板每次打开是全新 WebView,跨会话状态放这里。宿主落盘在 plugins/<id>/store/panel.json(插件级,不随 appid)。
js
(async () => {
const cfg = (await wadPanelLayer.storage.get('cfg')) || { kw: '', method: 'GET' };
await wadPanelLayer.storage.set('cfg', cfg);
const ks = await wadPanelLayer.storage.keys();
await wadPanelLayer.storage.remove('cfg');
await wadPanelLayer.storage.clear();
})();Web Storage(面板 WebView 内):
js
wadPanelLayer.localStorage.setItem('draft', bigText); // 面板重开仍在
wadPanelLayer.sessionStorage.setItem('scroll', '120'); // 面板关闭即消失toast 的三层语义
| 调用位置 | 效果 |
|---|---|
面板 wadPanelLayer.ui.toast | 宿主 Toast(postMessage 给面板壳) |
逻辑层 wadLogicLayer.ui.toast | 投递宿主 Toast |
渲染层 wadRenderLayer.ui.toast | 页面内 DOM Toast(黑底白字,2.6s 自动消失) |
跨层代理 wadPanelLayer.logicLayer.ui.toast / wadLogicLayer.panelLayer.ui.toast | 效果落在目标层(宿主 Toast) |
on / off / emit 与钩子糖
| API | 说明 |
|---|---|
on(name, fn) | 纯事件订阅(attach / pageChange / storageWrite…;'open'/'close' 特判已移除,用顶层 onOpen/onClose) |
off(name, fn) | 解绑 |
emit(name, data) | 本插件面板事件(多面板同步);面板 emit 还会派发到同插件逻辑层 |
onReady(fn) | 总线连上 |
onOpen(fn) / onClose(fn) | 面板打开/关闭钩子糖;onOpen 收 appid,onClose 无载荷 |
js
const off = wadPanelLayer.onOpen(function () { refresh(); });tools 工具包(三层同一实现)
wadLogicLayer.tools、wadRenderLayer.tools、wadPanelLayer.tools 是同一个实现产出的对象(三层各自实例、互不串扰),已知向量三层一致:
| 方法 | 说明 |
|---|---|
tools.md5(data) | MD5(输入 string/ArrayBuffer/TypedArray) |
tools.sha1(data) | SHA-1 |
tools.sha256(data) | SHA-256 |
tools.hmacSha256(key, data) | HMAC-SHA256(返回 hex) |
tools.base64.encode/decode/decodeText | Base64;decode 返回 Uint8Array,decodeText 直接出 UTF-8 字符串 |
tools.base64url.encode/decode | URL 安全 Base64(无填充;decode 兼容标准字符) |
tools.hex.encode/decode | hex ↔ Uint8Array |
tools.utf8.encode/decode | UTF-8 ↔ Uint8Array/字符串 |
tools.url.encode/decode | encodeURIComponent/decodeURIComponent 包装 |
tools.uuid() | RFC4122 v4(crypto.getRandomValues,降级 Math.random) |
tools.randomBytes(n) | Uint8Array(n) 随机字节 |
tools.crc32(data) | CRC-32(8 位 hex,带前导零) |
tools.deepClone(o) | JSON 深拷贝(失败返回原对象) |
tools.get(o, path, def?) | 'a.b.c' 路径取值(缺失返回 def) |
tools.set(o, path, value) | 路径写值(中间节点自动补对象) |
tools.typeOf(v) | 'null'/'undefined'/'arraybuffer'/'typedarray'/'array'/'string'/… |
tools.isArrayBuffer(v) | v instanceof ArrayBuffer |
tools.isPlainObject(v) | 普通对象判定(排除数组/TypedArray) |
已知向量(写死在文档里的自检锚点)
js
wadPanelLayer.tools.md5('abc'); // '900150983cd24fb0d6963f7d28e17f72'
wadPanelLayer.tools.sha1('abc'); // 'a9993e364706816aba3e25717850c26c9cd0d89d'
wadPanelLayer.tools.sha256('abc'); // 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'
wadPanelLayer.tools.crc32('abc'); // '352441c2'
wadPanelLayer.tools.base64.encode('Wad'); // 'V2Fk'
wadPanelLayer.tools.base64.decodeText('V2Fk'); // 'Wad'
wadPanelLayer.tools.hex.encode('Wad'); // '576164'工具包专题见 tools。
跨层代理速记
| 代理 | 可用成员 |
|---|---|
wadPanelLayer.logicLayer.call / eval / page / storage / ui.toast | 调逻辑层(storage = 逻辑层 wx 存储) |
wadPanelLayer.renderLayer.eval / screenshot / domSnapshot / targets / page / ui.toast | 控渲染层 |
js
(async () => {
await wadPanelLayer.logicLayer.call('add', 1, 2); // 可变参
const p = await wadPanelLayer.logicLayer.page(); // {route, options}
const shot = await wadPanelLayer.renderLayer.screenshot(); // {ok, png_b64, target}
})();示例:一个完整的交互面板
html
<body>
<script>
wadPanelLayer.ready.then(function () {
var U = wadPanelLayer.ui;
var kw = U.input({ placeholder: 'URL 关键字' });
var method = U.select({ options: ['GET', 'POST'], value: 'GET' });
var status = U.badge('待捕获', true);
var startBtn = U.button('开始监听', function () {
// storage 是 Promise:回调里务必 .then / await
wadPanelLayer.storage.set('watch', { kw: kw.value, method: method.value })
.then(function () {
status.textContent = '监听中';
startWatch();
});
}, { block: true });
U.mount(U.card({
title: '请求监听',
desc: '匹配的请求会实时 Toast',
children: [U.field('URL 包含', kw), U.field('方法', method), U.row([status])],
actions: [startBtn]
}));
// 安装即恢复上一次配置
wadPanelLayer.storage.get('watch').then(function (cfg) {
if (cfg && cfg.kw) kw.value = cfg.kw;
if (cfg && cfg.method) method.value = cfg.method;
if (cfg && cfg.kw) startWatch();
});
function startWatch() {
wadPanelLayer.capture.watch(function (e) {
wadPanelLayer.storage.get('watch').then(function (cfg) {
if (cfg && cfg.kw && e.url.indexOf(cfg.kw) >= 0) {
U.toast(e.method + ' ' + e.status + ' ' + e.url.slice(0, 48));
}
});
});
}
});
</script>
</body>示例:表格展示数据
js
var U = wadPanelLayer.ui;
function pre(text) {
var p = U.el('pre', { style: 'max-height:200px;overflow:auto' });
p.textContent = text;
return p;
}
wadPanelLayer.capture.records(20).then(function (list) {
U.mount(U.card({
title: '最近请求',
children: [pre(list.map(function (r) {
return r.status + ' ' + r.method + ' ' + r.url;
}).join('\n'))]
}));
});