修复(标签栏): 调整导航逻辑并更新文档以修复WebView原生返回按钮显示问题
1. 修改AppTabbar.vue的点击事件处理,仅对首页按钮使用Taro.redirectTo,其他按钮使用Taro.navigateTo以保留页面栈 2. 调整底部导航栏的面板和内容布局样式 3. 更新README和AGENTS.md文档,说明新的导航约定及修改原因
Showing
3 changed files
with
31 additions
and
3 deletions
| ... | @@ -5,6 +5,7 @@ | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | ||
| 6 | - 首页主链路在 `src/pages/index/`,通过 `src/api/index.js` 拉取 banner、宫格导航和图片入口;点击后要么跳内部页面,要么通过 `src/utils/webview.js` 组装参数进入通用 `WebView` 容器。 | 6 | - 首页主链路在 `src/pages/index/`,通过 `src/api/index.js` 拉取 banner、宫格导航和图片入口;点击后要么跳内部页面,要么通过 `src/utils/webview.js` 组装参数进入通用 `WebView` 容器。 |
| 7 | - 底部导航不是写死在页面里的,核心在 `src/components/AppTabbar.vue`、`src/stores/tabbar.js`、`src/api/tabbar.js` 与 `src/utils/tabbar.js`。应用启动时会预加载 tabbar 配置,`message`、`application` 和 `mine` 现在都按接口返回地址承接 WebView。 | 7 | - 底部导航不是写死在页面里的,核心在 `src/components/AppTabbar.vue`、`src/stores/tabbar.js`、`src/api/tabbar.js` 与 `src/utils/tabbar.js`。应用启动时会预加载 tabbar 配置,`message`、`application` 和 `mine` 现在都按接口返回地址承接 WebView。 |
| 8 | +- 当前底部导航的真实结构是“后台动态菜单 + 固定 `pages/webview-preview/index` 承接页”,不是“每个 tab 都有一个固定业务页”。因此不要因为菜单地址来自后台,就把这条链路改回 `redirectTo`;非 `home` 按钮需要用 `navigateTo` 保留页面栈,这样进入 `webview-preview` 后微信原生左上角返回按钮才会出现。只有小程序冷启动直接进入某个 WebView 场景时,才会因为没有上一页而天然没有原生返回按钮。 | ||
| 8 | - `src/pages/message/` 现在已经切到和导航栏“应用 / 我的”一致的 WebView 容器模式;`src/pages/message-detail/` 与 `src/api/message.js` 更适合作为旧版原生资讯列表/详情演示链路参考,不应再默认当作线上资讯主入口继续扩展。 | 9 | - `src/pages/message/` 现在已经切到和导航栏“应用 / 我的”一致的 WebView 容器模式;`src/pages/message-detail/` 与 `src/api/message.js` 更适合作为旧版原生资讯列表/详情演示链路参考,不应再默认当作线上资讯主入口继续扩展。 |
| 9 | - 支付相关目前有三类页面:`src/pages/pay-test/` 用于手工调试授权和支付参数;`src/pages/pay-confirm/` 是用户确认金额后点击支付的正式按钮页;`src/pages/pay-bridge/` 是给 H5/WebView 调起小程序支付用的桥页,负责自动授权、拉起支付、展示结果并返回上一页。 | 10 | - 支付相关目前有三类页面:`src/pages/pay-test/` 用于手工调试授权和支付参数;`src/pages/pay-confirm/` 是用户确认金额后点击支付的正式按钮页;`src/pages/pay-bridge/` 是给 H5/WebView 调起小程序支付用的桥页,负责自动授权、拉起支付、展示结果并返回上一页。 |
| 10 | - `src/pages/webview-preview/` 是通用外链承接页,`src/pages/application/`、`src/pages/mine/`、首页外链入口都会复用这类能力;`src/pages/map-guide/` 是固定地图签到 H5 页;`src/pages/auth/` 是统一授权页,不能绕过。 | 11 | - `src/pages/webview-preview/` 是通用外链承接页,`src/pages/application/`、`src/pages/mine/`、首页外链入口都会复用这类能力;`src/pages/map-guide/` 是固定地图签到 H5 页;`src/pages/auth/` 是统一授权页,不能绕过。 |
| ... | @@ -41,6 +42,8 @@ Mock 目录也有明确分工:`src/mock/index.js` 只做统一入口和分发 | ... | @@ -41,6 +42,8 @@ Mock 目录也有明确分工:`src/mock/index.js` 只做统一入口和分发 |
| 41 | ## 授权与支付链路约定 | 42 | ## 授权与支付链路约定 |
| 42 | 授权逻辑的核心在 `src/app.js`、`src/utils/authRedirect.js`、`src/utils/request.js` 与 `src/pages/auth/index`。应用启动时会优先尝试静默授权,`sessionid` 统一写入 Taro 本地缓存,并由请求拦截器动态注入到请求头;接口返回 `401` 时,会先尝试 `refreshSession` 静默续期并重放原请求,失败后再降级跳转授权页。因此除非明确重构整条链路,否则不要随意改动 `sessionid` 的存取方式、`saveCurrentPagePath` / `returnToOriginalPage` 的回跳机制、`navigateToAuth` 的防重逻辑,也不要跳过 `src/pages/auth/index` 直接在业务页硬编码授权流程。若修改分享进入、启动授权、401 重试或来源页回填逻辑,需至少手工验证一次“未授权进入页面 -> 自动或手动授权 -> 成功回跳原页面”的完整闭环。 | 43 | 授权逻辑的核心在 `src/app.js`、`src/utils/authRedirect.js`、`src/utils/request.js` 与 `src/pages/auth/index`。应用启动时会优先尝试静默授权,`sessionid` 统一写入 Taro 本地缓存,并由请求拦截器动态注入到请求头;接口返回 `401` 时,会先尝试 `refreshSession` 静默续期并重放原请求,失败后再降级跳转授权页。因此除非明确重构整条链路,否则不要随意改动 `sessionid` 的存取方式、`saveCurrentPagePath` / `returnToOriginalPage` 的回跳机制、`navigateToAuth` 的防重逻辑,也不要跳过 `src/pages/auth/index` 直接在业务页硬编码授权流程。若修改分享进入、启动授权、401 重试或来源页回填逻辑,需至少手工验证一次“未授权进入页面 -> 自动或手动授权 -> 成功回跳原页面”的完整闭环。 |
| 43 | 44 | ||
| 45 | +如果改动底部动态菜单、`webview-preview`、授权回跳或页面跳转方式,还要额外验证一次“首页 -> 底部动态菜单进入 WebView -> 如未授权则去授权页 -> 授权成功后回到原 WebView 页 -> 左上角仍可返回首页”的完整闭环。这里的关键前提是保住页面栈,所以除 `home` 外不要随手把 tab 点击改成 `redirectTo`。 | ||
| 46 | + | ||
| 44 | 支付链路现在至少有两种入口,不要再把它理解成只有一个测试桥页。第一种是当前线上主支付链路:业务页把 `order_id`、金额等参数带到 `src/pages/pay-confirm/index.vue`,用户确认后通过 `src/composables/useWechatMiniPay.js` 调用 `/srv/?a=pay`,再由请求层统一补上公共参数 `f`、`client_id`,最后由小程序侧执行 `Taro.requestPayment`。第二种是 H5/WebView 发起支付:外部页面先进入 `pages/webview-preview/index` 或 tabbar 对应的 WebView 容器,再把 `order_id` 传给 `pages/pay-bridge/index`;桥页负责检查授权状态、必要时补做静默授权、拉起支付、展示成功/取消/失败结果,并自动返回上一页。 | 47 | 支付链路现在至少有两种入口,不要再把它理解成只有一个测试桥页。第一种是当前线上主支付链路:业务页把 `order_id`、金额等参数带到 `src/pages/pay-confirm/index.vue`,用户确认后通过 `src/composables/useWechatMiniPay.js` 调用 `/srv/?a=pay`,再由请求层统一补上公共参数 `f`、`client_id`,最后由小程序侧执行 `Taro.requestPayment`。第二种是 H5/WebView 发起支付:外部页面先进入 `pages/webview-preview/index` 或 tabbar 对应的 WebView 容器,再把 `order_id` 传给 `pages/pay-bridge/index`;桥页负责检查授权状态、必要时补做静默授权、拉起支付、展示成功/取消/失败结果,并自动返回上一页。 |
| 45 | 48 | ||
| 46 | 仓库实现上,共享支付能力核心在 `src/composables/useWechatMiniPay.js` 与 `src/api/index.js`;调试入口在 `src/pages/pay-test/index.vue`,正式确认页在 `src/pages/pay-confirm/index.vue`,H5 桥接页在 `src/pages/pay-bridge/index.vue`。另一条是历史保留的通用支付封装,位于 `src/utils/wechatPay.js` 与 `src/api/wx/pay.js`,通过 `pay_id` 调用 `/srv/?a=icbc_pay_wxamp`。当前若处理支付问题,默认先看 `order_id -> useWechatMiniPay -> /srv/?a=pay` 这条主链路;其中 `f`、`client_id` 由请求层公共参数统一补齐,不要在单个支付接口 URL 上重复手写。若没有用户明确点名 `pay_id`、`wechatPay.js` 或 `src/api/wx/pay.js`,暂时不要把排查范围扩到这条历史链路,也不要顺手改它。修改支付逻辑时务必先确认当前页面接的是哪一条接口链路,不要混用 `order_id` 与 `pay_id`,也不要在未拿到后端有效支付参数时直接调用 `requestPayment`。涉及 H5/WebView 唤起支付时,应优先保持 `pages/pay-bridge` 的桥接职责与返回参数约定稳定;涉及小程序内直接支付时,应优先保持 `pages/pay-confirm` 的“展示金额 -> 用户点击 -> 调用共享支付能力”职责单一。无论改哪条链路,都需要区分成功、取消、失败三类状态,并至少在微信开发者工具或真机中验证一次“授权状态检查 -> 拉起支付 -> 返回结果展示/回跳”的流程。 | 49 | 仓库实现上,共享支付能力核心在 `src/composables/useWechatMiniPay.js` 与 `src/api/index.js`;调试入口在 `src/pages/pay-test/index.vue`,正式确认页在 `src/pages/pay-confirm/index.vue`,H5 桥接页在 `src/pages/pay-bridge/index.vue`。另一条是历史保留的通用支付封装,位于 `src/utils/wechatPay.js` 与 `src/api/wx/pay.js`,通过 `pay_id` 调用 `/srv/?a=icbc_pay_wxamp`。当前若处理支付问题,默认先看 `order_id -> useWechatMiniPay -> /srv/?a=pay` 这条主链路;其中 `f`、`client_id` 由请求层公共参数统一补齐,不要在单个支付接口 URL 上重复手写。若没有用户明确点名 `pay_id`、`wechatPay.js` 或 `src/api/wx/pay.js`,暂时不要把排查范围扩到这条历史链路,也不要顺手改它。修改支付逻辑时务必先确认当前页面接的是哪一条接口链路,不要混用 `order_id` 与 `pay_id`,也不要在未拿到后端有效支付参数时直接调用 `requestPayment`。涉及 H5/WebView 唤起支付时,应优先保持 `pages/pay-bridge` 的桥接职责与返回参数约定稳定;涉及小程序内直接支付时,应优先保持 `pages/pay-confirm` 的“展示金额 -> 用户点击 -> 调用共享支付能力”职责单一。无论改哪条链路,都需要区分成功、取消、失败三类状态,并至少在微信开发者工具或真机中验证一次“授权状态检查 -> 拉起支付 -> 返回结果展示/回跳”的流程。 | ... | ... |
| ... | @@ -209,6 +209,19 @@ export default { | ... | @@ -209,6 +209,19 @@ export default { |
| 209 | 209 | ||
| 210 | 此配置已在 `config/index.js` 中设置,请确保遵循此规范。 | 210 | 此配置已在 `config/index.js` 中设置,请确保遵循此规范。 |
| 211 | 211 | ||
| 212 | +## 🧭 动态底部导航与 WebView 承接 | ||
| 213 | + | ||
| 214 | +当前底部导航不是微信原生 tabBar,而是首页里的自定义 `AppTabbar`。除 `home` 外,其余按钮的菜单地址都来自后台 `app_menu` 配置,但最终仍然统一落到固定承接页 `pages/webview-preview/index`,再由该页把 `url` / `title` 传给 `<web-view>`。 | ||
| 215 | + | ||
| 216 | +这里有一个很重要的导航约定: | ||
| 217 | + | ||
| 218 | +- `home` 可以继续用 `redirectTo` | ||
| 219 | +- 非 `home` 的动态菜单必须用 `navigateTo` | ||
| 220 | + | ||
| 221 | +原因不是业务页是否固定,而是页面栈是否保住。只要首页点击动态菜单时用的是 `navigateTo`,`webview-preview` 就会作为新页面入栈,微信原生左上角返回按钮才能正常出现;如果改成 `redirectTo`,首页会被替换掉,进入 WebView 后就不会有原生返回按钮。 | ||
| 222 | + | ||
| 223 | +只有一种场景例外:如果小程序是冷启动后直接打开某个 WebView 页面,那么它天然没有上一页,这时即使用的是同一个承接页,也不会凭空出现原生返回按钮。这类场景需要另外设计“返回首页”兜底,不要误判成 tabbar 跳转逻辑失效。 | ||
| 224 | + | ||
| 212 | ## 🔐 认证流程 | 225 | ## 🔐 认证流程 |
| 213 | 226 | ||
| 214 | 项目内置完整的微信登录认证系统: | 227 | 项目内置完整的微信登录认证系统: |
| ... | @@ -223,6 +236,13 @@ export default { | ... | @@ -223,6 +236,13 @@ export default { |
| 223 | 236 | ||
| 224 | **重要**:当前授权链路使用的是 `/srv/?a=openid`。如果后端动作名、cookie 返回方式或公共参数发生变化,需要同时检查真实授权链路和本地 mock 授权链路是否保持一致。 | 237 | **重要**:当前授权链路使用的是 `/srv/?a=openid`。如果后端动作名、cookie 返回方式或公共参数发生变化,需要同时检查真实授权链路和本地 mock 授权链路是否保持一致。 |
| 225 | 238 | ||
| 239 | +如果你同时修改了动态菜单跳转、`webview-preview` 或授权回跳逻辑,建议至少手工验证一次这条闭环: | ||
| 240 | + | ||
| 241 | +1. 首页点击底部动态菜单进入 WebView | ||
| 242 | +2. 如未授权则跳到授权页 | ||
| 243 | +3. 授权成功后回到原来的 WebView 页面 | ||
| 244 | +4. 左上角原生返回按钮仍然可以返回首页 | ||
| 245 | + | ||
| 226 | ## 🌐 弱网/离线支持 | 246 | ## 🌐 弱网/离线支持 |
| 227 | 247 | ||
| 228 | 项目内置弱网和离线支持: | 248 | 项目内置弱网和离线支持: | ... | ... |
| ... | @@ -123,7 +123,13 @@ const handleTabClick = (item) => { | ... | @@ -123,7 +123,13 @@ const handleTabClick = (item) => { |
| 123 | return | 123 | return |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | - Taro.redirectTo({ url: targetUrl }) | 126 | + if (item?.key === 'home') { |
| 127 | + Taro.redirectTo({ url: targetUrl }) | ||
| 128 | + return | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + // 非首页按钮需要保留页面栈,这样 webview-preview 才能显示微信原生返回按钮。 | ||
| 132 | + Taro.navigateTo({ url: targetUrl }) | ||
| 127 | } | 133 | } |
| 128 | 134 | ||
| 129 | const clearScrollHintTimers = () => { | 135 | const clearScrollHintTimers = () => { |
| ... | @@ -210,7 +216,7 @@ onBeforeUnmount(() => { | ... | @@ -210,7 +216,7 @@ onBeforeUnmount(() => { |
| 210 | } | 216 | } |
| 211 | 217 | ||
| 212 | .app-tabbar__panel { | 218 | .app-tabbar__panel { |
| 213 | - height: 140rpx; | 219 | + height: 150rpx; |
| 214 | border-top: 2rpx solid rgba(166, 121, 57, 0.12); | 220 | border-top: 2rpx solid rgba(166, 121, 57, 0.12); |
| 215 | background: rgba(255, 255, 255, 0.98); | 221 | background: rgba(255, 255, 255, 0.98); |
| 216 | box-sizing: border-box; | 222 | box-sizing: border-box; |
| ... | @@ -220,7 +226,6 @@ onBeforeUnmount(() => { | ... | @@ -220,7 +226,6 @@ onBeforeUnmount(() => { |
| 220 | .app-tabbar__content { | 226 | .app-tabbar__content { |
| 221 | display: flex; | 227 | display: flex; |
| 222 | align-items: stretch; | 228 | align-items: stretch; |
| 223 | - height: 132rpx; | ||
| 224 | padding: 16rpx 28rpx 16rpx 52rpx; | 229 | padding: 16rpx 28rpx 16rpx 52rpx; |
| 225 | box-sizing: border-box; | 230 | box-sizing: border-box; |
| 226 | } | 231 | } | ... | ... |
-
Please register or login to post a comment