const TAB_KEYS = ['nav.home', 'nav.ai', 'nav.message', 'nav.mine'] /** 与 pages.json tabBar.list[].pagePath 一致(无开头 /) */ const TAB_PAGE_PATHS = new Set([ 'pages/home/index', 'pages/ai/index', 'pages/message/index', 'pages/mine/index' ]) /** 运行时会改 TabBar 的 uni API(非 Tab 页调用会报 not TabBar page) */ const TABBAR_API_NAMES = [ 'setTabBarStyle', 'setTabBarItem', 'setTabBarBadge', 'removeTabBarBadge', 'showTabBarRedDot', 'hideTabBarRedDot', 'showTabBar', 'hideTabBar', 'setTabBarMidButton' ] /** 首次安装时缓存的原始 API(避免重复 bind 已包装函数) */ const TABBAR_RAW_APIS = {} let tabBarEntryTask = null function readUniTabPagePaths() { try { const list = (typeof __uniConfig !== 'undefined' && __uniConfig.tabBar && __uniConfig.tabBar.list) || [] list.forEach((item) => { const path = normalizePageRoute(item.pagePath || '') if (path) { TAB_PAGE_PATHS.add(path) } }) } catch (e) { // ignore } } function getPublicPathPrefixes() { const prefixes = ['bqH5/', 'bqjy/'] try { const envPath = String(import.meta.env.VITE_APP_PUBLIC_PATH || '') .trim() .replace(/^\/|\/$/g, '') if (envPath) { const withSlash = `${envPath}/` if (!prefixes.includes(withSlash)) { prefixes.unshift(withSlash) } } } catch (e) { // ignore } return prefixes } function normalizePageRoute(route) { if (!route) { return '' } let r = String(route).split('?')[0].replace(/^\//, '') if (r.endsWith('.html')) { r = r.slice(0, -5) } for (const prefix of getPublicPathPrefixes()) { if (r.startsWith(prefix)) { r = r.slice(prefix.length) } } return r } function getCurrentPageRoute() { const stack = getCurrentPages() if (stack.length) { const cur = stack[stack.length - 1] return cur.route || cur.$page?.route || cur.$page?.fullPath || '' } if (typeof window !== 'undefined' && window.location && window.location.pathname) { return window.location.pathname } return '' } function getCurrentPageMeta() { const stack = getCurrentPages() if (!stack.length) { return null } const cur = stack[stack.length - 1] return cur.$page?.meta || cur.$vm?.$page?.meta || null } /** * 路由是否对应 tabBar.list 中的页面(不等于当前栈顶一定是 Tab 上下文) */ export function isTabBarRoute(route) { const normalized = normalizePageRoute(route || getCurrentPageRoute()) return !!normalized && TAB_PAGE_PATHS.has(normalized) } /** * 当前栈顶是否为原生 TabBar 页 * 须同时满足:meta.isTabBar === true 且 route 在 tabBar.list 内 * (仅 meta 或仅 route 在 H5 生产环境都可能误判,导致 setTabBarStyle:fail) */ export function isTabBarPage() { if (!isTabBarRoute()) { return false } const meta = getCurrentPageMeta() return !!(meta && meta.isTabBar === true) } /** * H5 生产环境直开/刷新 Tab URL 时,用 switchTab 切回真正的 Tab 上下文 * @returns {Promise} 切换后是否为 TabBar 页 */ export function ensureTabBarEntry() { if (isTabBarPage()) { return Promise.resolve(true) } if (!isTabBarRoute()) { return Promise.resolve(false) } if (tabBarEntryTask) { return tabBarEntryTask } const route = normalizePageRoute(getCurrentPageRoute()) tabBarEntryTask = new Promise((resolve) => { uni.switchTab({ url: `/${route}`, complete: () => { tabBarEntryTask = null resolve(isTabBarPage()) } }) }) return tabBarEntryTask } function noopTabBarResult(name) { return { errMsg: `${name}:ok` } } function finishNoopTabBarCall(name, options) { const result = noopTabBarResult(name) if (options && typeof options.success === 'function') { options.success(result) } if (options && typeof options.complete === 'function') { options.complete(result) } return Promise.resolve(result) } function isTabBarFailMsg(msg) { return String(msg || '').includes('TabBar') && String(msg || '').includes('fail') } function rememberRawTabBarApi(target, name) { if (TABBAR_RAW_APIS[name]) { return TABBAR_RAW_APIS[name] } const fn = target[name] if (typeof fn !== 'function') { return null } if (fn._tabBarGuardRaw) { TABBAR_RAW_APIS[name] = fn._tabBarGuardRaw return TABBAR_RAW_APIS[name] } TABBAR_RAW_APIS[name] = fn.bind(target) return TABBAR_RAW_APIS[name] } /** * H5 生产包中框架 / uview 可能在我们安装守卫后又写回 uni API,故每次启动 / 切页都重装 */ function wrapTabBarApi(target, name) { if (!target || typeof target[name] !== 'function') { return } const raw = rememberRawTabBarApi(target, name) if (!raw) { return } function patchedTabBarApi(options) { const opts = options || {} if (!isTabBarPage()) { return finishNoopTabBarCall(name, opts) } try { const ret = raw(opts) if (ret && typeof ret.catch === 'function') { ret.catch((err) => { if (isTabBarFailMsg(err && err.errMsg)) { return finishNoopTabBarCall(name, opts) } throw err }) } if (ret && typeof ret.then === 'function') { return ret.then( (res) => { if (res && isTabBarFailMsg(res.errMsg)) { return noopTabBarResult(name) } return res }, (err) => { if (isTabBarFailMsg(err && err.errMsg)) { return noopTabBarResult(name) } throw err } ) } return ret } catch (err) { if (isTabBarFailMsg(err && err.errMsg)) { return finishNoopTabBarCall(name, opts) } throw err } } patchedTabBarApi._tabBarGuardPatched = true patchedTabBarApi._tabBarGuardRaw = raw target[name] = patchedTabBarApi } function patchTabBarApis(target) { if (!target) { return } TABBAR_API_NAMES.forEach((name) => wrapTabBarApi(target, name)) } function installTabBarInterceptors() { if (typeof uni === 'undefined' || !uni.addInterceptor) { return } TABBAR_API_NAMES.forEach((name) => { if (typeof uni[name] !== 'function') { return } uni.addInterceptor(name, { invoke() { if (!isTabBarPage()) { return false } } }) }) } export function installTabBarApiGuard() { readUniTabPagePaths() if (typeof uni !== 'undefined') { patchTabBarApis(uni) installTabBarInterceptors() } if (typeof wx !== 'undefined') { patchTabBarApis(wx) } } installTabBarApiGuard() /** * 原生 tabBar 的 text 不能写进 pages.json 的 i18n,需在运行时按当前语言更新(仅 Tab 页) * @param {(key: string) => string} t 如 (k) => this.$t(k) */ export function syncTabBarText(t) { if (!isTabBarPage()) { return } TAB_KEYS.forEach((key, index) => { uni.setTabBarItem({ index, text: t(key) }) }) }