dsh-remote-web 0.7.6-beta.9 → 0.7.9-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +138 -18
- package/lib/index.js +555 -63
- package/package.json +1 -1
- package/runtime/clients/dsh-remote/dsh-bridge.mjs +332 -19
- package/runtime/clients/dsh-remote/src/channel-ref.mjs +69 -0
- package/runtime/clients/dsh-remote/wechat-channel.mjs +378 -6
- package/runtime/clients/dsh-remote/wechat-runtime.mjs +408 -48
- package/runtime/dsh-setup.mjs +297 -51
package/lib/client.js
CHANGED
|
@@ -1266,6 +1266,7 @@ window.__ModuleLoader__.load({
|
|
|
1266
1266
|
* 之所以是模块级的:拉取发生在 apply() 里(组件还没挂载),而消费方有两个
|
|
1267
1267
|
* (设置面板的卡片 / shell.overlay 上的弹窗),模块级 store 才能让两边看到同一份数据。
|
|
1268
1268
|
*/
|
|
1269
|
+
var annRetryCount = 0; // 公告首次拉取瞬态失败的有界重试计数(成功即清零)
|
|
1269
1270
|
var annState = {
|
|
1270
1271
|
ok: false, // 拉取成功且结构是我们认识的那一种(老服务端 404 / 结构不符 → 一直是 false)
|
|
1271
1272
|
bubble: false, // bubble_enabled === true 才置真(字段缺失一律当 false = fail closed)
|
|
@@ -1428,6 +1429,7 @@ window.__ModuleLoader__.load({
|
|
|
1428
1429
|
var norm = annNormalize(b);
|
|
1429
1430
|
if (!norm) { annState.ok = false; annState.bubble = false; annState.items = []; annState.unread = 0; annEmit(); return false; }
|
|
1430
1431
|
annState.ok = true;
|
|
1432
|
+
annRetryCount = 0;
|
|
1431
1433
|
annState.bubble = norm.bubble;
|
|
1432
1434
|
annState.items = norm.items;
|
|
1433
1435
|
annState.unread = norm.unread;
|
|
@@ -1436,13 +1438,24 @@ window.__ModuleLoader__.load({
|
|
|
1436
1438
|
}
|
|
1437
1439
|
annEmit();
|
|
1438
1440
|
return true;
|
|
1439
|
-
}).catch(function () {
|
|
1441
|
+
}).catch(function (err) {
|
|
1440
1442
|
// 404(老服务端)/ 不可达 / 超时:fail closed —— 不显示入口、不弹窗,**绝不抛**
|
|
1441
1443
|
annState.ok = false;
|
|
1442
1444
|
annState.bubble = false;
|
|
1443
1445
|
annState.items = [];
|
|
1444
1446
|
annState.unread = 0;
|
|
1445
1447
|
annEmit();
|
|
1448
|
+
// 2026-10-25(用户反馈「公告板块没了」):dsh 重启后页面常常比插件宿主路由先就绪,
|
|
1449
|
+
// 首次拉取失败一次,整页(sessionStorage 一次性标记)就再也没有公告入口。
|
|
1450
|
+
// 这里只对**瞬态失败**做有界重试(最多 2 次、间隔 1.5s/3s);404 明确是老服务端,不重试。
|
|
1451
|
+
// 注意:这只发生在**失败后**,成功路径仍然严格"每次页面加载只拉一次"。
|
|
1452
|
+
var st = Number(err && (err.status || err.statusCode));
|
|
1453
|
+
if (st !== 404 && annRetryCount < 2) {
|
|
1454
|
+
annRetryCount += 1;
|
|
1455
|
+
try {
|
|
1456
|
+
setTimeout(function () { annFetch({ openPopup: false }); }, 1500 * annRetryCount);
|
|
1457
|
+
} catch (e2) { /* 忽略 */ }
|
|
1458
|
+
}
|
|
1446
1459
|
return false;
|
|
1447
1460
|
});
|
|
1448
1461
|
}
|
|
@@ -1623,6 +1636,33 @@ window.__ModuleLoader__.load({
|
|
|
1623
1636
|
try { clearTimeout(t); } catch (e) { /* 忽略 */ }
|
|
1624
1637
|
}
|
|
1625
1638
|
|
|
1639
|
+
// ── 设备超额锁定(契约 docs/device-overquota-lock-contract.md §8)─────────────
|
|
1640
|
+
/**
|
|
1641
|
+
* 「设备被超额锁定」的统一文案(远程访问红条 / 后台服务卡共用,避免两处各写一套漂移)。
|
|
1642
|
+
* 契约 §8.1 要求同时讲清三件事:① 当前套餐可同时用 N 台;② 两条出路(升级 Pro Max / 解绑);
|
|
1643
|
+
* ③ 解绑或升级后**本机会自动解锁**(不是"点重试直到成功",也不能暗示无限重试)。
|
|
1644
|
+
* @param {number|null} limit 服务端给的可用额度;null/未知时用 N 占位
|
|
1645
|
+
*/
|
|
1646
|
+
function lockedPanelDetail(limit) {
|
|
1647
|
+
var n = (limit === 0 || limit) ? String(limit) : "N";
|
|
1648
|
+
return "本机已被锁定(当前套餐可同时使用 " + n + " 台)。"
|
|
1649
|
+
+ "升级 Pro Max 可多台设备同时使用;或到手机端「管理电脑」解绑其他设备后本机会自动解锁。";
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* 从云端地址派生「升级 Pro Max」页 URL:`https://host/app/…` → `https://host/app/promo`。
|
|
1653
|
+
* **不硬编码任何域名**(官方/自建/测试环境都走同一个派生规则);拿不到地址返回 "",
|
|
1654
|
+
* 调用方回退到既有「带登录态升级」通道(openUpgradeAuth)。
|
|
1655
|
+
*/
|
|
1656
|
+
function upgradePromoUrlOf(base) {
|
|
1657
|
+
var s = String(base || "");
|
|
1658
|
+
if (!s) return "";
|
|
1659
|
+
try {
|
|
1660
|
+
var origin = s.split("/app")[0];
|
|
1661
|
+
if (origin && /^https?:\/\//i.test(origin)) return origin.replace(/\/+$/, "") + "/app/promo";
|
|
1662
|
+
} catch (e) { /* 落到返回空 */ }
|
|
1663
|
+
return "";
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1626
1666
|
// ── 剪贴板(0.6.9 邀请区「一键复制」)────────────────────────────────────
|
|
1627
1667
|
/**
|
|
1628
1668
|
* 复制文本到剪贴板,返回 Promise<boolean>。
|
|
@@ -4664,11 +4704,15 @@ window.__ModuleLoader__.load({
|
|
|
4664
4704
|
// 状态行优先用连接阶段文案(node 半下发的 user-facing 文案):
|
|
4665
4705
|
// 「正在准备运行环境(首次约 1~2 分钟)… / 正在启动 Bridge… / 正在连接中继… / 已连接 ✅ …」;
|
|
4666
4706
|
// 旧版 host 没有 connect 字段 → 完全回退到原来的 running 文案(既有行为不变)。
|
|
4707
|
+
// 锁定态的状态行不能沿用 CONNECT_TEXT.error("连接失败,请按下方提示处理"):用户会以为是自己网络/软件坏了。
|
|
4708
|
+
var connLocked = Boolean(connInfo && connInfo.locked);
|
|
4667
4709
|
var statusTxt = st === null
|
|
4668
4710
|
? "查询中…"
|
|
4669
4711
|
: !loggedInSaaS
|
|
4670
4712
|
? "请先登录(下方账号卡片)后启用远程访问"
|
|
4671
|
-
: connInfo
|
|
4713
|
+
: connInfo
|
|
4714
|
+
? (connLocked ? "本机已被锁定(当前套餐设备额度已满,升级 Pro Max 或解绑其他设备后自动恢复)" : connText)
|
|
4715
|
+
: serviceRunning ? "已连接(可远程访问)" : "等待设备连接";
|
|
4672
4716
|
var dotOn = loggedInSaaS ? (connInfo ? connOnline : serviceRunning) : false;
|
|
4673
4717
|
var dotCls = "dru-dot " + (dotOn ? "dru-dot-on" : "dru-dot-off");
|
|
4674
4718
|
|
|
@@ -4751,6 +4795,22 @@ window.__ModuleLoader__.load({
|
|
|
4751
4795
|
return h("div", null, rows);
|
|
4752
4796
|
}
|
|
4753
4797
|
|
|
4798
|
+
/**
|
|
4799
|
+
* 锁定态的主行动:「🚀 升级 Pro Max」。
|
|
4800
|
+
* 链接从云端账号信息派生(public-config 的 app_url / 面板已拿到的 remoteUrl → origin + /app/promo),
|
|
4801
|
+
* **不硬编码域名**;派生不出地址(未登录/取配置失败)时退回既有「带登录态升级」通道。
|
|
4802
|
+
*/
|
|
4803
|
+
function openLockedUpgrade() {
|
|
4804
|
+
var promo = "";
|
|
4805
|
+
try { promo = upgradePromoUrlOf((st && st.remoteUrl) || (pub && pub.app_url)); } catch (e) { promo = ""; }
|
|
4806
|
+
if (promo) {
|
|
4807
|
+
try { window.open(promo, "_blank", "noopener"); } catch (e2) { /* 弹窗被拦截:下面给提示 */ }
|
|
4808
|
+
return;
|
|
4809
|
+
}
|
|
4810
|
+
try { openUpgradeAuth(); } catch (e3) {
|
|
4811
|
+
try { setMessage({ kind: "err", text: "打开升级页失败,请在手机端「我的 → 升级 Pro Max」完成升级" }); } catch (e4) { /* 忽略 */ }
|
|
4812
|
+
}
|
|
4813
|
+
}
|
|
4754
4814
|
function renderConnectBlock() {
|
|
4755
4815
|
if (!loggedInSaaS || !connInfo) return null;
|
|
4756
4816
|
var phase = connInfo.phase || "";
|
|
@@ -4760,18 +4820,29 @@ window.__ModuleLoader__.load({
|
|
|
4760
4820
|
? "设备已登记到你的账号:手机端「设备列表」可以看到这台电脑。"
|
|
4761
4821
|
: "设备已注册到中继:手机端扫码或打开链接即可进入这台电脑。");
|
|
4762
4822
|
}
|
|
4823
|
+
// 锁定态(契约 §8.1):设备被超额锁定 ≠ 普通故障 —— bridge 活着并在 30s 轮询等解锁,
|
|
4824
|
+
// 所以文案给「升级 / 解绑」两条出路,主行动是升级按钮,且**不显示"自动重试 N 次"**(那会暗示死循环)。
|
|
4825
|
+
var lockedInfo = connInfo.locked && typeof connInfo.locked === "object" ? connInfo.locked : null;
|
|
4826
|
+
var isLocked = Boolean(lockedInfo) || Boolean(connError && connError.code === "device_locked");
|
|
4763
4827
|
var rows = [];
|
|
4764
4828
|
if (phase === "error") {
|
|
4765
4829
|
rows.push(h("div", { className: "dru-msg dru-msg-err", style: { marginTop: 6 } },
|
|
4766
|
-
"⚠️ " + ((connError && connError.message) || connText)));
|
|
4830
|
+
(isLocked ? "🔒 " : "⚠️ ") + ((connError && connError.message) || connText)));
|
|
4767
4831
|
if (connInfo.detail) rows.push(h("div", { className: "dru-hint", style: { marginTop: 4 } }, connInfo.detail));
|
|
4768
|
-
if (connInfo.nextRetryInMs > 0) {
|
|
4832
|
+
if (!isLocked && connInfo.nextRetryInMs > 0) {
|
|
4769
4833
|
rows.push(h("div", { className: "dru-hint", style: { marginTop: 4 } },
|
|
4770
4834
|
"已自动重试 " + (connInfo.attempts || 0) + " 次,约 " + Math.ceil(connInfo.nextRetryInMs / 1000) + " 秒后自动再试(也可以点下面按钮立刻重试)"));
|
|
4771
4835
|
}
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
h("
|
|
4836
|
+
if (isLocked) {
|
|
4837
|
+
// 主行动:升级 Pro Max(红色错误条里最该点的就是它)
|
|
4838
|
+
rows.push(h("div", { className: "dru-actions", style: { marginTop: 8 } },
|
|
4839
|
+
h("button", { type: "button", className: "dru-btn dru-btn-primary", onClick: openLockedUpgrade }, "🚀 升级 Pro Max"),
|
|
4840
|
+
h("button", { type: "button", className: "dru-btn dru-btn-ghost", onClick: copyDiagnostics }, copiedDiag ? "已复制" : "复制诊断信息")));
|
|
4841
|
+
} else {
|
|
4842
|
+
rows.push(h("div", { className: "dru-actions", style: { marginTop: 8 } },
|
|
4843
|
+
h("button", { type: "button", className: "dru-btn dru-btn-primary", disabled: busy !== "", onClick: retryConnect }, busy === "connect-retry" ? "重试中…" : "重试"),
|
|
4844
|
+
h("button", { type: "button", className: "dru-btn dru-btn-ghost", onClick: copyDiagnostics }, copiedDiag ? "已复制" : "复制诊断信息")));
|
|
4845
|
+
}
|
|
4775
4846
|
rows.push(h("div", { className: "dru-hint", style: { marginTop: 6 } },
|
|
4776
4847
|
"查看日志:后台服务 " + (connInfo.logPath || "") + " ,安装 " + (connInfo.installLogPath || "") + "(把「复制诊断信息」的内容发给客服可加速定位)"));
|
|
4777
4848
|
var diagToolsErr = renderDiagTools();
|
|
@@ -5121,7 +5192,8 @@ window.__ModuleLoader__.load({
|
|
|
5121
5192
|
// 自动连接进度(登录后由面板 2.5s 短轮询自动推进):让用户在 bridge 卡也能一眼看到
|
|
5122
5193
|
// 「到底走到哪一步了」——进程在跑≠能用,注册到中继才算。
|
|
5123
5194
|
connInfo && connPhase && connPhase !== "no_account"
|
|
5124
|
-
? h("div", { className: "dru-hint", style: { marginTop: 6 } },
|
|
5195
|
+
? h("div", { className: "dru-hint", style: { marginTop: 6 } },
|
|
5196
|
+
"自动连接进度:" + (connInfo.locked ? "本机已被锁定(当前套餐额度已满,升级或解绑后自动恢复)" : connText))
|
|
5125
5197
|
: null,
|
|
5126
5198
|
h("div", { className: "dru-actions", style: { marginTop: 10 } },
|
|
5127
5199
|
!serviceRunning
|
|
@@ -5145,13 +5217,22 @@ window.__ModuleLoader__.load({
|
|
|
5145
5217
|
"⚠️ 上一次启动失败(自启动入口失效),已自动清理失效自启动项并重新补装运行环境。稍候会自动恢复;仍失败请点「一键更新」。")
|
|
5146
5218
|
: null,
|
|
5147
5219
|
st && st.service && st.service.bindError
|
|
5148
|
-
?
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5220
|
+
? (function (be) {
|
|
5221
|
+
var isLocked = be.code === "device_locked" || be.code === "device_limit_exceeded";
|
|
5222
|
+
var isBindCap = be.code === "device_bind_cap";
|
|
5223
|
+
var beLimit = (be.limit === 0 || be.limit) ? be.limit : null;
|
|
5224
|
+
return h("div", { className: "dru-msg dru-msg-err", style: { marginTop: 8 } },
|
|
5225
|
+
(isLocked ? "🔒 本机已被锁定:" : "⚠️ 设备注册失败:") + (be.message || "未说明原因"),
|
|
5226
|
+
h("div", { className: "dru-hint", style: { marginTop: 4 } },
|
|
5227
|
+
isLocked
|
|
5228
|
+
// 契约 §8.1 的新语义:不是"解绑旧设备才能登录",而是"超额锁定 → 升级或解绑,本机自动解锁"
|
|
5229
|
+
? lockedPanelDetail(beLimit)
|
|
5230
|
+
: isBindCap
|
|
5231
|
+
// device_bind_cap = 防滥用硬上限(bridge 退出码 5):守护按指数退避重试,不是无限重试
|
|
5232
|
+
? "已绑定设备数达到上限:请在手机端「管理电脑」解绑不再使用的设备;解绑后本机会自动恢复(后台守护会按指数退避重试,无需反复点按钮)。"
|
|
5233
|
+
: "请确认网络与账号状态后重试;仍未解决可点下方「彻底卸载」后重新安装。")
|
|
5234
|
+
);
|
|
5235
|
+
})(st.service.bindError)
|
|
5155
5236
|
: null
|
|
5156
5237
|
),
|
|
5157
5238
|
// 版本与更新(自管理:检测新版 / 一键在线更新 / 彻底卸载)
|
|
@@ -5332,8 +5413,12 @@ window.__ModuleLoader__.load({
|
|
|
5332
5413
|
function renderAnnouncementsCard() {
|
|
5333
5414
|
if (!annState.ok || !annState.bubble) return null;
|
|
5334
5415
|
var items = annState.items;
|
|
5335
|
-
if (!items.length) return null;
|
|
5336
5416
|
var unread = annState.unread;
|
|
5417
|
+
// 2026-10-25:列表为空时**保留板块**(显示「暂无公告」)而不是整卡消失 ——
|
|
5418
|
+
// 板块本身是用户确认"公告功能还在"的锚点,静默消失会被当成功能坏了。
|
|
5419
|
+
if (!items.length) {
|
|
5420
|
+
return card("📣 公告", [h("div", { className: "dru-hint" }, "暂无公告")]);
|
|
5421
|
+
}
|
|
5337
5422
|
return card("📣 公告" + (unread > 0 ? "(" + unread + " 条未读)" : ""), [
|
|
5338
5423
|
h("div", { className: "dru-ann-list" }, items.map(function (it) {
|
|
5339
5424
|
return h("button", {
|
|
@@ -5621,6 +5706,19 @@ window.__ModuleLoader__.load({
|
|
|
5621
5706
|
return "约 " + Math.round(n / 60_000) + " 分钟";
|
|
5622
5707
|
}
|
|
5623
5708
|
|
|
5709
|
+
/**
|
|
5710
|
+
* 宽松读一个「条数」字段:只有真正的数字(JSON number / 纯数字串)才算权威,
|
|
5711
|
+
* null / "" / undefined / true 一律视为**这个字段没有** → 用 fallback。
|
|
5712
|
+
* 为什么要较真:旧 bridge 不认这个键时若把它当 0,面板会**藏起**真实积压(用户以为通知丢了);
|
|
5713
|
+
* 反过来把字段缺失当"有积压"同样不行 —— 那会平白吓人。
|
|
5714
|
+
*/
|
|
5715
|
+
function wechatCountOf(value, fallback) {
|
|
5716
|
+
var numeric = typeof value === "number" || (typeof value === "string" && value.trim() !== "");
|
|
5717
|
+
var n = numeric ? Number(value) : NaN;
|
|
5718
|
+
if (!Number.isFinite(n) || n < 0) return fallback;
|
|
5719
|
+
return Math.floor(n);
|
|
5720
|
+
}
|
|
5721
|
+
|
|
5624
5722
|
/**
|
|
5625
5723
|
* 🤖 微信机器人绑定面板。
|
|
5626
5724
|
* @param {{embedded?: boolean}} [props] embedded=true(远程访问面板里的「微信机器人通道」tab)
|
|
@@ -6000,6 +6098,16 @@ window.__ModuleLoader__.load({
|
|
|
6000
6098
|
// 规格 §9:**只有两态**。这一行是「已绑定 · 健康提示」,两种取值都明确带「已绑定」;
|
|
6001
6099
|
// 健康只由「最近一次推送是否失败」决定,**不参与绑定状态**(通道没在跑等情形另起一行说明,不冒充第三种状态)。
|
|
6002
6100
|
var healthy = !wx.last_error;
|
|
6101
|
+
// 🛑 推送静默(2026-10-29):微信只在「你最近发过消息」的很短窗口内允许机器人主动发言,
|
|
6102
|
+
// 窗口外平台没有续期接口(契约 §2.4/§3.4)→ bridge 识别出令牌过期后会**暂停主动推送**
|
|
6103
|
+
// (不再每小时猛打 60 次),并把没送出去的通知留在待补发队列,等你下次说话再补。
|
|
6104
|
+
// 面板这里只做两件事:① 如实说明「为什么暂时收不到推送」;② 给出唯一一条恢复动作(回一句话)。
|
|
6105
|
+
// ⚠️ 它只是**提示**:不参与两态绑定判定,也不隐藏绑定/解绑按钮(规格 §9)。
|
|
6106
|
+
// 字段缺失(旧 bridge 没有这个键)= 没开启静默 → 一律不渲染,绝不能把 undefined 当成"已静默"。
|
|
6107
|
+
var pushPaused = wx.push_paused === true;
|
|
6108
|
+
// 待补发条数:新 bridge 下发 pending_outbox_count(契约 §3.7),旧 bridge 只有 pending_outbox。
|
|
6109
|
+
// 两个字段都在时**只认优先的那一个**,避免同一件事在卡片上提示两遍。
|
|
6110
|
+
var outboxCount = wechatCountOf(wx.pending_outbox_count, wechatCountOf(wx.pending_outbox, 0));
|
|
6003
6111
|
body.push(h("div", { className: "dru-card" },
|
|
6004
6112
|
h("h3", null, "已绑定"),
|
|
6005
6113
|
h("div", { className: "dru-status-line" },
|
|
@@ -6013,6 +6121,16 @@ window.__ModuleLoader__.load({
|
|
|
6013
6121
|
wechatTimeText(wx.connected_at) ? h("div", { className: "dru-meta" }, "最近上线:" + wechatTimeText(wx.connected_at)) : null,
|
|
6014
6122
|
Number(wx.pending_replies) > 0 ? h("div", { className: "dru-meta" }, "等待你回复的通知:" + Number(wx.pending_replies) + " 条") : null,
|
|
6015
6123
|
wx.last_error ? h("div", { className: "dru-msg dru-msg-warn", role: "status", "aria-live": "polite" }, "⚠️ 最近一次推送失败:" + String(wx.last_error)) : null,
|
|
6124
|
+
// 静默提示紧跟 last_error:两者是**两回事**(上面是单次发送失败,这里是"平台侧窗口关了"),
|
|
6125
|
+
// 但都只是健康提示。文案刻意给出可执行的恢复动作,避免用户以为要重新绑定。
|
|
6126
|
+
pushPaused
|
|
6127
|
+
? h("div", { className: "dru-msg dru-msg-warn", role: "status", "aria-live": "polite" },
|
|
6128
|
+
"⚠️ 微信推送已静默:微信通道只在你最近发过消息后的一小段时间内允许机器人主动发消息。给机器人回任意一条消息即可恢复,未送达的通知会补发。")
|
|
6129
|
+
: null,
|
|
6130
|
+
// 静默期间给一个**简短**的待补发数字(详细说明已由上面的静默提示承担,不再重复一整段)。
|
|
6131
|
+
pushPaused && outboxCount > 0
|
|
6132
|
+
? h("div", { className: "dru-meta" }, "待补发 " + outboxCount + " 条:你回任意一条消息后会一起补给你。")
|
|
6133
|
+
: null,
|
|
6016
6134
|
Number(wx.cooldown_ms) > 0
|
|
6017
6135
|
? h("div", { className: "dru-msg dru-msg-warn" }, [
|
|
6018
6136
|
"⏳ 微信侧会话超时,后台已按官方做法退避(还剩 " + wechatCooldownText(wx.cooldown_ms) + ")。",
|
|
@@ -6037,9 +6155,11 @@ window.__ModuleLoader__.load({
|
|
|
6037
6155
|
? h("div", { className: "dru-msg dru-msg-warn" },
|
|
6038
6156
|
"提示:任务事件订阅未就绪(状态 " + String(wx.events_state) + "),这段时间任务跑完可能不会推给你。稍候会自动重连;持续如此请到「📱 远程访问」重启后台服务。")
|
|
6039
6157
|
: null,
|
|
6040
|
-
|
|
6158
|
+
// 静默态下这条长文案与上面的「静默提示 + 待补发 N 条」重复,故只在**未静默**时显示:
|
|
6159
|
+
// 条数统一走 outboxCount(新 bridge 用 pending_outbox_count,旧 bridge 回落 pending_outbox)。
|
|
6160
|
+
!pushPaused && outboxCount > 0
|
|
6041
6161
|
? h("div", { className: "dru-msg dru-msg-warn" },
|
|
6042
|
-
"有 " +
|
|
6162
|
+
"有 " + outboxCount + " 条通知刚才没能发出去(微信侧发送失败),已排在待补发队列里 —— 你下次在微信里任意发一句话,就会自动补给你。")
|
|
6043
6163
|
: null,
|
|
6044
6164
|
wx.last_fault && wx.last_fault.code
|
|
6045
6165
|
? h("div", { className: "dru-hint" },
|