Ver Fonte

feat: 路由拦截

王家程 há 10 meses atrás
pai
commit
44def8c4d8

+ 10 - 3
src/components/MIcon/index.vue

@@ -1,3 +1,10 @@
+<!--
+ * @Author: wjc
+ * @Date: 2024-07-01 10:05:11
+ * @LastEditors: wjc
+ * @LastEditTime: 2024-07-02 15:20:40
+ * @Description: 
+-->
 <template>
   <svg v-bind="$attrs" aria-hidden="true" class="m-icon">
     <use :xlink:href="symbolId" :fill="color" />
@@ -11,10 +18,10 @@
 
   const props = withDefaults(
     defineProps<{
-      prefix: string
+      prefix?: string
       name: string
-      color: string
-      size: number | string
+      color?: string
+      size?: number | string
     }>(),
     {
       prefix: 'icon',

+ 16 - 0
src/interceptors/index.ts

@@ -0,0 +1,16 @@
+/*
+ * @Author: wjc
+ * @Date: 2024-07-02 10:26:06
+ * @LastEditors: wjc
+ * @LastEditTime: 2024-07-02 11:34:05
+ * @Description:
+ */
+import type { App } from 'vue'
+
+import { setupRequestInterceptor } from './request'
+import { setupRouterInterceptor } from './router'
+
+export default function setupInterceptors(app: App) {
+  setupRequestInterceptor(app)
+  setupRouterInterceptor(app)
+}

+ 54 - 0
src/interceptors/request.ts

@@ -0,0 +1,54 @@
+/*
+ * @Author: wjc
+ * @Date: 2024-07-02 10:12:51
+ * @LastEditors: wjc
+ * @LastEditTime: 2024-07-02 10:25:19
+ * @Description: uni.request API 请求拦截
+ */
+import type { App } from 'vue'
+import qs from 'qs'
+import type { ILogin } from '@/models/userTypes'
+import type { IRequestOptions } from '@/models/requestTypes'
+import { useUserStore } from '@/stores/modules/userStore'
+
+const interceptor = {
+  //  请求前拦截
+  invoke(options: IRequestOptions) {
+    const userStore = useUserStore()
+    // api 处理
+    options.url = `${import.meta.env.VITE_APP_BASE_API}${options.url}`
+    // 查询参数处理
+    if (options.query) {
+      const query = qs.stringify(Object.assign({}, options.query), {
+        addQueryPrefix: true,
+      })
+      options.url += query
+    }
+    // 10 秒请求超时
+    options.timeout = 100000
+    // 请求头标识符
+    const sysInfo = uni.getSystemInfoSync()
+    const storageLoginInfo: ILogin = userStore.storageLoginInfo
+    options.header = {
+      ...options.header,
+      version: sysInfo.appVersion, // 版本号
+      platform: sysInfo.osName ?? 'app', // 所用系统
+    }
+    // entCode
+    if (storageLoginInfo && storageLoginInfo.entCode) {
+      options.header.entcode = storageLoginInfo.entCode
+    }
+    // token
+    if (userStore.token) {
+      options.header.Authorization = `Bearer ${userStore.token}`
+    }
+  },
+}
+
+export function setupRequestInterceptor(app: App) {
+  app.use({
+    install() {
+      uni.addInterceptor('request', interceptor)
+    },
+  })
+}

+ 51 - 0
src/interceptors/router.ts

@@ -0,0 +1,51 @@
+/*
+ * @Author: wjc
+ * @Date: 2024-07-02 10:16:29
+ * @LastEditors: wjc
+ * @LastEditTime: 2024-07-02 15:05:47
+ * @Description:
+ */
+import type { App } from 'vue'
+import qs from 'qs'
+import { useUserStore } from '@/stores/modules/userStore'
+
+import { pages } from '@/pages.json'
+
+// 需要拦截的页面
+const loginBlacklist = pages
+  .filter((p) => p.actions && p.actions.includes('login'))
+  .map((p) => `/${p.path}`)
+
+const interceptor = {
+  invoke({ url }) {
+    const userStore = useUserStore()
+    const token = userStore.storageUserId
+    const path = url.split('?')[0]
+
+    const isNeedLogin = loginBlacklist.includes(path)
+    console.log('244-', path, loginBlacklist, isNeedLogin)
+    // 不需要登录权限的,直接跳转
+    if (!isNeedLogin) {
+      return true
+    }
+    // 已经登录的,直接跳转
+    if (token) {
+      return true
+    }
+
+    // 访问的是需要登录权限才能查看的页面,先跳转到提示页面提醒用户
+    const redirectRoute = `/pages/auth/index?redirect=${encodeURIComponent(url)}`
+    uni.navigateTo({ url: redirectRoute })
+    return false
+  },
+}
+
+export function setupRouterInterceptor(app: App) {
+  app.use({
+    install() {
+      uni.addInterceptor('navigateTo', interceptor)
+      uni.addInterceptor('reLaunch', interceptor)
+      uni.addInterceptor('redirectTo', interceptor)
+    },
+  })
+}

+ 3 - 3
src/main.ts

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2024-05-27 11:49:45
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-01 10:13:20
+ * @LastEditTime: 2024-07-02 10:27:33
  * @Description:
  */
 import { createSSRApp } from 'vue'
@@ -12,7 +12,7 @@ import 'virtual:uno.css'
 import 'virtual:svg-icons-register'
 
 import { setupStores } from '@/stores'
-import { setupInterceptor } from '@/utils/request'
+import setupInterceptors from '@/interceptors'
 import '@/static/styles/vars.scss'
 import '@/static/styles/index.scss'
 
@@ -23,7 +23,7 @@ export function createApp() {
 
   app.use(uviewPlus)
   setupStores(app)
-  setupInterceptor(app)
+  setupInterceptors(app)
 
   return {
     app,

+ 5 - 1
src/models/requestTypes.ts

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2024-06-12 11:46:30
  * @LastEditors: wjc
- * @LastEditTime: 2024-06-12 14:24:32
+ * @LastEditTime: 2024-07-02 10:14:02
  * @Description:
  */
 export type BasicRes<T = unknown> = {
@@ -12,3 +12,7 @@ export type BasicRes<T = unknown> = {
   status: number
   statusText: string
 }
+
+export type IRequestOptions = UniApp.RequestOptions & {
+  query?: Record<string, any>
+}

+ 13 - 0
src/pages.json

@@ -63,6 +63,19 @@
 			"style": {
 				"navigationBarTitleText": "uni-app"
 			}
+		},
+		{
+			"path": "pages/auth/index",
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
+			"path": "pages/test/index",
+			"actions": ["login"],
+			"style": {
+				"navigationBarTitleText": "test page"
+			}
 		}
 	],
 	"globalStyle": {

+ 20 - 0
src/pages/auth/index.vue

@@ -0,0 +1,20 @@
+<template>
+  <view>
+    <up-modal :show="true">
+      <view class="text-18px py-24px">暂无权限访问该页面,请先登录</view>
+      <template #confirmButton>
+        <view class="flex justify-between text-center gap-20px">
+          <view class="btn-primary flex-1" @click="handleConfirm">确定</view>
+        </view>
+      </template>
+    </up-modal>
+  </view>
+</template>
+
+<script setup lang="ts">
+  defineOptions({ name: 'Auth' })
+
+  const handleConfirm = () => {
+    uni.navigateTo({ url: '/pages/login/index' })
+  }
+</script>

+ 8 - 1
src/pages/index/index.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2019-08-22 19:41:20
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-01 10:37:01
+ * @LastEditTime: 2024-07-02 11:51:16
  * @Description: 
 -->
 <template>
@@ -17,6 +17,7 @@
       <MIcon :name="icon" :size="16"></MIcon>
     </view>
     <u-button type="primary" text="确定"></u-button>
+    <u-button @click="onTest">前往 Auth 页面</u-button>
   </view>
 </template>
 
@@ -25,6 +26,12 @@
 
   const title = ref('自定义图标')
   const icon = ref('anl')
+
+  const onTest = () => {
+    uni.navigateTo({
+      url: '/pages/test/index',
+    })
+  }
 </script>
 
 <style lang="scss">

+ 6 - 1
src/pages/login/index.vue

@@ -2,7 +2,7 @@
  * @Author: wjc
  * @Date: 2024-06-06 14:51:25
  * @LastEditors: wjc
- * @LastEditTime: 2024-06-25 15:28:12
+ * @LastEditTime: 2024-07-02 15:05:10
  * @Description: 
 -->
 <template>
@@ -41,6 +41,7 @@
         <text class="font-500 color-black" @click="goPrivacy">《绘管家个人信息保护政策》</text>
       </view>
       <up-button type="primary" class="btn-primary" @click="onSubmit">登录</up-button>
+      <up-button type="text" class="mt-24px" @click="onTest">查看 Auth 页面</up-button>
     </view>
     <MFooter></MFooter>
     <PrivacyModal @privacy="handlePrivacy"></PrivacyModal>
@@ -88,6 +89,10 @@
     uni.navigateTo({ url: '/pages/privacy/index' })
   }
 
+  const onTest = () => {
+    uni.navigateTo({ url: '/pages/test/index' })
+  }
+
   const onSubmit = () => {
     formRef.value
       .validate()

+ 14 - 0
src/pages/test/index.vue

@@ -0,0 +1,14 @@
+<!--
+ * @Author: wjc
+ * @Date: 2024-07-02 10:07:46
+ * @LastEditors: wjc
+ * @LastEditTime: 2024-07-02 15:22:36
+ * @Description: 
+-->
+<template>
+  <view>需要登录才能查看的页面</view>
+</template>
+
+<script setup lang="ts">
+  defineOptions({ name: 'Auth' })
+</script>

+ 8 - 1
src/utils/request/checkStatus.ts

@@ -2,17 +2,24 @@
  * @Author: wjc
  * @Date: 2024-06-05 10:36:43
  * @LastEditors: wjc
- * @LastEditTime: 2024-06-12 16:08:43
+ * @LastEditTime: 2024-07-02 15:24:46
  * @Description:
  */
+import { useUserStore } from '@/stores/modules/userStore'
+
 export function checkStatus(err: any) {
   let errMessage = err.data.responseJSON.message
+  const userStore = useUserStore()
 
   switch (err.statusCode) {
     case 400:
       errMessage = `${errMessage}`
       break
     case 401:
+      userStore.logoutAction()
+      uni.navigateTo({
+        url: '/pages/login/index',
+      })
       errMessage = '登录超时,请重新登录'
       break
     case 403:

+ 2 - 51
src/utils/request/index.ts

@@ -2,61 +2,12 @@
  * @Author: wjc
  * @Date: 2024-06-05 10:21:23
  * @LastEditors: wjc
- * @LastEditTime: 2024-07-01 15:24:39
+ * @LastEditTime: 2024-07-02 10:15:13
  * @Description:
  */
-import type { App } from 'vue'
-import qs from 'qs'
-import type { ILogin } from '@/models/userTypes'
-import { useUserStore } from '@/stores/modules/userStore'
+import type { IRequestOptions } from '@/models/requestTypes'
 import { checkStatus } from './checkStatus'
 
-export type IRequestOptions = UniApp.RequestOptions & {
-  query?: Record<string, any>
-}
-
-const interceptor = {
-  //  请求前拦截
-  invoke(options: IRequestOptions) {
-    const userStore = useUserStore()
-    // api 处理
-    options.url = `${import.meta.env.VITE_APP_BASE_API}${options.url}`
-    // 查询参数处理
-    if (options.query) {
-      const query = qs.stringify(Object.assign({}, options.query), {
-        addQueryPrefix: true,
-      })
-      options.url += query
-    }
-    // 10 秒请求超时
-    options.timeout = 100000
-    // 请求头标识符
-    const sysInfo = uni.getSystemInfoSync()
-    const storageLoginInfo: ILogin = userStore.storageLoginInfo
-    options.header = {
-      ...options.header,
-      version: sysInfo.appVersion, // 版本号
-      platform: sysInfo.osName ?? 'app', // 所用系统
-    }
-    // entCode
-    if (storageLoginInfo && storageLoginInfo.entCode) {
-      options.header.entcode = storageLoginInfo.entCode
-    }
-    // token
-    if (userStore.token) {
-      options.header.Authorization = `Bearer ${userStore.token}`
-    }
-  },
-}
-
-export function setupInterceptor(app: App) {
-  app.use({
-    install() {
-      uni.addInterceptor('request', interceptor)
-    },
-  })
-}
-
 export const request = <T>(options: IRequestOptions) => {
   return new Promise<T>((resolve, reject) => {
     uni.request({