西藏巴青项目

tabBar.js 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. const TAB_KEYS = ['nav.home', 'nav.ai', 'nav.message', 'nav.mine']
  2. /** 与 pages.json tabBar.list[].pagePath 一致(无开头 /) */
  3. const TAB_PAGE_PATHS = new Set([
  4. 'pages/home/index',
  5. 'pages/ai/index',
  6. 'pages/message/index',
  7. 'pages/mine/index'
  8. ])
  9. /** 运行时会改 TabBar 的 uni API(非 Tab 页调用会报 not TabBar page) */
  10. const TABBAR_API_NAMES = [
  11. 'setTabBarStyle',
  12. 'setTabBarItem',
  13. 'setTabBarBadge',
  14. 'removeTabBarBadge',
  15. 'showTabBarRedDot',
  16. 'hideTabBarRedDot',
  17. 'showTabBar',
  18. 'hideTabBar',
  19. 'setTabBarMidButton'
  20. ]
  21. /** 首次安装时缓存的原始 API(避免重复 bind 已包装函数) */
  22. const TABBAR_RAW_APIS = {}
  23. let tabBarEntryTask = null
  24. function readUniTabPagePaths() {
  25. try {
  26. const list =
  27. (typeof __uniConfig !== 'undefined' &&
  28. __uniConfig.tabBar &&
  29. __uniConfig.tabBar.list) ||
  30. []
  31. list.forEach((item) => {
  32. const path = normalizePageRoute(item.pagePath || '')
  33. if (path) {
  34. TAB_PAGE_PATHS.add(path)
  35. }
  36. })
  37. } catch (e) {
  38. // ignore
  39. }
  40. }
  41. function getPublicPathPrefixes() {
  42. const prefixes = ['bqH5/', 'bqjy/']
  43. try {
  44. const envPath = String(import.meta.env.VITE_APP_PUBLIC_PATH || '')
  45. .trim()
  46. .replace(/^\/|\/$/g, '')
  47. if (envPath) {
  48. const withSlash = `${envPath}/`
  49. if (!prefixes.includes(withSlash)) {
  50. prefixes.unshift(withSlash)
  51. }
  52. }
  53. } catch (e) {
  54. // ignore
  55. }
  56. return prefixes
  57. }
  58. function normalizePageRoute(route) {
  59. if (!route) {
  60. return ''
  61. }
  62. let r = String(route).split('?')[0].replace(/^\//, '')
  63. if (r.endsWith('.html')) {
  64. r = r.slice(0, -5)
  65. }
  66. for (const prefix of getPublicPathPrefixes()) {
  67. if (r.startsWith(prefix)) {
  68. r = r.slice(prefix.length)
  69. }
  70. }
  71. return r
  72. }
  73. function getCurrentPageRoute() {
  74. const stack = getCurrentPages()
  75. if (stack.length) {
  76. const cur = stack[stack.length - 1]
  77. return cur.route || cur.$page?.route || cur.$page?.fullPath || ''
  78. }
  79. if (typeof window !== 'undefined' && window.location && window.location.pathname) {
  80. return window.location.pathname
  81. }
  82. return ''
  83. }
  84. function getCurrentPageMeta() {
  85. const stack = getCurrentPages()
  86. if (!stack.length) {
  87. return null
  88. }
  89. const cur = stack[stack.length - 1]
  90. return cur.$page?.meta || cur.$vm?.$page?.meta || null
  91. }
  92. /**
  93. * 路由是否对应 tabBar.list 中的页面(不等于当前栈顶一定是 Tab 上下文)
  94. */
  95. export function isTabBarRoute(route) {
  96. const normalized = normalizePageRoute(route || getCurrentPageRoute())
  97. return !!normalized && TAB_PAGE_PATHS.has(normalized)
  98. }
  99. /**
  100. * 当前栈顶是否为原生 TabBar 页
  101. * 须同时满足:meta.isTabBar === true 且 route 在 tabBar.list 内
  102. * (仅 meta 或仅 route 在 H5 生产环境都可能误判,导致 setTabBarStyle:fail)
  103. */
  104. export function isTabBarPage() {
  105. if (!isTabBarRoute()) {
  106. return false
  107. }
  108. const meta = getCurrentPageMeta()
  109. return !!(meta && meta.isTabBar === true)
  110. }
  111. /**
  112. * H5 生产环境直开/刷新 Tab URL 时,用 switchTab 切回真正的 Tab 上下文
  113. * @returns {Promise<boolean>} 切换后是否为 TabBar 页
  114. */
  115. export function ensureTabBarEntry() {
  116. if (isTabBarPage()) {
  117. return Promise.resolve(true)
  118. }
  119. if (!isTabBarRoute()) {
  120. return Promise.resolve(false)
  121. }
  122. if (tabBarEntryTask) {
  123. return tabBarEntryTask
  124. }
  125. const route = normalizePageRoute(getCurrentPageRoute())
  126. tabBarEntryTask = new Promise((resolve) => {
  127. uni.switchTab({
  128. url: `/${route}`,
  129. complete: () => {
  130. tabBarEntryTask = null
  131. resolve(isTabBarPage())
  132. }
  133. })
  134. })
  135. return tabBarEntryTask
  136. }
  137. function noopTabBarResult(name) {
  138. return { errMsg: `${name}:ok` }
  139. }
  140. function finishNoopTabBarCall(name, options) {
  141. const result = noopTabBarResult(name)
  142. if (options && typeof options.success === 'function') {
  143. options.success(result)
  144. }
  145. if (options && typeof options.complete === 'function') {
  146. options.complete(result)
  147. }
  148. return Promise.resolve(result)
  149. }
  150. function isTabBarFailMsg(msg) {
  151. return String(msg || '').includes('TabBar') && String(msg || '').includes('fail')
  152. }
  153. function rememberRawTabBarApi(target, name) {
  154. if (TABBAR_RAW_APIS[name]) {
  155. return TABBAR_RAW_APIS[name]
  156. }
  157. const fn = target[name]
  158. if (typeof fn !== 'function') {
  159. return null
  160. }
  161. if (fn._tabBarGuardRaw) {
  162. TABBAR_RAW_APIS[name] = fn._tabBarGuardRaw
  163. return TABBAR_RAW_APIS[name]
  164. }
  165. TABBAR_RAW_APIS[name] = fn.bind(target)
  166. return TABBAR_RAW_APIS[name]
  167. }
  168. /**
  169. * H5 生产包中框架 / uview 可能在我们安装守卫后又写回 uni API,故每次启动 / 切页都重装
  170. */
  171. function wrapTabBarApi(target, name) {
  172. if (!target || typeof target[name] !== 'function') {
  173. return
  174. }
  175. const raw = rememberRawTabBarApi(target, name)
  176. if (!raw) {
  177. return
  178. }
  179. function patchedTabBarApi(options) {
  180. const opts = options || {}
  181. if (!isTabBarPage()) {
  182. return finishNoopTabBarCall(name, opts)
  183. }
  184. try {
  185. const ret = raw(opts)
  186. if (ret && typeof ret.catch === 'function') {
  187. ret.catch((err) => {
  188. if (isTabBarFailMsg(err && err.errMsg)) {
  189. return finishNoopTabBarCall(name, opts)
  190. }
  191. throw err
  192. })
  193. }
  194. if (ret && typeof ret.then === 'function') {
  195. return ret.then(
  196. (res) => {
  197. if (res && isTabBarFailMsg(res.errMsg)) {
  198. return noopTabBarResult(name)
  199. }
  200. return res
  201. },
  202. (err) => {
  203. if (isTabBarFailMsg(err && err.errMsg)) {
  204. return noopTabBarResult(name)
  205. }
  206. throw err
  207. }
  208. )
  209. }
  210. return ret
  211. } catch (err) {
  212. if (isTabBarFailMsg(err && err.errMsg)) {
  213. return finishNoopTabBarCall(name, opts)
  214. }
  215. throw err
  216. }
  217. }
  218. patchedTabBarApi._tabBarGuardPatched = true
  219. patchedTabBarApi._tabBarGuardRaw = raw
  220. target[name] = patchedTabBarApi
  221. }
  222. function patchTabBarApis(target) {
  223. if (!target) {
  224. return
  225. }
  226. TABBAR_API_NAMES.forEach((name) => wrapTabBarApi(target, name))
  227. }
  228. function installTabBarInterceptors() {
  229. if (typeof uni === 'undefined' || !uni.addInterceptor) {
  230. return
  231. }
  232. TABBAR_API_NAMES.forEach((name) => {
  233. if (typeof uni[name] !== 'function') {
  234. return
  235. }
  236. uni.addInterceptor(name, {
  237. invoke() {
  238. if (!isTabBarPage()) {
  239. return false
  240. }
  241. }
  242. })
  243. })
  244. }
  245. export function installTabBarApiGuard() {
  246. readUniTabPagePaths()
  247. if (typeof uni !== 'undefined') {
  248. patchTabBarApis(uni)
  249. installTabBarInterceptors()
  250. }
  251. if (typeof wx !== 'undefined') {
  252. patchTabBarApis(wx)
  253. }
  254. }
  255. installTabBarApiGuard()
  256. /**
  257. * 原生 tabBar 的 text 不能写进 pages.json 的 i18n,需在运行时按当前语言更新(仅 Tab 页)
  258. * @param {(key: string) => string} t 如 (k) => this.$t(k)
  259. */
  260. export function syncTabBarText(t) {
  261. if (!isTabBarPage()) {
  262. return
  263. }
  264. TAB_KEYS.forEach((key, index) => {
  265. uni.setTabBarItem({
  266. index,
  267. text: t(key)
  268. })
  269. })
  270. }