Explorar o código

refactor(user): 合并用户信息和token接口并优化登录逻辑

重构用户信息存储结构,将IUserTokenVo合并到IUserInfoVo中
移除冗余的userToken状态管理,统一使用userInfo存储token信息
优化路由拦截器和登录页面处理逻辑,增加query参数解析功能
修改默认登录策略为需要登录
清理me页面中冗余的登录状态检查逻辑
feige996 hai 8 meses
pai
achega
210a77aca6

+ 0 - 6
src/api/types/login.ts

@@ -5,12 +5,6 @@ export interface IUserInfoVo {
   id: number
   id: number
   username: string
   username: string
   avatar: string
   avatar: string
-}
-
-/**
- * 用户token
- */
-export interface IUserTokenVo {
   token: string
   token: string
   refreshToken?: string
   refreshToken?: string
   refreshExpire?: number
   refreshExpire?: number

+ 1 - 1
src/http/http.ts

@@ -35,7 +35,7 @@ export function http<T>(options: CustomRequestOptions) {
             return reject(res)
             return reject(res)
           }
           }
           /* -------- 无感刷新 token ----------- */
           /* -------- 无感刷新 token ----------- */
-          const { refreshToken } = store.userToken || {}
+          const { refreshToken } = store.userInfo || {}
           // token 失效的,且有刷新 token 的,才放到请求队列里
           // token 失效的,且有刷新 token 的,才放到请求队列里
           if ((res.statusCode === 401 || resData.code === 401) && refreshToken) {
           if ((res.statusCode === 401 || resData.code === 401) && refreshToken) {
             taskQueue.push(() => {
             taskQueue.push(() => {

+ 1 - 1
src/http/interceptor.ts

@@ -48,7 +48,7 @@ const httpInterceptor = {
     }
     }
     // 3. 添加 token 请求头标识
     // 3. 添加 token 请求头标识
     const userStore = useUserStore()
     const userStore = useUserStore()
-    const { token } = userStore.userToken as unknown as IUserToken
+    const { token } = userStore.userInfo as unknown as IUserToken
     if (token) {
     if (token) {
       options.header.Authorization = `Bearer ${token}`
       options.header.Authorization = `Bearer ${token}`
     }
     }

+ 1 - 1
src/login/config.ts

@@ -3,7 +3,7 @@ export const LOGIN_STRATEGY_MAP = {
   DEFAULT_NEED_LOGIN: 1, // 白名单策略,默认不可以进入APP,需要强制登录
   DEFAULT_NEED_LOGIN: 1, // 白名单策略,默认不可以进入APP,需要强制登录
 }
 }
 // 登录策略,默认使用`无需登录策略`,即默认不需要登录就可以访问
 // 登录策略,默认使用`无需登录策略`,即默认不需要登录就可以访问
-export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.DEFAULT_NO_NEED_LOGIN
+export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
 export const isNeedLogin = LOGIN_STRATEGY === LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
 export const isNeedLogin = LOGIN_STRATEGY === LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
 
 
 export const LOGIN_PAGE = '/pages/login/login'
 export const LOGIN_PAGE = '/pages/login/login'

+ 9 - 4
src/pages/login/login.vue

@@ -8,6 +8,7 @@
 </route>
 </route>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
+import { parseRouteStr } from '@/router/queryString'
 import { useUserStore } from '@/store/user'
 import { useUserStore } from '@/store/user'
 import { tabbarList } from '@/tabbar/config'
 import { tabbarList } from '@/tabbar/config'
 import { isPageTabbar } from '@/tabbar/store'
 import { isPageTabbar } from '@/tabbar/store'
@@ -15,13 +16,14 @@ import { ensureDecodeURIComponent } from '@/utils'
 
 
 const redirectUrl = ref('')
 const redirectUrl = ref('')
 onLoad((options) => {
 onLoad((options) => {
-  console.log('login options', options)
+  console.log('login options: ', options)
   if (options.redirect) {
   if (options.redirect) {
     redirectUrl.value = ensureDecodeURIComponent(options.redirect)
     redirectUrl.value = ensureDecodeURIComponent(options.redirect)
   }
   }
   else {
   else {
     redirectUrl.value = tabbarList[0].pagePath
     redirectUrl.value = tabbarList[0].pagePath
   }
   }
+  console.log('redirectUrl.value: ', redirectUrl.value)
 })
 })
 const userStore = useUserStore()
 const userStore = useUserStore()
 function doLogin() {
 function doLogin() {
@@ -36,10 +38,13 @@ function doLogin() {
   if (!path.startsWith('/')) {
   if (!path.startsWith('/')) {
     path = `/${path}`
     path = `/${path}`
   }
   }
-  console.log('path:', path)
-  if (isPageTabbar(path)) {
+  const { path: _path, query } = parseRouteStr(path)
+  console.log('_path:', _path, 'query:', query)
+  console.log('isPageTabbar(_path):', isPageTabbar(_path))
+  if (isPageTabbar(_path)) {
     uni.switchTab({
     uni.switchTab({
-      url: path,
+      url: _path,
+      query,
     })
     })
   }
   }
   else {
   else {

+ 1 - 10
src/pages/me/me.vue

@@ -15,14 +15,7 @@ import { useUpload } from '@/utils/uploadFile'
 const userStore = useUserStore()
 const userStore = useUserStore()
 // 使用storeToRefs解构userInfo
 // 使用storeToRefs解构userInfo
 const { userInfo } = storeToRefs(userStore)
 const { userInfo } = storeToRefs(userStore)
-const hasLogin = ref(false)
 
 
-onShow((options) => {
-  hasLogin.value = !!uni.getStorageSync('token')
-  console.log('个人中心onShow', hasLogin.value, options)
-
-  hasLogin.value && useUserStore().getUserInfo()
-})
 // #ifndef MP-WEIXIN
 // #ifndef MP-WEIXIN
 // 上传头像
 // 上传头像
 const { run: uploadAvatar } = useUpload<IUploadSuccessInfo>(
 const { run: uploadAvatar } = useUpload<IUploadSuccessInfo>(
@@ -43,7 +36,6 @@ async function handleLogin() {
 
 
   // 微信登录
   // 微信登录
   await userStore.wxLogin()
   await userStore.wxLogin()
-  hasLogin.value = true
   // #endif
   // #endif
   // #ifndef MP-WEIXIN
   // #ifndef MP-WEIXIN
   uni.navigateTo({ url: '/pages/login/login' })
   uni.navigateTo({ url: '/pages/login/login' })
@@ -86,7 +78,6 @@ function handleLogout() {
       if (res.confirm) {
       if (res.confirm) {
         // 清空用户信息
         // 清空用户信息
         useUserStore().logout()
         useUserStore().logout()
-        hasLogin.value = false
         // 执行退出登录逻辑
         // 执行退出登录逻辑
         uni.showToast({
         uni.showToast({
           title: '退出登录成功',
           title: '退出登录成功',
@@ -146,7 +137,7 @@ function handleLogout() {
 
 
     <view class="mt-20 px-3">
     <view class="mt-20 px-3">
       <view class="m-auto w-160px text-center">
       <view class="m-auto w-160px text-center">
-        <button v-if="hasLogin" type="warn" class="w-full" @click="handleLogout">
+        <button v-if="userStore.hasLogin" type="warn" class="w-full" @click="handleLogout">
           退出登录
           退出登录
         </button>
         </button>
         <button v-else type="primary" class="w-full" @click="handleLogin">
         <button v-else type="primary" class="w-full" @click="handleLogin">

+ 19 - 10
src/router/interceptor.ts

@@ -8,35 +8,41 @@ import { useUserStore } from '@/store'
 import { tabbarStore } from '@/tabbar/store'
 import { tabbarStore } from '@/tabbar/store'
 import { getLastPage } from '@/utils'
 import { getLastPage } from '@/utils'
 import { EXCLUDE_PAGE_LIST, isNeedLogin, LOGIN_PAGE, LOGIN_PAGE_LIST } from '../login/config'
 import { EXCLUDE_PAGE_LIST, isNeedLogin, LOGIN_PAGE, LOGIN_PAGE_LIST } from '../login/config'
+import { parseRouteStr } from './queryString'
 
 
 // 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
 // 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
 export const navigateToInterceptor = {
 export const navigateToInterceptor = {
   // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
   // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
   // 增加对相对路径的处理,BY 网友 @ideal
   // 增加对相对路径的处理,BY 网友 @ideal
   invoke({ url, query }: { url: string, query?: Record<string, string> }) {
   invoke({ url, query }: { url: string, query?: Record<string, string> }) {
-    console.log(url) // /pages/route-interceptor/index?name=feige&age=30
     if (url === undefined) {
     if (url === undefined) {
       return
       return
     }
     }
-    console.log(getCurrentPages())
-    if (getCurrentPages().length === 0) {
-      return
-    }
-    let path = url.split('?')[0]
+
+    let path = decodeURIComponent(url).split('?')[0]
+    // /pages/route-interceptor/index?name=feige&age=30
+    console.log('路由拦截器:url->', url, ', query ->', query, ', path ->', path)
 
 
     // 处理相对路径
     // 处理相对路径
     if (!path.startsWith('/')) {
     if (!path.startsWith('/')) {
+      console.log(getCurrentPages())
+      if (getCurrentPages().length === 0) {
+        return
+      }
       const currentPath = getLastPage()?.route || ''
       const currentPath = getLastPage()?.route || ''
       const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
       const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
       const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
       const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
       path = `${baseDir}/${path}`
       path = `${baseDir}/${path}`
     }
     }
 
 
+    const { path: _path, query: _query } = parseRouteStr(path)
+    console.log('_path:', _path, 'query:', _query)
+
     // 处理直接进入路由非首页时,tabbarIndex 不正确的问题
     // 处理直接进入路由非首页时,tabbarIndex 不正确的问题
-    tabbarStore.setAutoCurIdx(path)
+    tabbarStore.setAutoCurIdx(_path)
 
 
-    if (LOGIN_PAGE_LIST.includes(path)) {
-      console.log('000')
+    if (LOGIN_PAGE_LIST.includes(_path)) {
+      console.log('命中了 LOGIN_PAGE_LIST')
       return
       return
     }
     }
 
 
@@ -48,6 +54,7 @@ export const navigateToInterceptor = {
     const redirectUrl = `${LOGIN_PAGE}?redirect=${encodeURIComponent(path)}`
     const redirectUrl = `${LOGIN_PAGE}?redirect=${encodeURIComponent(path)}`
 
 
     const userStore = useUserStore()
     const userStore = useUserStore()
+    console.log('userStore.hasLogin:', userStore.hasLogin)
 
 
     // #region 1/2 需要登录的情况 ---------------------------
     // #region 1/2 需要登录的情况 ---------------------------
     if (isNeedLogin) {
     if (isNeedLogin) {
@@ -55,10 +62,11 @@ export const navigateToInterceptor = {
         return
         return
       }
       }
       else {
       else {
-        if (EXCLUDE_PAGE_LIST.includes(path)) {
+        if (EXCLUDE_PAGE_LIST.includes(_path)) {
           return
           return
         }
         }
         else {
         else {
+          console.log('isNeedLogin redirectUrl:', redirectUrl)
           uni.navigateTo({ url: redirectUrl })
           uni.navigateTo({ url: redirectUrl })
         }
         }
       }
       }
@@ -68,6 +76,7 @@ export const navigateToInterceptor = {
     // #region 2/2 不需要登录的情况 ---------------------------
     // #region 2/2 不需要登录的情况 ---------------------------
     else {
     else {
       if (EXCLUDE_PAGE_LIST.includes(path)) {
       if (EXCLUDE_PAGE_LIST.includes(path)) {
+        console.log('isNeedLogin redirectUrl:', redirectUrl)
         uni.navigateTo({ url: redirectUrl })
         uni.navigateTo({ url: redirectUrl })
       }
       }
     }
     }

+ 42 - 0
src/router/queryString.ts

@@ -0,0 +1,42 @@
+/**
+ * 把一个带查询参数的路由字符串解析为对象
+ * @param query 要解析的查询字符串
+ * @returns 解析后的对象
+ */
+export function parseQuery(query: string): Record<string, any> {
+  if (!query)
+    return {}
+
+  // 处理 ? 前缀
+  if (query.startsWith('?'))
+    query = query.slice(1)
+
+  return query.split('&').reduce((acc, cur) => {
+    const [key, value] = cur.split('=')
+    acc[key] = decodeURIComponent(value)
+    return acc
+  }, {} as Record<string, any>)
+}
+
+export function parseRouteStr(routeStr: string): Record<string, any> {
+  if (!routeStr)
+    return {}
+
+  if (!routeStr.includes('?')) {
+    return {
+      path: routeStr,
+    }
+  }
+
+  const [path, query] = routeStr.split('?')
+  if (query) {
+    return {
+      path,
+      query: parseQuery(query),
+    }
+  }
+
+  return {
+    path,
+  }
+}

+ 10 - 27
src/store/user.ts

@@ -1,4 +1,4 @@
-import type { IUserInfoVo, IUserLogin, IUserTokenVo } from '@/api/types/login'
+import type { IUserInfoVo } from '@/api/types/login'
 import { defineStore } from 'pinia'
 import { defineStore } from 'pinia'
 import { ref } from 'vue'
 import { ref } from 'vue'
 import {
 import {
@@ -16,9 +16,6 @@ const userInfoState: IUserInfoVo = {
   id: 0,
   id: 0,
   username: '',
   username: '',
   avatar: '/static/images/default-avatar.png',
   avatar: '/static/images/default-avatar.png',
-}
-
-const userTokenState: IUserTokenVo = {
   token: '',
   token: '',
   refreshToken: '',
   refreshToken: '',
   refreshExpire: 0,
   refreshExpire: 0,
@@ -29,7 +26,6 @@ export const useUserStore = defineStore(
   () => {
   () => {
     // 定义用户信息
     // 定义用户信息
     const userInfo = ref<IUserInfoVo>({ ...userInfoState })
     const userInfo = ref<IUserInfoVo>({ ...userInfoState })
-    const userToken = ref<IUserTokenVo>({ ...userTokenState })
     // 设置用户信息
     // 设置用户信息
     const setUserInfo = (val: IUserInfoVo) => {
     const setUserInfo = (val: IUserInfoVo) => {
       console.log('设置用户信息', val)
       console.log('设置用户信息', val)
@@ -50,21 +46,7 @@ export const useUserStore = defineStore(
     // 删除用户信息
     // 删除用户信息
     const removeUserInfo = () => {
     const removeUserInfo = () => {
       userInfo.value = { ...userInfoState }
       userInfo.value = { ...userInfoState }
-      userToken.value = { ...userTokenState }
-      uni.removeStorageSync('userInfo')
-      uni.removeStorageSync('token')
-      uni.removeStorageSync('refreshToken')
-    }
-
-    /**
-     * 存储token,非导出
-     */
-    const setToken = (tokenBody: IUserLogin) => {
-      userToken.value.token = tokenBody.token
-      userToken.value.refreshToken = tokenBody.refreshToken
-      userToken.value.refreshExpire = tokenBody.refreshExpire
-      uni.setStorageSync('token', tokenBody.token)
-      uni.setStorageSync('refreshToken', tokenBody.refreshToken)
+      uni.removeStorageSync('user')
     }
     }
 
 
     /**
     /**
@@ -74,7 +56,6 @@ export const useUserStore = defineStore(
       const res = await _getUserInfo()
       const res = await _getUserInfo()
       const userInfo = res.data
       const userInfo = res.data
       setUserInfo(userInfo)
       setUserInfo(userInfo)
-      uni.setStorageSync('userInfo', userInfo)
       // TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
       // TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
       return res
       return res
     }
     }
@@ -92,8 +73,6 @@ export const useUserStore = defineStore(
       const res = await _login(credentials)
       const res = await _login(credentials)
       console.log('登录信息', res)
       console.log('登录信息', res)
       toast.success('登录成功')
       toast.success('登录成功')
-      // 这里设置token 和 refreshToken
-      setToken(res.data)
       await getUserInfo()
       await getUserInfo()
       return res
       return res
     }
     }
@@ -102,8 +81,13 @@ export const useUserStore = defineStore(
      * 刷新token
      * 刷新token
      */
      */
     const refreshToken = async () => {
     const refreshToken = async () => {
-      const res = await _refreshToken(userToken.value.refreshToken)
-      setToken(res.data)
+      const res = await _refreshToken(userInfo.value.refreshToken)
+      setUserInfo({
+        ...userInfo.value,
+        token: res.data.token,
+        refreshToken: res.data.refreshToken,
+        refreshExpire: res.data.refreshExpire,
+      })
       await getUserInfo()
       await getUserInfo()
       return res
       return res
     }
     }
@@ -130,14 +114,13 @@ export const useUserStore = defineStore(
 
 
     return {
     return {
       userInfo,
       userInfo,
-      userToken,
       login,
       login,
       wxLogin,
       wxLogin,
       setUserInfo,
       setUserInfo,
       getUserInfo,
       getUserInfo,
       setUserAvatar,
       setUserAvatar,
       logout,
       logout,
-      hasLogin: computed(() => !!userToken.value.token),
+      hasLogin: computed(() => !!userInfo.value.token),
       refreshToken,
       refreshToken,
     }
     }
   },
   },