refactor(diy-bottom-nav): 优化 TabBar 高亮逻辑,新增来源参数记忆和页面映射规则

1. 新增 redirectLink 处理,添加 from_tab 来源参数实现 Tab 高亮记忆
2. 新增 TAB_BAR_KEYS、NO_TAB_PAGES、PAGE_TAB_MAP 统一管理 Tab 归属配置
3. 重构 checkTabBarActive 函数,增加页面精确映射和来源参数判断逻辑
4. 兼容小程序和 H5 的页面 query 参数获取
This commit is contained in:
2026-06-29 11:25:10 +08:00
parent 053a279102
commit 509b4d2238
2 changed files with 195 additions and 8 deletions

View File

@@ -128,6 +128,144 @@ export const PAGE_KEY_MAP = {
PAGE_WEBVIEW: '/pages_tool/webview/webview'
};
// ================== TabBar 高亮映射配置 ==================
// TabBar 标识(使用各 Tab 根页面的 wap_url)
export const TAB_BAR_KEYS = {
HOME: '/pages/index/index',
CATEGORY: '/pages_goods/category',
MERCHANT: '/pages_promotion/merch/merchcategory',
CART: '/pages_goods/cart',
MEMBER: '/pages_tool/member/index'
};
// 明确不归属任何 TabBar 的独立页面:进入这些页面时 TabBar 全不亮
export const NO_TAB_PAGES = new Set([
// 登录/注册/找回密码
'/pages_tool/login/login',
'/pages_tool/login/register',
'/pages_tool/login/find',
// 支付/收银流程
'/pages_tool/pay/index',
'/pages_tool/pay/wx_pay',
'/pages_tool/pay/cashier',
'/pages_tool/pay/result',
// 订单独立流程页(详情/退款/物流/评价)
'/pages_order/payment',
'/pages_order/detail',
'/pages_order/detail_local_delivery',
'/pages_order/detail_pickup',
'/pages_order/detail_point',
'/pages_order/refund',
'/pages_order/refund_detail',
'/pages_order/refund_goods_select',
'/pages_order/refund_type_select',
'/pages_order/refund_batch',
'/pages_order/logistics',
'/pages_order/activist',
'/pages_order/order/evaluate',
'/pages_order/detail_virtual',
// 表单
'/pages_tool/form/form',
'/pages_tool/form/formdata',
// 外部/特殊页面
'/pages_tool/webview/webview',
'/pages_tool/ai-chat/index',
'/pages_tool/vr/index',
'/pages_tool/cases/index',
'/pages_tool/contact/contact',
'/pages_tool/weapp/order_shipping',
'/pages_tool/file-preview/file-preview',
// 帮助/公告/文章详情
'/pages_tool/help/detail',
'/pages_tool/notice/detail',
'/pages_tool/article/detail',
// 介质材料
'/pages_tool/seal/structure',
'/pages_tool/seal/medium/detail'
]);
// 页面级 TabBar 精确归属映射(key: 页面路径, value: TabBar 根路径)
// 未在此映射中的页面,优先走 NO_TAB_PAGES 判断,其次走同分包回退逻辑
export const PAGE_TAB_MAP = {
// 首页
'/pages/index/index': TAB_BAR_KEYS.HOME,
'/pages/empty/index': TAB_BAR_KEYS.HOME,
// 分类
'/pages_goods/category': TAB_BAR_KEYS.CATEGORY,
'/pages_goods/list': TAB_BAR_KEYS.CATEGORY,
'/pages_goods/detail': TAB_BAR_KEYS.CATEGORY,
'/pages_tool/goods/search': TAB_BAR_KEYS.CATEGORY,
'/pages_tool/goods/brand': TAB_BAR_KEYS.CATEGORY,
'/pages_tool/goods/coupon': TAB_BAR_KEYS.CATEGORY,
'/pages_tool/goods/coupon_receive': TAB_BAR_KEYS.CATEGORY,
'/pages_tool/goods/evaluate': TAB_BAR_KEYS.CATEGORY,
'/pages_tool/goods/not_exist': TAB_BAR_KEYS.CATEGORY,
// 入驻企业
'/pages_promotion/merch/merchcategory': TAB_BAR_KEYS.MERCHANT,
'/pages_promotion/merch/detail': TAB_BAR_KEYS.MERCHANT,
// 购物车
'/pages_goods/cart': TAB_BAR_KEYS.CART,
// 我的(会员中心及子页面)
'/pages_tool/member/index': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/info': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/info_edit': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/address': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/address_edit': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/account': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/account_edit': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/balance': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/balance_detail': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/point': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/point_detail': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/coupon': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/level': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/level_growth_rules': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/card': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/card_buy': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/card_log': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/card_agreement': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/invite_friends': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/collection': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/footprint': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/contact': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/modify_face': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/pay_password': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/apply_withdrawal': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/withdrawal': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/withdrawal_detail': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/cancellation': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/cancelstatus': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/cancelsuccess': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/cancelrefuse': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/assets': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/signin': TAB_BAR_KEYS.MEMBER,
'/pages_tool/member/bind_mobile': TAB_BAR_KEYS.MEMBER,
// 我的频道下的其他功能页
'/pages_order/list': TAB_BAR_KEYS.MEMBER,
'/pages_tool/recharge/list': TAB_BAR_KEYS.MEMBER,
'/pages_tool/recharge/order_list': TAB_BAR_KEYS.MEMBER,
// 帮助/公告/文章列表属于首页频道
'/pages_tool/help/list': TAB_BAR_KEYS.HOME,
'/pages_tool/notice/list': TAB_BAR_KEYS.HOME,
'/pages_tool/article/list': TAB_BAR_KEYS.HOME,
// 文件列表(视业务可归首页,暂不归任何 Tab)
'/pages_tool/files/list': null
};
/**
* 根据 page_key + params 解析得到实际跳转URL
* @param {string} page_key 页面语义常量,如 PAGE_GOODS_LIST
@@ -366,9 +504,10 @@ export const isTabBarRootPage = (pagePath, tabBarList) => {
* @param {string} currentPageRoute 当前页面路径 /pages_goods/category
* @param {Object} tabBarList tabBar配置对象,用于动态判断
* @param {Object} packageEntryTab 追踪每个分包的入口Tab { 'pages_goods': '/pages_goods/category' }
* @param {Object} currentPageQuery 当前页面 query 参数,用于读取 from_tab 来源记忆
* @returns {boolean} 是否激活
*/
export const checkTabBarActive = (linkUrl, currentPageRoute, tabBarList, packageEntryTab) => {
export const checkTabBarActive = (linkUrl, currentPageRoute, tabBarList, packageEntryTab, currentPageQuery = {}) => {
// 1) 归一化 linkUrl:统一前导 '/' + 子包路径适配 + 去除 query
const normalizedLink = _normalizePath(linkUrl);
const normalizedCurrent = _normalizePath(currentPageRoute);
@@ -378,7 +517,10 @@ export const checkTabBarActive = (linkUrl, currentPageRoute, tabBarList, package
// 2) 精确匹配:当前页面就是 linkUrl 指向的页面
if (normalizedLink === normalizedCurrent) return true;
// 3) 若传入 tabBarList,使用动态判断逻辑
// 3) 独立流程页:不点亮任何 Tab
if (NO_TAB_PAGES.has(normalizedCurrent)) return false;
// 4) 若传入 tabBarList,使用动态判断逻辑
if (tabBarList && tabBarList.list && Array.isArray(tabBarList.list)) {
const tabUrls = tabBarList.list
.filter(item => item.link && item.link.wap_url)
@@ -387,13 +529,27 @@ export const checkTabBarActive = (linkUrl, currentPageRoute, tabBarList, package
// 检查当前 linkUrl 是否是合法的 tab URL
if (!tabUrls.includes(normalizedLink)) return false;
// 找出当前页面所属的 tab
const matchingTabUrl = _findMatchingTabUrl(currentPageRoute, tabBarList, packageEntryTab);
// 5) 页面级 Tab 精确绑定
const mappedTabUrl = PAGE_TAB_MAP[normalizedCurrent];
if (mappedTabUrl) {
return normalizedLink === _normalizePath(mappedTabUrl);
}
if (mappedTabUrl === null) return false;
// 当前页面若命中某个 tab,则只有该 tab 高亮
// 6) 来源记忆兜底:从哪个 Tab 跳转过来,就保持那个 Tab 高亮
const fromTab = currentPageQuery.from_tab;
if (fromTab) {
const normalizedFromTab = _normalizePath(fromTab);
if (tabUrls.includes(normalizedFromTab)) {
return normalizedLink === normalizedFromTab;
}
}
// 7) 分包入口追踪:通过 updateCurrentRoute 写入的入口 tab
const matchingTabUrl = _findMatchingTabUrl(currentPageRoute, tabBarList, packageEntryTab);
if (matchingTabUrl) return normalizedLink === matchingTabUrl;
// 4) 同分包回退:linkUrl 和当前页面在同一分包 → 高亮
// 8) 同分包回退:linkUrl 和当前页面在同一分包 → 高亮
const linkPackage = _getPackageName(normalizedLink);
const currentPackage = _getPackageName(normalizedCurrent);
return linkPackage && currentPackage && linkPackage === currentPackage;

View File

@@ -197,6 +197,7 @@ export default {
},
redirectTo(link) {
this.$emit('callback');
const redirectLink = link ? { ...link } : link;
if (link && link.wap_url) {
const decodedUrl = decodeEntity(link.wap_url);
@@ -209,9 +210,27 @@ export default {
[packageName]: adaptedUrl.split('?')[0]
});
}
// 若目标页面不是 TabBar 根页面,追加 from_tab 来源参数,
// 用于非 Tab 页面的 TabBar 高亮记忆
if (this.tabBarList && this.tabBarList.list) {
const tabUrls = this.tabBarList.list
.filter(item => item.link && item.link.wap_url)
.map(item => adaptSubpackageUrl(decodeEntity(item.link.wap_url)).split('?')[0]);
if (!tabUrls.includes(adaptedUrl.split('?')[0])) {
const currentPages = getCurrentPages();
const currentPage = currentPages[currentPages.length - 1];
const currentTab = currentPage && currentPage.route
? tabUrls.find(tabUrl => checkTabBarActive(tabUrl, '/' + currentPage.route, this.tabBarList, this.packageEntryTab))
: null;
if (currentTab) {
redirectLink.from_tab = currentTab;
}
}
}
}
this.$util.diyRedirectTo(link);
this.$util.diyRedirectTo(redirectLink);
},
verify(link) {
if (link == null || link == '' || !link.wap_url) return false;
@@ -222,11 +241,23 @@ export default {
? this._normalizeRoute(currentPage.route)
: '';
// 读取当前页面 query 参数(兼容小程序与 H5)
let currentPageQuery = {};
if (currentPage) {
if (currentPage.options) {
currentPageQuery = currentPage.options;
} else if (currentPage.$page && currentPage.$page.options) {
currentPageQuery = currentPage.$page.options;
} else if (currentPage.$route && currentPage.$route.query) {
currentPageQuery = currentPage.$route.query;
}
}
if (link.wap_url === this.$util.INDEX_PAGE_URL && this.name === 'DIY_VIEW_INDEX') {
return true;
}
return checkTabBarActive(link.wap_url, currentPageRoute, this.tabBarList, this.packageEntryTab);
return checkTabBarActive(link.wap_url, currentPageRoute, this.tabBarList, this.packageEntryTab, currentPageQuery);
}
}
};