| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293 |
- 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<boolean>} 切换后是否为 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)
- })
- })
- }
|