Files
lgp-wx-plus/utils/tabbar.js
2026-08-19 08:17:18 +08:00

204 lines
5.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 底栏路由对齐:高亮只认当前页 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)
}