From aa7d967422c0dd4e95eb8656bc7a3e54f4964780 Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Mon, 6 Jul 2026 16:48:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=B8=BF=E8=92=99?= =?UTF-8?q?=E5=85=83=E6=9C=8D=E5=8A=A1TabBar=E8=B7=B3=E8=BD=AC=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修复多处页面onTabItemTap事件,使用uni.reLaunch兜底鸿蒙下switchTab静默失败的问题 2. 全局混入onTabItemTap兜底未手动处理的页面 3. 优化路由跳转逻辑,针对鸿蒙环境增加switchTab失败兜底和超时强制跳转 4. 优化可选链调用和空值判断,提升代码健壮性 --- common/js/util.js | 50 +++++++++++++++- .../ns-video-player-popup.vue | 2 +- main.js | 58 ++++++++++++++----- pages/empty/index.vue | 14 +++++ pages/index/public/js/index.js | 14 +++++ pages_goods/category.vue | 2 +- 6 files changed, 120 insertions(+), 20 deletions(-) diff --git a/common/js/util.js b/common/js/util.js index 587887a..04b3255 100644 --- a/common/js/util.js +++ b/common/js/util.js @@ -590,6 +590,9 @@ export default { */ redirectTo(to, param = {}, mode = 'navigateTo') { // 1. 拆分 path 与已有 query,避免后面 indexOf('?') 误判 + // #ifdef MP-HARMONY + console.log('[navigate][HARMONY] redirectTo entry: to=', to, 'mode=', mode, 'param=', param); + // #endif const queryIdx = to.indexOf('?'); let path = queryIdx === -1 ? to : to.substring(0, queryIdx); const existingParams = {}; @@ -635,8 +638,36 @@ export default { if (url.indexOf(tabBarUrl) == 0) { // 首页使用switchTab(pages.json中注册的tabBar页面),其他tab页面使用reLaunch // reLaunch关闭所有页面再打开目标页,确保页面栈与浏览器历史一致,避免返回按钮异常 + // 鸿蒙元服务环境下系统 TabBar 点击 uni.switchTab 静默失败, 用 reLaunch 兜底 if (tabBarUrl == INDEX_PAGE_URL) { + // #ifdef MP-HARMONY + // 鸿蒙元服务环境下系统 TabBar 点击 uni.switchTab 静默失败, 用 reLaunch 兜底 + // 用不携带 query 的干净路径, 避免 HarmonyOS 对带 query 的 tabBar 跳转有兼容性问题 + const cleanUrl = url.split('?')[0]; + console.log('[navigate][HARMONY] tabBar INDEX navigate, url=', cleanUrl); + let navigated = false; + uni.switchTab({ + url: cleanUrl, + success: () => { navigated = true; console.log('[navigate][HARMONY] switchTab success:', cleanUrl); }, + fail: (err) => { + navigated = true; + console.warn('[navigate][HARMONY] switchTab fail, fallback reLaunch:', cleanUrl, err); + uni.reLaunch({ url: cleanUrl }); + } + }); + // 兜底: 500ms 后若仍无回调或未完成, 强制 reLaunch + // (鸿蒙下 switchTab 经常静默失败, success/fail 都不调用) + setTimeout(() => { + if (!navigated) { + console.warn('[navigate][HARMONY] switchTab silent fail, forcing reLaunch:', cleanUrl); + uni.reLaunch({ url: cleanUrl }); + } + }, 500); + // #endif + + // #ifndef MP-HARMONY uni.switchTab({ url }); + // #endif } else { uni.reLaunch({ url }); } @@ -660,8 +691,14 @@ export default { if (isDiyTabRoot) { const adaptedUrl = adaptSubpackageUrl(cleanUrl); const inSystemTabBar = systemTabBarList.some(sysUrl => adaptedUrl === sysUrl || cleanUrl === sysUrl); - // 首页用switchTab,其他DIY tab根页用reLaunch确保页面栈干净 - const navApi = inSystemTabBar ? (adaptedUrl === INDEX_PAGE_URL ? uni.switchTab : uni.reLaunch) : uni.reLaunch; + // 首页用switchTab,其他DIY tab根页用reLaunch确保页面栈干净 + let navApi = inSystemTabBar ? (adaptedUrl === INDEX_PAGE_URL ? uni.switchTab : uni.reLaunch) : uni.reLaunch; + + // 鸿蒙元服务环境下 uni.switchTab 有兼容性问题, 全部使用 reLaunch + // #ifdef MP-HARMONY + navApi = uni.reLaunch; + // #endif + navApi({ url, fail: (err) => { @@ -676,9 +713,18 @@ export default { switch (mode) { case 'tabbar': // 跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面。重置页面栈,仅保留 tabBar 页面 + // 鸿蒙元服务环境下 uni.switchTab 静默失败, 改用 reLaunch + // #ifdef MP-HARMONY + uni.reLaunch({ + url + }); + // #endif + + // #ifndef MP-HARMONY uni.switchTab({ url }); + // #endif break; case 'redirectTo': // 关闭当前页面,跳转到应用内的某个页面。替换栈顶页面,长度不变(上限10层) diff --git a/components/ns-video-player-popup/ns-video-player-popup.vue b/components/ns-video-player-popup/ns-video-player-popup.vue index d23e185..e698390 100644 --- a/components/ns-video-player-popup/ns-video-player-popup.vue +++ b/components/ns-video-player-popup/ns-video-player-popup.vue @@ -49,7 +49,7 @@ export default { * @type {boolean} */ isVisible() { - return this.$refs.videoPopup?.visible || false; + return Boolean(this.$refs?.videoPopup?.visible); } }, methods: { diff --git a/main.js b/main.js index 6f98650..ce970a4 100644 --- a/main.js +++ b/main.js @@ -14,22 +14,22 @@ import globalConfig from './common/js/golbalConfig.js'; import { uniStorage } from './common/js/storage.js' -import configExternal from './common/js/config-external.js' -import PrivacyManager from './common/js/privacy.js' -import splashState from './common/js/splash-state.js' - -Vue.prototype.$store = store //挂在vue - -Vue.config.productionTip = false - -Vue.prototype.$util = Util; -Vue.prototype.$api = Http; - -Vue.prototype.$langConfig = Lang; //语言包对象 -Vue.prototype.$lang = Lang.lang; //解析语言包 - -Vue.prototype.$config = Config; - +import configExternal from './common/js/config-external.js' +import PrivacyManager from './common/js/privacy.js' +import splashState from './common/js/splash-state.js' + +Vue.prototype.$store = store //挂在vue + +Vue.config.productionTip = false + +Vue.prototype.$util = Util; +Vue.prototype.$api = Http; + +Vue.prototype.$langConfig = Lang; //语言包对象 +Vue.prototype.$lang = Lang.lang; //解析语言包 + +Vue.prototype.$config = Config; + Vue.prototype.$privacy = PrivacyManager; //全局隐私管理器 @@ -52,6 +52,32 @@ Vue.mixin({ } }); +// 鸿蒙元服务环境: 全局接管 onTabItemTap, 兜底系统 TabBar 点击 +// 背景: 鸿蒙下 uni.switchTab 对系统 TabBar 点击静默失败 +// 通过全局 mixin 覆盖所有未显式定义 onTabItemTap 的页面(子包动态 tab 页等), +// 显式定义 onTabItemTap 的页面(如 pages/empty/index.vue、index.js)优先级高于此 mixin +// #ifdef MP-HARMONY +Vue.mixin({ + onTabItemTap(e) { + console.log('[TabBar][HARMONY] global onTabItemTap fired:', e); + if (!e || !e.pagePath) return; + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const currentPath = currentPage ? '/' + currentPage.route : ''; + const targetPath = e.pagePath.startsWith('/') ? e.pagePath : '/' + e.pagePath; + console.log('[TabBar][HARMONY] onTabItemTap currentPath=', currentPath, 'targetPath=', targetPath); + // 已经在目标页则不重复跳转 + if (currentPath === targetPath) return; + // 用干净路径(不带 query), 避免 HarmonyOS 对带 query 的 tabBar 跳转有兼容性问题 + uni.reLaunch({ + url: targetPath, + success: () => console.log('[TabBar][HARMONY] global reLaunch success:', targetPath), + fail: (err) => console.error('[TabBar][HARMONY] global reLaunch fail:', targetPath, err) + }); + } +}); +// #endif + App.mpType = 'app'; // 重写存储,增加前缀 diff --git a/pages/empty/index.vue b/pages/empty/index.vue index 87cedad..0ca8ca6 100644 --- a/pages/empty/index.vue +++ b/pages/empty/index.vue @@ -8,7 +8,21 @@ export default { data() { return {} + }, + // 鸿蒙元服务环境: 系统 TabBar 点击 uni.switchTab 静默失败 + // 手动接管 onTabItemTap, 用 uni.reLaunch 兜底, 确保 TabBar 跳转在鸿蒙下也能工作 + // #ifdef MP-HARMONY + onTabItemTap(e) { + if (!e || !e.pagePath) return; + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const currentPath = currentPage ? '/' + currentPage.route : ''; + const targetPath = e.pagePath.startsWith('/') ? e.pagePath : '/' + e.pagePath; + // 已经在目标页则不重复跳转 + if (currentPath === targetPath) return; + uni.reLaunch({ url: targetPath }); } + // #endif } diff --git a/pages/index/public/js/index.js b/pages/index/public/js/index.js index addebae..20a75be 100644 --- a/pages/index/public/js/index.js +++ b/pages/index/public/js/index.js @@ -7,6 +7,20 @@ export default { shopInfo: null // 店铺信息 }; }, + // 鸿蒙元服务环境: 系统 TabBar 点击 uni.switchTab 静默失败 + // 手动接管 onTabItemTap, 用 uni.reLaunch 兜底, 确保 TabBar 跳转在鸿蒙下也能工作 + // #ifdef MP-HARMONY + onTabItemTap(e) { + if (!e || !e.pagePath) return; + const pages = getCurrentPages(); + const currentPage = pages[pages.length - 1]; + const currentPath = currentPage ? '/' + currentPage.route : ''; + const targetPath = e.pagePath.startsWith('/') ? e.pagePath : '/' + e.pagePath; + // 已经在目标页则不重复跳转 + if (currentPath === targetPath) return; + uni.reLaunch({ url: targetPath }); + }, + // #endif onLoad(option) { // #ifdef H5 // H5地图选择位置回调数据 diff --git a/pages_goods/category.vue b/pages_goods/category.vue index 399b1a1..ff370db 100644 --- a/pages_goods/category.vue +++ b/pages_goods/category.vue @@ -60,7 +60,7 @@ export default { // #endif }, onUnload() { - if (!this.storeToken && this.$refs.login) this.$refs.login.cancelCompleteInfo(); + !this.storeToken && this.$refs.login?.cancelCompleteInfo?.(); }, methods: { getDiyInfo() {