Files
crmeb-mp/AGENTS.md
Karlcarlo dcc84dd7b7 docs(AGENTS): 补 §4.3 —— 回到首页要清 last_app(首页壳 + mama-box 两侧互补)
- 表格列出三条返回路径分别由谁清、判定依据是什么
- 明确禁止「mama-box 每次加载都清」(会把恢复落点加载误清,毁掉记录)
- 首页壳只在重载目标命中 /mama-box 时清
2026-09-22 11:58:12 +08:00

106 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md · CRMEB 小程序(web-view 宿主)
> 在本仓库工作前先读。**跨仓库的权威约定在 `box_app/AGENTS.md`**(WSL:`/home/jone/workspace/box_app/AGENTS.md`)——
> 三仓布局、身份契约、接口命名、部署流程都在那里。本文件只写小程序侧特有的部分。
---
## 1. 这是什么
CRMEB 商城小程序(uniapp,manifest `name=DoubaShop`),同时是**整个 H5 生态的 web-view 宿主**:
- **首页壳** `pages/index/index.vue` —— tabBar 首页,硬编码加载 `https://mp.yizhezhe.com/mama-box/`;
H5 应用宫格在 mama-box 内用 `location.href` 换应用,**本页不会重新 onLoad**。
- **容器页** `pages/annex/web_view/index.vue` —— 太阳码 / 分享落地 / 商城宫格 / JumpPath 的通用宿主。
- H5 应用(30+)只在 box_app 后台登记,本仓库没有它们的源码。
| 项 | 值 |
| --- | --- |
| 本地路径 | `/mnt/c/Users/Jone/Documents/HBuilderProjects/crmeb`(Windows:`C:\Users\Jone\Documents\HBuilderProjects\crmeb`) |
| 发布 | HBuilderX 发行 → 微信小程序上传 → 微信审核 → 发布(**没有 git remote,本仓库是纯本地库**) |
| 版本 | `manifest.json` 的 `versionName` / `versionCode` |
## 2. 关键文件
| 文件 | 作用 |
| --- | --- |
| `pages/index/index.vue` | 首页壳:身份 fail-closed、加载 mama-box、**冷启动恢复 last_app**、分享卡片、tabBar 显隐 |
| `pages/annex/web_view/index.vue` | 容器页:太阳码 scene 解析、身份注入(query)、`_reportLastApp` 上报 |
| `mixins/webviewHost.js` | 宿主公共能力:postMessage 分发(location/share/copyLink/setTitle/requestProfile)、原生授权面板、授权回传、静默登录复用 |
| `mixins/shareCard.js` | 分享图:按当前 H5 URL 反查 box_app App 表;朋友圈本地图转换 |
| `App.vue` | 冷启动静默登录、`globalData`(`spid` / `appShowAt`)、`onShow`(前台化时间戳) |
| `pages/annex/auth/index.vue` | 独立授权页(`navigateTo` 进入,成功写 `__AUTH_RESULT__` 后 `navigateBack`) |
| `pages/guide/index.vue` | `pages.json` 第一个页面(冷启动入口,无广告时 `switchTab` 到首页壳) |
## 3. 硬约定
1. **两个宿主页必须共用 mixin**:`mixins/webviewHost.js` + `mixins/shareCard.js`。历史上两页各写一份导致能力分叉(首页壳缺 copyLink / 授权回传 / unionid),别再分叉。
2. **身份注入走 query、授权结果走 hash**:容器页 `_applyIdentity()` 负责按当前 openid 重写 URL(含 `_v=` 缓存击穿);授权回传只走 hash(`#openid=&phone=&nickname=&avatar=`)——微信 web-view **首次加载丢 query、不丢 hash**。
3. **拿不到 openid 就不加载 H5**(fail-closed):重试 → 兜底屏 → 手动重试;**不要匿名加载**(H5 会进访客态,反而更难排查)。
4. **分享深链必须 `stripAuthParams()`**:别把发送者的 `openid/unionid/t` 泄露给接收者;接收者由容器页按自己身份重新注入。
5. **分享图/标题按 `onShareAppMessage(options).webViewUrl` 当场解析**:postMessage 是延迟投递,首页壳又不随 H5 换应用 onLoad,沿用上次结果会「B 应用带 A 应用的图」。
6. **tabBar 是全局状态**:首页壳必须 `uni.hideTabBar()`(web-view 盖不住原生条,H5 自带底栏会双条);**严禁在商城页主动 `showTabBar` 配对** —— 会与 DIY 底栏同显成双 tabBar。
7. **恢复/落地统一 `navigateTo` 容器页**,并保留首页壳作为返回落点;不要直接把首页壳的 web-view src 指向第三方应用(首页壳的 `buildWebUrl` 是 **hash** 注入,会破坏自带 hash 路由的应用)。
8. **只用 `options.url` 当显式落地判据**:`options.scene` **每次冷启动都有值**(1001 最近使用 / 1007 分享 / 1011 扫码…),拿它判落地会导致恢复永远不执行。
## 4. 踩坑:宿主生命周期(2026-09-22,都付过线上代价)
### 4.1 热启动 vs 冷启动
- 用户点右上角胶囊圆点「关闭」**不是销毁**,只是切后台。马上再进是**热启动**:页面栈与 web-view 都在,
**只触发 `onShow`,不触发 `onLoad`**;被回收后才是冷启动。
- 所以「退出小程序再进要回到上次的 H5 应用」这类需求,**只写在 `onLoad` 里会失败** —— 用户测的正是「关闭后马上再进」。
- 反过来,`onShow` 里**无条件重载 web-view** 会把用户从 H5 应用踢回首页。首页壳曾有一段「切回首页 tab 就重载」的老逻辑,
热启动也走它,于是把恢复好的应用冲掉(`shareHref` 因 postMessage 延迟还停在 mama-box)。
- **判据**:切 tab **不触发** `App.onShow`,热启动**会**。做法:`App.vue onShow` 记 `globalData.appShowAt`,
页面 `onShow` 里 `Date.now() - appShowAt < 2000` 视为热启动恢复 → **跳过重载**。
- 改任何 `onShow` 逻辑前,先想清楚触发来源(App 前后台切换 / 页内跳转 / 切 tab)。
### 4.2 显式落地后点「小房子」回首页,不能被恢复逻辑再送回去
- 扫码(太阳码)/ 分享落地时,用户直接落在**容器页**,页面栈只有它 → 左上角是**「小房子」**,点它回小程序首页。
- 如果这时「上次应用恢复」还生效,就会把用户又送回刚扫码的那个应用:表现为**点了小房子回不了家**,
而且因为多push了一层容器页,左上角变成**返回箭头**,得再点一次返回才真回首页(用户实测)。
- **修法两层,缺一不可**:
1. **入口判定**:`App.vue onLaunch` 用 `option.path` 置 `globalData.restoreArmed` ——
只有 `pages/index/index` / `pages/guide/index` 且无 `query.url` 才为 true;首页壳 `onLoad` 一次性消费。
(用 path 不用时间窗:引导页可能停很久看开屏广告。)
2. **抑制 H5 兜底**:这类「明确不恢复」的首页加载,以及显式落地/恢复后的返回落点,
`loadMamaBox(true)` / `_loadWithCachedIdentity` / `onShow` 重载 / `retryLoad` **都要带 `#norestore=1`**,
否则 mama-box 的兜底恢复会绕开小程序判定又把用户送走。
- 反向:小程序**尝试恢复但取数失败/超时**时**不带** `norestore`,让 mama-box 兜底重试一次。
### 4.3 回到首页要清 last_app(两侧互补,2026-09-22)
现象:进入 H5 应用 → **返回**回到 mama-box 首页 → 再冷启动进小程序,又被送回刚退出的应用。
记录的是「最后进入的 H5 应用」,人已经回到首页了 → 必须清掉。清除动作分两侧,互补覆盖:
| 路径 | 谁清 | 判定 |
| --- | --- | --- |
| H5 应用自己的返回(`history.back()` / 后退重载) | **mama-box** `clearLastAppOnBackHome()` | `pageshow.persisted` / `navigation.type === 'back_forward'` |
| H5 应用以 `location.href` 回 mama-box | **mama-box**(同上) | `document.referrer` 是 `https://mp.yizhezhe.com/<app>/` |
| 容器页**返回箭头**回首页(host 重载,不带 referrer) | **首页壳** `_clearLastApp()` | `onShow` 决定重载回 mama-box 时(`reloadBase` 命中 `/mama-box`) |
- 接口:`DELETE /api/mp/public/last-app?openid=`(幂等、fail-soft)。
- ⚠️ **绝不能改成「mama-box 每次加载都清」**:恢复流程会把 mama-box 当**返回落点**加载,
误清会毁掉刚用上的记录(实测:普通加载与 `#norestore=1` 落点加载都不清)。
- ⚠️ 首页壳只在**重载目标是 mama-box** 时清;重载目标是某个应用(`shareHref` 指向它)时不能清。
## 5. 本仓库状态提醒
- ⚠️ `mixins/shareCard.js` **未被 git 跟踪**(untracked),但两个宿主页都 import 它 —— 提交时别漏,否则一旦丢工作区这份文件就没了。
- `pages/index/index.vue.origin-bak`(及若干 `.bak_*`)是原装修首页/历史备份,**别当改动目标**,也别删(回退时要用)。
- 仓库里有多次会话遗留的未提交改动(`manifest.json` / `pages.json` / `pages/index/mall.vue` 等),动手前先 `git status` 分清哪些是本次的。
## 6. 自测
微信开发者工具(HBuilderX 运行到小程序)。**Console 过滤 `[enter-app]`** 可看到完整判定链
(入口路径 / restoreArmed / last-app 请求与响应 / 是否命中 / 热启动判定 / 容器页加载与上报):
1. **冷启动**:点「编译」→ 有 last_app 记录时应直接进上次的 H5 应用(不经过首页)。
2. **热启动**:点工具栏「切后台」再「切前台」→ 应停在原 H5,**不应重载回首页**。
3. **扫码入口**:编译模式选「通过二维码编译」(或扫太阳码)→ 进应用后点左上角「小房子」→ **应停在首页**,
不应被送回应用、也不应出现返回箭头。
4. 无记录 / 断网冷启动 → 最多约 1.5s 后照常进首页。
5. 授权页返回、分享卡片、商城宫格进应用各走一遍。
接口契约与后端验证清单见 `box_app/AGENTS.md` §5、§6。