refactor: 重构底部导航高亮逻辑,新增跨平台兼容与工具脚本

1. 抽离底部导航归属逻辑为纯函数resolveEntryTab,优化分包页面高亮规则,对齐iOS HIG规范
2. 新增页面Tab通配符映射,简化PAGE_TAB_MAP维护
3. 修复鸿蒙平台webview返回按钮适配问题
4. 新增pages.json校验与导航标题自动注入脚本
5. 优化启动页动画性能,提升合成渲染效率
6. 修复diy-picture组件v-for缺少key的警告
7. 清理鸿蒙平台多余的分包构建配置
8. 更新统计数据与store注释说明
This commit is contained in:
2026-07-16 11:47:29 +08:00
parent 165edc38ee
commit 55a9ed5946
15 changed files with 2011 additions and 171 deletions

View File

@@ -45,6 +45,7 @@
<script>
import { adaptSubpackageUrl, checkTabBarActive, decodeEntity } from '@/common/js/util.js'
import { resolveEntryTab } from '@/common/js/util-tabbar.js'
import DiyMinx from './minx.js'
// 底部导航栏
export default {
@@ -103,6 +104,11 @@ export default {
packageEntryTab() {
return this.$store.state.packageEntryTab;
},
// 响应 tabBarList 异步加载: 后端返回前 updateCurrentRoute 是 no-op,
// tabBarList 就绪后必须重跑一次, 否则冷启动+秒点链接会出现"先不高亮→再突然高亮"
tabBarReady() {
return this.tabBarList && this.tabBarList.list && this.tabBarList.list.length > 0;
},
},
watch: {
cartChange: function (nval, oval) {
@@ -118,7 +124,17 @@ export default {
this.cartAnimation = animation.export();
}, 300);
}
}
},
// tabBarList 异步加载完成后, 补一次 updateCurrentRoute
// 修复: 冷启动时用户在 tabBarList 就绪前点击链接, 第一次 updateCurrentRoute 是 no-op
tabBarReady: {
handler(ready) {
if (ready) {
this.updateCurrentRoute();
}
},
immediate: false,
},
},
methods: {
// 统一归一化路径:currentPage.route 不带前导 '/',但 tab 的 wap_url 带前导 '/'
@@ -145,45 +161,28 @@ export default {
// 归一化:补全前导 '/',确保后续 split('/') 后 parts[1]=分包名
const newRoute = this._normalizeRoute(currentPage.route);
const newRouteParts = newRoute.split('?')[0].split('/');
const currentPackageName = newRouteParts[1] || '';
const currentPackageName = newRoute.split('?')[0].split('/')[1] || '';
if (this.tabBarList && this.tabBarList.list && currentPackageName) {
const tabUrls = this.tabBarList.list
.filter(item => item.link && item.link.wap_url)
.map(item => adaptSubpackageUrl(item.link.wap_url).split('?')[0]);
// 收集历史栈路由 (排除当前页, 已 _normalizeRoute)
// 注意: 组件里直接传 raw route 给 resolveEntryTab,
// 内部 adaptSubpackageUrl 会兜底处理老路径 /pages/member/ 等
const historyRoutes = currentPages.length >= 2
? currentPages.slice(0, -1).map((p) =>
p && p.route ? this._normalizeRoute(p.route) : null)
: [];
// 1) 当前页面精确命中某个 tab → 该 tab 就是入口
let entryTab = tabUrls.find(tabUrl => tabUrl === newRoute.split('?')[0]);
// 2) 当前页面在同一分包内 → 用分包名匹配
if (!entryTab) {
entryTab = tabUrls.find(tabUrl => {
const tabParts = tabUrl.split('/');
return tabParts[1] === currentPackageName;
});
}
// 3) 回退到上一页的分包匹配
if (!entryTab) {
const prevPage = currentPages[currentPages.length - 2];
if (prevPage && prevPage.route) {
const prevParts = this._normalizeRoute(prevPage.route).split('?')[0].split('/');
const prevPackageName = prevParts[1] || '';
entryTab = tabUrls.find(tabUrl => {
const tabParts = tabUrl.split('/');
return tabParts[1] === prevPackageName;
});
if (entryTab && prevPackageName !== currentPackageName) {
// 上一页在别的分包 → 只记录到上一页的分包,不污染当前分包
this.$store.commit('setPackageEntryTab', {
...this.packageEntryTab,
[prevPackageName]: entryTab
});
entryTab = null;
}
}
}
// 调用纯函数 resolveEntryTab 完成 4 步查找
// 规则: 从哪个 TabBar 出发, 非 tab 页就归属哪个 TabBar 高亮
// 修复: home → diy 微页面 等跨分包导航被"同分包第一个 tab"误判的问题
const entryTab = resolveEntryTab({
currentPageRoute: newRoute,
tabBarList: this.tabBarList,
historyRoutes,
// 传 NO_TAB_PAGES 进去, 让 login/pay/AI 等 modal 页不参与归属判定
// 例子: home → login → diy 应算作 home → diy (login 是 modal)
noTabPages: this.$util && this.$util.NO_TAB_PAGES ? this.$util.NO_TAB_PAGES : null,
});
if (entryTab) {
this.$store.commit('setPackageEntryTab', {
@@ -253,8 +252,17 @@ export default {
}
}
if (link.wap_url === this.$util.INDEX_PAGE_URL && this.name === 'DIY_VIEW_INDEX') {
return true;
if (this.name === 'DIY_VIEW_INDEX') {
// DIY_VIEW_INDEX 短路: 当 name 是 DIY_VIEW_INDEX 时, 任意匹配的 tab 都高亮
// 修复前: 只对 HOME (INDEX_PAGE_URL) 生效
// 修复后: 适用于所有 tab 页面 (如 merchant DIY 首页、cart DIY 首页 等)
// 前提: 当前页面本身就是该 tab 根, 链接 == 当前页 → step 1 早就命中
// 这个短路主要兜底: "DIY_VIEW_INDEX 页面 + tab 链接" 的强语义关联
const linkUrl = adaptSubpackageUrl(link.wap_url).split('?')[0];
const currentUrl = currentPageRoute.split('?')[0];
if (linkUrl === currentUrl) {
return true;
}
}
return checkTabBarActive(link.wap_url, currentPageRoute, this.tabBarList, this.packageEntryTab, currentPageQuery);

View File

@@ -1,7 +1,7 @@
<template>
<view data-component-name="diy-picture" class="diy-picture" :style="style">
<view class="fui-picture">
<view v-for="(item, index) in value.list" style="line-height: 0;">
<view v-for="(item, index) in value.list" :key="index" style="line-height: 0;">
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)"
v-if="item.link.wap_url" @tap.stop="handlerClick(item)" show-menu-by-longpress></image>
<image mode="widthFix" style="width: 100%;height:auto" :src="$util.img(item.imageUrl)" v-else