美文网首页
Uniapp路由拦截(跳转登录)

Uniapp路由拦截(跳转登录)

作者: itfitness | 来源:发表于2024-07-16 15:58 被阅读0次

    效果展示

    新建interceptors文件夹

    新建文件route.ts

    /**
     * 路由拦截,通常也是登录拦截
     * 可以设置路由白名单,或者黑名单,看业务需要选哪一个
     * 我这里应为大部分都可以随便进入,所以使用黑名单
     */
    import { getNeedLoginPages, needLoginPages as _needLoginPages } from '@/utils'
    
    // TODO Check
    const loginRoute = '/pages/login/login'
    
    const isLogined = () => {
      return false
    }
    
    const isDev = true
    
    // 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
    const navigateToInterceptor = {
      // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
      invoke({ url }: { url: string }) {
        // console.log(url) // /pages/route-interceptor/index?name=feige&age=30
        const path = url.split('?')[0]
        let needLoginPages: string[] = []
        // 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
        if (isDev) {
          needLoginPages = getNeedLoginPages()
        } else {
          needLoginPages = _needLoginPages
        }
        const isNeedLogin = needLoginPages.includes(path)
        if (!isNeedLogin) {
          return true
        }
        const hasLogin = isLogined()
        if (hasLogin) {
          return true
        }
        const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
        uni.navigateTo({ url: redirectRoute })
        return false
      },
    }
    
    export const routeInterceptor = {
      install() {
        uni.addInterceptor('navigateTo', navigateToInterceptor)
        uni.addInterceptor('reLaunch', navigateToInterceptor)
        uni.addInterceptor('redirectTo', navigateToInterceptor)
        uni.addInterceptor('switchTab', navigateToInterceptor)
      },
    }
    

    新建index.ts

    export { routeInterceptor } from './route'
    

    新建utils文件夹

    新建index.ts文件

    import { pages, subPackages, tabBar } from '@/pages.json'
    
    /** 判断当前页面是否是tabbar页  */
    export const getIsTabbar = () => {
      if (!tabBar) {
        return false
      }
      if (!tabBar.list.length) {
        // 通常有tabBar的话,list不能有空,且至少有2个元素,这里其实不用处理
        return false
      }
      // getCurrentPages() 至少有1个元素,所以不再额外判断
      const lastPage = getCurrentPages().at(-1)
      const currPath = lastPage.route
      return !!tabBar.list.find((e) => e.pagePath === currPath)
    }
    
    /**
     * 获取当前页面路由的 path 路劲和 redirectPath 路径
     * path 如 ‘/pages/login/index’
     * redirectPath 如 ‘/pages/demo/base/route-interceptor’
     */
    export const currRoute = () => {
      // getCurrentPages() 至少有1个元素,所以不再额外判断
      const lastPage = getCurrentPages().at(-1)
      const currRoute = (lastPage as any).$page
      // console.log('lastPage.$page:', currRoute)
      // console.log('lastPage.$page.fullpath:', currRoute.fullPath)
      // console.log('lastPage.$page.options:', currRoute.options)
      // console.log('lastPage.options:', (lastPage as any).options)
      // 经过多端测试,只有 fullPath 靠谱,其他都不靠谱
      const { fullPath } = currRoute as { fullPath: string }
      // console.log(fullPath)
      // eg: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (小程序)
      // eg: /pages/login/index?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
      return getUrlObj(fullPath)
    }
    
    const ensureDecodeURIComponent = (url: string) => {
      if (url.startsWith('%')) {
        return ensureDecodeURIComponent(decodeURIComponent(url))
      }
      return url
    }
    /**
     * 解析 url 得到 path 和 query
     * 比如输入url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
     * 输出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
     */
    export const getUrlObj = (url: string) => {
      const [path, queryStr] = url.split('?')
      // console.log(path, queryStr)
    
      if (!queryStr) {
        return {
          path,
          query: {},
        }
      }
      const query: Record<string, string> = {}
      queryStr.split('&').forEach((item) => {
        const [key, value] = item.split('=')
        // console.log(key, value)
        query[key] = ensureDecodeURIComponent(value) // 这里需要统一 decodeURIComponent 一下,可以兼容h5和微信y
      })
      return { path, query }
    }
    /**
     * 得到所有的需要登录的pages,包括主包和分包的
     * 这里设计得通用一点,可以传递key作为判断依据,默认是 needLogin, 与 route-block 配对使用
     * 如果没有传 key,则表示所有的pages,如果传递了 key, 则表示通过 key 过滤
     */
    export const getAllPages = (key = 'needLogin') => {
      // 这里处理主包
      const mainPages = [
        ...pages
          .filter((page) => !key || page[key])
          .map((page) => ({
            ...page,
            path: `/${page.path}`,
          })),
      ]
      // 这里处理分包
      const subPages: any[] = []
      subPackages.forEach((subPageObj) => {
        // console.log(subPageObj)
        const { root } = subPageObj
    
        subPageObj.pages
          .filter((page) => !key || page[key])
          .forEach((page: { path: string } & Record<string, any>) => {
            subPages.push({
              ...page,
              path: `/${root}/${page.path}`,
            })
          })
      })
      const result = [...mainPages, ...subPages]
      // console.log(`getAllPages by ${key} result: `, result)
      return result
    }
    
    /**
     * 得到所有的需要登录的pages,包括主包和分包的
     * 只得到 path 数组
     */
    export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map((page) => page.path)
    
    /**
     * 得到所有的需要登录的pages,包括主包和分包的
     * 只得到 path 数组
     */
    export const needLoginPages: string[] = getAllPages('needLogin').map((page) => page.path)
    

    下面是pages.json文件,把需要登录的页面设置上"needLogin":true,

    {
        "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
            {
                "path": "pages/index/index",
                "style": {
                    "navigationBarTitleText": "首页"
                }
            },
            {
                "path": "pages/Mine/Mine",
                "needLogin":true,
                "style": {
                    "navigationBarTitleText": "我的",
                    "navigationStyle": "custom"
                }
            },
            {
                "path" : "pages/Waterfall/Waterfall",
                "needLogin":true,
                "style" : 
                {
                    "navigationBarTitleText" : "瀑布流"
                }
            },
            {
                "path" : "pages/login/login",
                "style" : 
                {
                    "navigationBarTitleText" : "登录"
                }
            }
        ],
        "globalStyle": {
            "navigationBarTextStyle": "black",
            "navigationBarTitleText": "uni-app",
            "navigationBarBackgroundColor": "#F8F8F8"
            // "backgroundColor": "#F8F8F8"
        },
        "uniIdRouter": {},
        "tabBar": {
            "color": "#7A7E83",
            "selectedColor": "#1296db",
            "borderStyle": "black",
            "backgroundColor": "#ffffff",
            "list": [{
                "pagePath": "pages/index/index",
                "iconPath": "static/ic_tab_home_unselect.png",
                "selectedIconPath": "static/ic_tab_home_select.png",
                "text": "首页"
            },{
                "pagePath": "pages/Waterfall/Waterfall",
                "iconPath": "static/ic_tab_home_unselect.png",
                "selectedIconPath": "static/ic_tab_home_select.png",
                "text": "瀑布流"
            }, {
                "pagePath": "pages/Mine/Mine",
                "iconPath": "static/ic_tab_mine_unselect.png",
                "selectedIconPath": "static/ic_tab_mine_select.png",
                "text": "我的"
            }]
        },
        "subPackages": []
    }
    

    main.js引入

    然后再main.js中引入监听

    import App from './App'
    import { routeInterceptor} from './interceptors'
    // #ifndef VUE3
    import Vue from 'vue'
    import './uni.promisify.adaptor'
    Vue.config.productionTip = false
    App.mpType = 'app'
    const app = new Vue({
      ...App
    })
    
    app.$mount()
    // #endif
    
    // #ifdef VUE3
    import { createSSRApp } from 'vue'
    export function createApp() {
      const app = createSSRApp(App)
      app.use(routeInterceptor)
      return {
        app
      }
    }
    // #endif
    

    相关文章

      网友评论

          本文标题:Uniapp路由拦截(跳转登录)

          本文链接:https://www.haomeiwen.com/subject/rlmkhjtx.html