@open-wxa/wxa-runner-core 0.2.0 → 0.2.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.
@@ -236,6 +236,14 @@
236
236
  };
237
237
  function createMiscApis(appid, options) {
238
238
  const fs = new MemoryFileSystem();
239
+ const realLogin = async (api, url) => {
240
+ try {
241
+ return await postDevServer(url);
242
+ } catch (error) {
243
+ options?.onRealLoginFail?.(api, error.message);
244
+ throw error;
245
+ }
246
+ };
239
247
  const guard = (api, action) => {
240
248
  try {
241
249
  return ok(api, action() || {});
@@ -285,9 +293,9 @@
285
293
  operateWXData: (args, { api }) => args.data?.api_name === "webapi_getwxanetwork" ? bizDomainUnavailable(api) : ok(api),
286
294
  getAppContact: (args, { api }) => args.keyPath === "WxaAppInfo.Network.BizDomain" ? bizDomainUnavailable(api) : ok(api),
287
295
  // 真实登录失败时明确返回 fail,不退回假数据:配置或登录态的问题要让开发者马上发现
288
- login: options?.realLogin ? async (_args, { api }) => ok(api, { code: (await postDevServer("/__wxa__/api/login")).code }) : (_args, { api }) => ok(api, { code: `wxa_mock_code_${Date.now().toString(36)}` }),
296
+ login: options?.realLogin ? async (_args, { api }) => ok(api, { code: (await realLogin(api, "/__wxa__/api/login")).code }) : (_args, { api }) => ok(api, { code: `wxa_mock_code_${Date.now().toString(36)}` }),
289
297
  // 基础库只从 JSON.parse(data) 里取手机号的 code
290
- getPhoneNumber: options?.realLogin ? async (_args, { api }) => ok(api, await postDevServer("/__wxa__/api/phone-number")) : (_args, { api }) => ok(api, { data: JSON.stringify({ code: `mock_phone_code_${Date.now()}` }), encryptedData: "mock_encrypted", iv: "mock_iv" }),
298
+ getPhoneNumber: options?.realLogin ? async (_args, { api }) => ok(api, await realLogin(api, "/__wxa__/api/phone-number")) : (_args, { api }) => ok(api, { data: JSON.stringify({ code: `mock_phone_code_${Date.now()}` }), encryptedData: "mock_encrypted", iv: "mock_iv" }),
291
299
  checkSession: () => void 0,
292
300
  authorize: () => void 0,
293
301
  getSetting: (_args, { api }) => ok(api, {
@@ -1404,7 +1412,14 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
1404
1412
  .wxa-toolbar button { border: 0; background: #111214; color: #fff; border-radius: 999px; padding: 4px 12px; font-size: 12px; cursor: pointer; }
1405
1413
  .wxa-toolbar .wxa-scale { min-width: 52px; background: #fff; color: #333; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12); font-variant-numeric: tabular-nums; }
1406
1414
  .wxa-route { font-family: Menlo, monospace; }
1415
+ .wxa-notice { position: fixed; top: 16px; right: 16px; z-index: 100; display: flex; align-items: flex-start; gap: 10px; width: max-content; max-width: min(320px, calc(100vw - 32px)); padding: 12px 10px 12px 14px; box-sizing: border-box; border-radius: 12px; background: #fff; box-shadow: 0 0 0 1px rgba(15,23,42,.06), 0 12px 32px -8px rgba(15,23,42,.28); font-size: 13px; line-height: 20px; color: #555; animation: wxa-notice-in .18s ease-out; }
1416
+ .wxa-notice-icon { flex: none; width: 18px; height: 18px; margin-top: 1px; border-radius: 50%; background: #fa5151; color: #fff; font-size: 12px; font-weight: 700; line-height: 18px; text-align: center; }
1417
+ .wxa-notice-text { flex: 1; min-width: 0; word-break: break-word; text-wrap: pretty; }
1418
+ .wxa-notice-title { font-weight: 600; color: #111214; }
1419
+ .wxa-notice-close { flex: none; width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%; background: none; color: #999; font-size: 16px; line-height: 20px; cursor: pointer; }
1420
+ .wxa-notice-close:hover { background: rgba(0,0,0,.06); color: #111214; }
1407
1421
  @keyframes wxa-spin { to { transform: rotate(360deg); } }
1422
+ @keyframes wxa-notice-in { from { opacity: 0; transform: translateY(-8px); } }
1408
1423
  `;
1409
1424
 
1410
1425
  // src/runtime/host/ui/Picker.ts
@@ -1493,6 +1508,7 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
1493
1508
  // src/runtime/host/ui/PhoneShell.ts
1494
1509
  var SCALE_STORAGE_KEY = "wxa-runner:scale";
1495
1510
  var MIN_SCALE = 0.5;
1511
+ var NOTICE_DURATION = 6e3;
1496
1512
  function loadScaleMode() {
1497
1513
  try {
1498
1514
  const saved = localStorage.getItem(SCALE_STORAGE_KEY);
@@ -1507,6 +1523,8 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
1507
1523
  this.scaleMode = loadScaleMode();
1508
1524
  this.toastEl = null;
1509
1525
  this.toastTimer = null;
1526
+ this.noticeEl = null;
1527
+ this.noticeTimer = null;
1510
1528
  this.tabBarConfig = null;
1511
1529
  this.onTabTap = () => void 0;
1512
1530
  this.navigation = { title: "", backgroundColor: "#000", textStyle: "white", showBack: false, custom: false };
@@ -1542,6 +1560,7 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
1542
1560
  <span class="wxa-route"></span><button class="wxa-scale"></button><button class="wxa-reload">\u5237\u65B0</button>
1543
1561
  </div>
1544
1562
  </div>`;
1563
+ this.stage = root.querySelector(".wxa-stage");
1545
1564
  this.slot = root.querySelector(".wxa-device-slot");
1546
1565
  this.device = root.querySelector(".wxa-device");
1547
1566
  this.toolbar = root.querySelector(".wxa-toolbar");
@@ -1771,6 +1790,30 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
1771
1790
  showImagePreview(urls, current) {
1772
1791
  openImagePreview(this.overlay, urls, current);
1773
1792
  }
1793
+ /**
1794
+ * 在手机外面提示 wxa-runner 自身的问题(如真实登录失败),不混进小程序的界面。
1795
+ * 同一时间只显示一条,新的替换旧的:业务代码反复重试时不会堆满屏幕
1796
+ */
1797
+ showNotice(title, message) {
1798
+ this.hideNotice();
1799
+ const notice = element("div", "wxa-notice");
1800
+ notice.setAttribute("role", "alert");
1801
+ const text = element("div", "wxa-notice-text");
1802
+ text.append(element("div", "wxa-notice-title", title), element("div", "wxa-notice-message", message));
1803
+ const close = element("button", "wxa-notice-close", "\xD7");
1804
+ close.setAttribute("aria-label", "\u5173\u95ED");
1805
+ close.addEventListener("click", () => this.hideNotice());
1806
+ notice.append(element("span", "wxa-notice-icon", "!"), text, close);
1807
+ this.stage.appendChild(notice);
1808
+ this.noticeEl = notice;
1809
+ this.noticeTimer = setTimeout(() => this.hideNotice(), NOTICE_DURATION);
1810
+ }
1811
+ hideNotice() {
1812
+ if (this.noticeTimer) clearTimeout(this.noticeTimer);
1813
+ this.noticeTimer = null;
1814
+ this.noticeEl?.remove();
1815
+ this.noticeEl = null;
1816
+ }
1774
1817
  showError(message) {
1775
1818
  this.clearError();
1776
1819
  const el = element("div", "wxa-error", message);
@@ -2154,7 +2197,10 @@ html, body { margin: 0; height: 100%; background: radial-gradient(120% 80% at 50
2154
2197
  documentOf: (webviewId) => this.pages.get(webviewId)?.win?.document,
2155
2198
  emitToWebview: (webviewId, event, data) => this.emitToWebview(webviewId, event, data)
2156
2199
  }),
2157
- ...createMiscApis(this.info.appid, { realLogin: this.info.realLogin })
2200
+ ...createMiscApis(this.info.appid, {
2201
+ realLogin: this.info.realLogin,
2202
+ onRealLoginFail: (api, reason2) => this.shell.showNotice(`wx.${api} \u771F\u5B9E\u767B\u5F55\u5931\u8D25`, reason2)
2203
+ })
2158
2204
  });
2159
2205
  }
2160
2206
  };