feat: 添加 H5 登录调试面板,显示初始 URL 信息并修复布尔值渲染问题

This commit is contained in:
2026-05-27 03:07:38 +08:00
parent 5a0f1f4f8e
commit 6dc6258c19
3 changed files with 203 additions and 1 deletions

196
docs/login-fix-plan.md Normal file
View File

@@ -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: <web-view src="{{webviewUrl}}">
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
<web-view
id="webview"
src="https://mp.yizhezhe.com/app_03/"
bindmessage="onMessage"
/>
```
**缺点:**
- 需要额外一轮消息通信,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):
<td>{v || <span className="text-gray-500">(空)</span>}</td>
// 修复后:
<td>{v === false ? 'false' : v === true ? 'true' : v || <span className="text-gray-500">(空)</span>}</td>
```
### 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` 相关代码

View File

@@ -9,6 +9,7 @@
</head>
<body>
<script>window.__INITIAL_URL__ = document.URL;</script>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>

View File

@@ -33,6 +33,7 @@ const INIT_DEBUG: DebugInfo = {
};
function DebugPanel({ info }: { info: DebugInfo }) {
const [initialUrl] = useState(() => (window as any).__INITIAL_URL__ || '(N/A)');
return (
<div className="absolute top-0 left-0 right-0 z-50 bg-black/80 text-white text-xs font-mono p-3 text-left max-h-[60vh] overflow-auto">
<div className="font-bold text-yellow-400 mb-1">🐞 调试信息 (关闭需去掉 ?debug=1)</div>
@@ -41,9 +42,13 @@ function DebugPanel({ info }: { info: DebugInfo }) {
{Object.entries(info).map(([k, v]) => (
<tr key={k} className="border-b border-white/10">
<td className="pr-2 text-gray-400 w-28 align-top whitespace-nowrap">{k}</td>
<td className="break-all text-green-300">{v || <span className="text-gray-500">(空)</span>}</td>
<td className="break-all text-green-300">{v === false ? 'false' : v === true ? 'true' : v || <span className="text-gray-500">(空)</span>}</td>
</tr>
))}
<tr className="border-b border-yellow-400/50">
<td className="pr-2 text-yellow-400 w-28 align-top whitespace-nowrap font-bold">initialUrl</td>
<td className="break-all text-yellow-300">{initialUrl}</td>
</tr>
</tbody>
</table>
</div>