Nenhuma Descrição

interceptor.ts 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. import { isMp } from '@uni-helper/uni-env'
  2. /**
  3. * by 菲鸽 on 2025-08-19
  4. * 路由拦截,通常也是登录拦截
  5. * 黑、白名单的配置,请看 config.ts 文件, EXCLUDE_LOGIN_PATH_LIST
  6. */
  7. import { useTokenStore } from '@/store/token'
  8. import { isPageTabbar, tabbarStore } from '@/tabbar/store'
  9. import { getAllPages, getLastPage, HOME_PAGE, parseUrlToObj } from '@/utils/index'
  10. import { EXCLUDE_LOGIN_PATH_LIST, isNeedLoginMode, LOGIN_PAGE, LOGIN_PAGE_ENABLE_IN_MP, NOT_FOUND_PAGE } from './config'
  11. export const FG_LOG_ENABLE = false
  12. // 系统内部页面白名单(不需要检查路由存在性)
  13. const SYSTEM_INTERNAL_PATHS = [
  14. '/__uniappchooselocation', // 选择位置页面
  15. '/__uniapproute', // 路由页面
  16. '/__uniappfilepicker', // 文件选择器
  17. '/__uniappimagepicker', // 图片选择器
  18. ]
  19. export function judgeIsExcludePath(path: string) {
  20. const isDev = import.meta.env.DEV
  21. if (!isDev) {
  22. return EXCLUDE_LOGIN_PATH_LIST.includes(path)
  23. }
  24. const allExcludeLoginPages = getAllPages('excludeLoginPath') // dev 环境下,需要每次都重新获取,否则新配置就不会生效
  25. return EXCLUDE_LOGIN_PATH_LIST.includes(path) || (isDev && allExcludeLoginPages.some(page => page.path === path))
  26. }
  27. // 检查是否为系统内部页面
  28. export function isSystemInternalPath(path: string): boolean {
  29. return SYSTEM_INTERNAL_PATHS.includes(path)
  30. }
  31. // 检查路由是否存在
  32. export function isRouteExists(path: string): boolean {
  33. // 系统内部页面始终认为存在
  34. if (isSystemInternalPath(path)) {
  35. return true
  36. }
  37. const allPages = getAllPages()
  38. return allPages.some(page => page.path === path) || path === '/'
  39. }
  40. export const navigateToInterceptor = {
  41. // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
  42. // 增加对相对路径的处理,BY 网友 @ideal
  43. invoke({ url, query }: { url: string, query?: Record<string, string> }) {
  44. if (url === undefined) {
  45. return
  46. }
  47. let { path, query: _query } = parseUrlToObj(url)
  48. FG_LOG_ENABLE && console.log('\n\n路由拦截器:-------------------------------------')
  49. FG_LOG_ENABLE && console.log('路由拦截器 1: url->', url, ', query ->', query)
  50. const myQuery = { ..._query, ...query }
  51. // /pages/route-interceptor/index?name=feige&age=30
  52. FG_LOG_ENABLE && console.log('路由拦截器 2: path->', path, ', _query ->', _query)
  53. FG_LOG_ENABLE && console.log('路由拦截器 3: myQuery ->', myQuery)
  54. // 处理相对路径
  55. if (!path.startsWith('/')) {
  56. const currentPath = getLastPage()?.route || ''
  57. const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
  58. const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
  59. path = `${baseDir}/${path}`
  60. }
  61. // 系统内部页面直接放行(不检查路由存在性,不进行登录拦截)
  62. if (isSystemInternalPath(path)) {
  63. FG_LOG_ENABLE && console.log('系统内部页面,直接放行:', path)
  64. return true
  65. }
  66. // 处理路由不存在的情况
  67. if (!isRouteExists(path)) {
  68. console.warn('路由不存在:', path)
  69. uni.navigateTo({ url: NOT_FOUND_PAGE })
  70. return false // 明确表示阻止原路由继续执行
  71. }
  72. // 处理直接进入路由非首页时,tabbarIndex 不正确的问题
  73. tabbarStore.setAutoCurIdx(path)
  74. // 小程序里面使用平台自带的登录,则不走下面的逻辑
  75. if (isMp && !LOGIN_PAGE_ENABLE_IN_MP) {
  76. return true // 明确表示允许路由继续执行
  77. }
  78. const tokenStore = useTokenStore()
  79. FG_LOG_ENABLE && console.log('tokenStore.hasLogin:', tokenStore.hasLogin)
  80. // 不管黑白名单,登录了就直接去吧(但是当前不能是登录页)
  81. if (tokenStore.hasLogin) {
  82. if (path !== LOGIN_PAGE) {
  83. return true // 明确表示允许路由继续执行
  84. }
  85. else {
  86. console.log('已经登录,但是还在登录页', myQuery.redirect)
  87. const url = myQuery.redirect || HOME_PAGE
  88. if (isPageTabbar(url)) {
  89. uni.switchTab({ url })
  90. }
  91. else {
  92. uni.navigateTo({ url })
  93. }
  94. return false // 明确表示阻止原路由继续执行
  95. }
  96. }
  97. let fullPath = path
  98. if (Object.keys(myQuery).length) {
  99. fullPath += `?${Object.keys(myQuery).map(key => `${key}=${myQuery[key]}`).join('&')}`
  100. }
  101. const redirectUrl = `${LOGIN_PAGE}?redirect=${encodeURIComponent(fullPath)}`
  102. // #region 1/2 默认需要登录的情况(白名单策略) ---------------------------
  103. if (isNeedLoginMode) {
  104. // 需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示白名单,可以直接通过
  105. if (judgeIsExcludePath(path)) {
  106. return true // 明确表示允许路由继续执行
  107. }
  108. // 否则需要重定向到登录页
  109. else {
  110. if (path === LOGIN_PAGE) {
  111. return true // 明确表示允许路由继续执行
  112. }
  113. FG_LOG_ENABLE && console.log('1 isNeedLogin(白名单策略) redirectUrl:', redirectUrl)
  114. uni.navigateTo({ url: redirectUrl })
  115. return false // 明确表示阻止原路由继续执行
  116. }
  117. }
  118. // #endregion 1/2 默认需要登录的情况(白名单策略) ---------------------------
  119. // #region 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
  120. else {
  121. // 不需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示黑名单,需要重定向到登录页
  122. if (judgeIsExcludePath(path)) {
  123. FG_LOG_ENABLE && console.log('2 isNeedLogin(黑名单策略) redirectUrl:', redirectUrl)
  124. uni.navigateTo({ url: redirectUrl })
  125. return false // 修改为false,阻止原路由继续执行
  126. }
  127. return true // 明确表示允许路由继续执行
  128. }
  129. // #endregion 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
  130. },
  131. }
  132. // 针对 chooseLocation 的特殊处理
  133. export const chooseLocationInterceptor = {
  134. invoke(options: any) {
  135. // 直接放行 chooseLocation 调用
  136. FG_LOG_ENABLE && console.log('chooseLocation 调用,直接放行:', options)
  137. return true
  138. },
  139. }
  140. export const routeInterceptor = {
  141. install() {
  142. uni.addInterceptor('navigateTo', navigateToInterceptor)
  143. uni.addInterceptor('reLaunch', navigateToInterceptor)
  144. uni.addInterceptor('redirectTo', navigateToInterceptor)
  145. uni.addInterceptor('switchTab', navigateToInterceptor)
  146. // 添加 chooseLocation 的拦截器,确保直接放行
  147. uni.addInterceptor('chooseLocation', chooseLocationInterceptor)
  148. },
  149. }