204 lines
5.9 KiB
JavaScript
204 lines
5.9 KiB
JavaScript
/**
|
||
* 底栏路由对齐:高亮只认当前页 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)
|
||
}
|