/** * 底栏路由对齐:高亮只认当前页 path * 动画优先打到官方 custom-tab-bar 单例,页内 ThemeTabBar 只作占位/兜底 * 真机用本地 PNG:微信会丢掉内联 SVG mask,只剩色块 * 套餐项受 features.package_enabled 控制,pages.json 仍声明 4 个 tab */ import { getThemeFeatures } from '@/utils/theme' export const ALL_TAB_PAGES = [ { pagePath: 'pages/goods/index', text: '首页', key: 'home', icon: '/static/tabbar/v2/home.png', iconOn: '/static/tabbar/v2/home-fill.png' }, { pagePath: 'pages/package/index', text: '套餐', key: 'package', icon: '/static/tabbar/v2/package.png', iconOn: '/static/tabbar/v2/package-fill.png' }, { pagePath: 'pages/order/list', text: '订单', key: 'order', icon: '/static/tabbar/v2/order.png', iconOn: '/static/tabbar/v2/order-fill.png' }, { pagePath: 'pages/mine/mine', text: '我', key: 'mine', icon: '/static/tabbar/v2/mine.png', iconOn: '/static/tabbar/v2/mine-fill.png' }, ] /** 兼容旧引用:默认全量;运行时请用 getTabPages() */ export const TAB_PAGES = ALL_TAB_PAGES /** 按应用开关过滤自定义底栏,关套餐时回到 3 项 */ export function getTabPages() { const enabled = getThemeFeatures().package_enabled return ALL_TAB_PAGES.filter((item) => item.key !== 'package' || enabled) } let nativeBar = null let pageBar = null const barsByRoute = {} let pendingIndex = -1 let lastSelected = 0 export function getLastSelected() { return lastSelected } export function setLastSelected(index) { if (index >= 0) lastSelected = index } export function normalizeRoute(route) { return String(route || '').replace(/^\//, '').split('?')[0] } export function currentRoute() { const pages = getCurrentPages() const last = pages[pages.length - 1] return last ? normalizeRoute(last.route) : '' } export function currentTabIndex() { const path = currentRoute() return getTabPages().findIndex((item) => item.pagePath === path) } export function registerNativeTabBar(vm) { nativeBar = vm } export function unregisterNativeTabBar(vm) { if (nativeBar === vm) nativeBar = null } export function peekNativeBar() { return nativeBar } export function registerPageTabBar(vm) { pageBar = vm } export function unregisterPageTabBar(vm) { if (pageBar === vm) pageBar = null Object.keys(barsByRoute).forEach((key) => { if (barsByRoute[key] === vm) delete barsByRoute[key] }) } /** Vue3 官方栏经常包一层 $vm,统一拆到带 syncFromRoute 的实例 */ export function unwrapTabBar(bar) { if (!bar) return null if (typeof bar.syncFromRoute === 'function') return bar if (bar.$vm && typeof bar.$vm.syncFromRoute === 'function') return bar.$vm const refs = (bar.$refs) || (bar.$vm && bar.$vm.$refs) || {} const child = refs.bar if (child && typeof child.syncFromRoute === 'function') return child if (child && child.$vm && typeof child.$vm.syncFromRoute === 'function') return child.$vm return null } /** 优先官方单例,没有再拿当前页那一份 */ export function resolvePageTabBar(ctx) { if (nativeBar && typeof nativeBar.syncFromRoute === 'function') return nativeBar try { if (ctx && typeof ctx.getTabBar === 'function') { const official = unwrapTabBar(ctx.getTabBar()) if (official) { nativeBar = official return official } } } catch (err) { /* 官方栏未就绪 */ } let bar = null const refs = ctx && ctx.$refs ? ctx.$refs : {} bar = refs.themeTabBar if (Array.isArray(bar)) bar = bar[0] bar = unwrapTabBar(bar) if (!bar) { try { if (ctx && typeof ctx.selectComponent === 'function') { bar = unwrapTabBar(ctx.selectComponent('#themeTabBar') || ctx.selectComponent('.theme-tab-bar')) } } catch (err) { bar = null } } const path = currentRoute() if (!bar) bar = unwrapTabBar((path && barsByRoute[path]) || pageBar) if (bar) { pageBar = bar if (path) barsByRoute[path] = bar return bar } return null } export function setPendingTab(index) { pendingIndex = index } export function peekPendingTab() { return pendingIndex } export function consumePendingTab() { const value = pendingIndex pendingIndex = -1 return value } /** * 点底栏:记下起点和目标。不要在 switchTab complete 里清掉, * 微信 complete 经常在旧页还是栈顶时就触发,清掉就播不出滑动。 */ export function markTabTap(fromIndex, toIndex) { if (fromIndex >= 0) lastSelected = fromIndex pendingIndex = toIndex } /** 目标页对上对齐才算这次点击,并吃掉,避免 onShow 再滑一次 */ export function takeTabTap(index) { if (pendingIndex !== index) return false pendingIndex = -1 return true } /** switchTab 失败:清掉点击,栏保持旧高亮 */ export function clearTabTap() { pendingIndex = -1 } /** * 官方栏已是 custom:true,再调 hideTabBar 会抛 * hideTabBar:fail custom Tabbar,这里刻意空实现 */ export function hideNativeTabBar() {} /** * 把底栏 selected 对齐到栈顶路由 * 先打官方单例,保证胶囊/圆点是同一颗在滑 */ export function syncTabBarByRoute(ctx, extra) { const selected = currentTabIndex() if (selected < 0) return const apply = (bar) => { const vm = unwrapTabBar(bar) if (!vm) return false if (typeof vm.syncFromRoute === 'function') { vm.syncFromRoute(selected, extra || {}) return true } if (typeof vm.setData === 'function') { vm.setData({ selected }) return true } return false } if (apply(resolvePageTabBar(ctx))) return try { if (apply(ctx && typeof ctx.getTabBar === 'function' ? ctx.getTabBar() : null)) return } catch (err) { /* 官方栏未就绪 */ } setTimeout(() => { try { apply(ctx && typeof ctx.getTabBar === 'function' ? ctx.getTabBar() : null) apply(resolvePageTabBar(ctx)) } catch (err) { /* 重试失败忽略 */ } }, 80) }