From 6dc6258c195109a3f08891958296a8bb130ba34c Mon Sep 17 00:00:00 2001 From: Karlcarlo Date: Wed, 27 May 2026 03:07:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20H5=20=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E8=B0=83=E8=AF=95=E9=9D=A2=E6=9D=BF=EF=BC=8C=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=88=9D=E5=A7=8B=20URL=20=E4=BF=A1=E6=81=AF=E5=B9=B6?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B8=83=E5=B0=94=E5=80=BC=E6=B8=B2=E6=9F=93?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/login-fix-plan.md | 196 +++++++++++++++++++++++++++ frontend/index.html | 1 + frontend/src/app/pages/LoginPage.tsx | 7 +- 3 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 docs/login-fix-plan.md diff --git a/docs/login-fix-plan.md b/docs/login-fix-plan.md new file mode 100644 index 0000000..2a415f1 --- /dev/null +++ b/docs/login-fix-plan.md @@ -0,0 +1,196 @@ +# H5 登录卡住问题分析与小程序端修复方案 + +--- + +## 一、问题描述 + +### 1.1 现象 + +从小程序跳转到 H5 页面后,页面一直显示"正在获取登录信息...",无法正常加载首页或引导页。 + +### 1.2 调试信息 + +H5 调试面板(`?debug=1`)输出: + +``` +url https://mp.yizhezhe.com/app_03/ +isWxMp (空 — 实际为 true,因 false 渲染为空) +hasTokenParam (空 — URL 中无 token 参数) +step wx-postMessage-done (已发送 NEED_TOKEN 请求) +``` + +### 1.3 根因分析 + +H5 登录流程有三个路径: + +| 分支 | 触发条件 | 预期行为 | +|------|---------|---------| +| URL 有 token | `?token=***` | 解码 token → 登录 → 跳首页 | +| 小程序环境 | 检测到 `MicroMessenger` + `miniprogram` | 发 `postMessage({NEED_TOKEN})` 等小程序响应 | +| 浏览器开发 | 其他情况 | 调 `dev-login` 自动登录 | + +当前状态:URL **没有 token**,而是进入了第 2 条路。H5 向小程序发送了 `postMessage({ type: "NEED_TOKEN" })`,但**小程序侧没有监听和处理这条消息**,导致 H5 永远停在"正在获取登录信息..."。 + +--- + +## 二、修复方案 + +### 方案 A(推荐):打开 web-view 时直接带上 token + +H5 不需要发 `NEED_TOKEN` 消息。每次打开 web-view 时,小程序直接把 token 拼到 URL 里。 + +```javascript +// 小程序侧 — 打开 web-view 的页面中 +Page({ + data: { + // 小程序自己的 token(通过 wx.login / 微信登录获取) + token: 'eyJ0e...Egnk', + }, + + onLoad() { + // 确保 token 已就绪后再打开 web-view + this.ensureToken().then(token => { + this.setData({ token }); + }); + }, + + async ensureToken() { + // 此处用小程序自己的登录逻辑获取 token + // 示例:如果已有缓存则用缓存,否则先登录 + const cached = wx.getStorageSync('auth_token'); + if (cached) return cached; + // 登录逻辑... + return token; + }, + + // WXML: + get webviewUrl() { + const base = 'https://mp.yizhezhe.com/app_03/'; + const token = this.data.token; + // 有 token 就加 ?token=,没有就不加(H5 会 fallback 到 dev-login) + return token ? `${base}?token=${encodeURIComponent(token)}` : base; + }, +}); +``` + +**优点:** +- 最简单直接,无额外消息通信 +- H5 拿到 token 后一步到位完成登录 +- 不需要修改 H5 端代码 + +--- + +### 方案 B:监听 NEED_TOKEN 消息后回传 + +如果 web-view 先打开(URL 不带 token),H5 检测到小程序环境后发 `NEED_TOKEN` 消息,小程序接收后把 token 写回 URL。 + +```javascript +// 小程序侧 — web-view 所在的页面 +Page({ + onLoad() { + // 确保小程序侧准备好接收 postMessage + }, + + // web-view 的 bindmessage 回调 + onMessage(e) { + const data = e.detail.data || e.detail; // 兼容不同版本 + console.log('web-view 消息:', data); + + if (data.type === 'NEED_TOKEN') { + // 获取 token 并传递回 web-view + const token = this.getToken(); + if (!token) { + console.error('token 未就绪,无法响应 NEED_TOKEN'); + return; + } + + // 方式 1: 重新加载 web-view 的 src(推荐) + const base = 'https://mp.yizhezhe.com/app_03/'; + const webview = this.selectComponent('#webview'); + // 注意: 小程序 web-view 不支持直接改 src,需要用 navigateTo 重新打开 + wx.navigateTo({ + url: `/pages/webview/webview?token=${encodeURIComponent(token)}`, + }); + + // 方式 2: 如果 web-view 通过页面参数传递 token(适合小程序自定义导航) + // 见方案 A,在打开时直接带上 token + } + }, + + getToken() { + return wx.getStorageSync('auth_token'); + }, +}); +``` + +对应 WXML: + +```html + +``` + +**缺点:** +- 需要额外一轮消息通信,H5 会先显示"正在获取登录信息..."再刷新 +- 小程序 `web-view` 组件没有直接修改 `src` 的 API,只能重新 `navigateTo` +- 用户体验不如方案 A 顺滑 + +--- + +## 三、推荐方案对比 + +| 维度 | 方案 A(URL 带 token) | 方案 B(消息回调) | +|------|-----------------------|------------------| +| 实现复杂度 | 低 | 中 | +| 用户体验 | 流畅(一步到位) | 有闪烁(页面刷新) | +| H5 端改动 | 不需要 | 不需要 | +| 小程序端改动 | 加 URL 拼接 | 加 bindmessage 回调 | +| 额外通信 | 无 | 一轮 postMessage | +| token 过期处理 | 打开时即携带最新 token | 可能携带过期 token 再等 NEED_TOKEN | + +**结论:采用方案 A。** + +--- + +## 四、H5 端问题关联修改 + +### 4.1 Debug 面板布尔值修复 + +当前 `DebugPanel` 渲染布尔值 `false` 时会因为 `{v || "(空)"}` 显示为空。需要改: + +```tsx +// 当前(有 bug): +{v || (空)} + +// 修复后: +{v === false ? 'false' : v === true ? 'true' : v || (空)} +``` + +### 4.2 `isWechatMiniProgram()` 废弃 + +如果小程序采用方案 A(打开时直接带 token),H5 不再需要 `isWechatMiniProgram` 检测和 `NEED_TOKEN` 消息发送逻辑。上线后可清理: + +```typescript +// LoginPage.tsx — 删除以下代码段 +if (isWechatMiniProgram()) { + try { + (window as any).wx?.miniProgram?.postMessage({ type: "NEED_TOKEN" }); + } catch {} + setError("正在获取登录信息..."); + return; +} +``` + +--- + +## 五、验证步骤 + +1. 小程序侧按方案 A 修改,确保打开 web-view 时 URL 带 `?token=***` +2. 访问 `https://mp.yizhezhe.com/app_03/?token=***&debug=1` +3. 检查调试面板:`hasTokenParam=true`、`step=decode-ok` 或 `step=fetch-ok` +4. 确认跳转到 `/home` 或 `/onboarding` +5. 关闭 `?debug=1` 正常使用 +6. (可选)清理 DebugPanel 和 `isWechatMiniProgram` 相关代码 diff --git a/frontend/index.html b/frontend/index.html index 75e76ae..5d66b0d 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -9,6 +9,7 @@ +
diff --git a/frontend/src/app/pages/LoginPage.tsx b/frontend/src/app/pages/LoginPage.tsx index 535fbb4..36ce6d6 100644 --- a/frontend/src/app/pages/LoginPage.tsx +++ b/frontend/src/app/pages/LoginPage.tsx @@ -33,6 +33,7 @@ const INIT_DEBUG: DebugInfo = { }; function DebugPanel({ info }: { info: DebugInfo }) { + const [initialUrl] = useState(() => (window as any).__INITIAL_URL__ || '(N/A)'); return (
🐞 调试信息 (关闭需去掉 ?debug=1)
@@ -41,9 +42,13 @@ function DebugPanel({ info }: { info: DebugInfo }) { {Object.entries(info).map(([k, v]) => ( {k} - {v || (空)} + {v === false ? 'false' : v === true ? 'true' : v || (空)} ))} + + initialUrl + {initialUrl} +