hookehuyr

fix(auth): 修复401重定向死循环和返回报错问题

核心改动:
1. 401拦截器智能选择跳转方式
   - 导航栈只有1页时使用 reLaunch(清空栈)
   - 导航栈有多页时使用 redirectTo(替换当前页)
   - 保存当前路径到 router store(用于登录后跳回)

2. NavHeader组件智能返回逻辑
   - 检查导航栈长度
   - 导航栈只有1页时跳转首页(避免返回报错)
   - 导航栈有多页时正常返回

3. 登录页智能跳转
   - 登录成功后读取 router store 保存的路径
   - 有保存路径则跳转回原页面
   - 无保存路径则跳转到首页

解决问题:
- ✅ 修复401重定向导致的死循环问题
- ✅ 修复登录页点击返回报错 "navigateBack:fail cannot navigate back at first page"
- ✅ 提升用户体验(登录后回到原页面而非总是首页)

技术细节:
- 正确使用 Pinia store:const store = routerStore()
- 理解导航 API 差异:navigateTo(入栈)、redirectTo(替换)、reLaunch(清空栈)

影响文件:
- src/utils/request.js - 401拦截器优化
- src/components/NavHeader.vue - 返回逻辑优化
- src/pages/login/index.vue - 登录成功后智能跳转
- components.d.ts - TypeScript声明自动更新

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
...@@ -35,7 +35,7 @@ declare module 'vue' { ...@@ -35,7 +35,7 @@ declare module 'vue' {
35 PdfPreview: typeof import('./src/components/PdfPreview.vue')['default'] 35 PdfPreview: typeof import('./src/components/PdfPreview.vue')['default']
36 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] 36 Picker: typeof import('./src/components/time-picker-data/picker.vue')['default']
37 PlanFormContainer: typeof import('./src/components/PlanFormContainer.vue')['default'] 37 PlanFormContainer: typeof import('./src/components/PlanFormContainer.vue')['default']
38 - PlanPopup: typeof import('./src/components/PlanPopup/index.vue')['default'] 38 + PlanPopup: typeof import('./src/components/PlanSchemes/PlanPopup.vue')['default']
39 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default'] 39 PosterBuilder: typeof import('./src/components/PosterBuilder/index.vue')['default']
40 ProductCard: typeof import('./src/components/ProductCard.vue')['default'] 40 ProductCard: typeof import('./src/components/ProductCard.vue')['default']
41 QrCode: typeof import('./src/components/qrCode.vue')['default'] 41 QrCode: typeof import('./src/components/qrCode.vue')['default']
......
...@@ -59,8 +59,20 @@ onMounted(() => { ...@@ -59,8 +59,20 @@ onMounted(() => {
59 59
60 /** 60 /**
61 * Handle back navigation 61 * Handle back navigation
62 + * @description 智能处理返回逻辑,避免导航栈为空时报错
62 */ 63 */
63 const goBack = () => { 64 const goBack = () => {
64 - Taro.navigateBack() 65 + const pages = Taro.getCurrentPages()
66 +
67 + if (pages.length > 1) {
68 + // 如果导航栈有多个页面,正常返回
69 + Taro.navigateBack()
70 + } else {
71 + // 如果导航栈只有1页,跳转到首页(避免报错)
72 + console.log('导航栈只有1页,跳转到首页')
73 + Taro.reLaunch({
74 + url: '/pages/index/index'
75 + })
76 + }
65 } 77 }
66 </script> 78 </script>
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
69 import { reactive } from 'vue' 69 import { reactive } from 'vue'
70 import Taro from '@tarojs/taro' 70 import Taro from '@tarojs/taro'
71 import { useUserStore } from '@/stores/user' 71 import { useUserStore } from '@/stores/user'
72 +import { routerStore } from '@/stores/router'
72 import NavHeader from '@/components/NavHeader.vue' 73 import NavHeader from '@/components/NavHeader.vue'
73 74
74 const userStore = useUserStore() 75 const userStore = useUserStore()
...@@ -112,9 +113,32 @@ const handleLogin = async () => { ...@@ -112,9 +113,32 @@ const handleLogin = async () => {
112 Taro.hideLoading() 113 Taro.hideLoading()
113 Taro.showToast({ title: '登录成功', icon: 'success' }) 114 Taro.showToast({ title: '登录成功', icon: 'success' })
114 115
115 - // 延迟后跳转首页 116 + // 延迟后跳转回原页面或首页
116 setTimeout(() => { 117 setTimeout(() => {
117 - Taro.reLaunch({ url: '/pages/index/index' }) 118 + // 获取 router store 实例
119 + const store = routerStore()
120 +
121 + // 从 router store 获取之前保存的路径
122 + const redirectUrl = store.url
123 +
124 + if (redirectUrl) {
125 + // 清空保存的路径
126 + store.remove()
127 + console.log('登录成功,跳转回原页面:', redirectUrl)
128 +
129 + // 跳转回原页面
130 + Taro.redirectTo({
131 + url: redirectUrl
132 + }).catch(() => {
133 + // 如果跳转失败,则跳转到首页
134 + console.warn('跳转回原页面失败,跳转到首页')
135 + Taro.reLaunch({ url: '/pages/index/index' })
136 + })
137 + } else {
138 + // 没有保存的路径,跳转到首页
139 + console.log('登录成功,跳转到首页')
140 + Taro.reLaunch({ url: '/pages/index/index' })
141 + }
118 }, 1500) 142 }, 1500)
119 } else { 143 } else {
120 Taro.hideLoading() 144 Taro.hideLoading()
......
...@@ -9,6 +9,7 @@ import axios from 'axios-miniprogram' ...@@ -9,6 +9,7 @@ import axios from 'axios-miniprogram'
9 import Taro from '@tarojs/taro' 9 import Taro from '@tarojs/taro'
10 import { parseQueryString } from './tools' 10 import { parseQueryString } from './tools'
11 import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config' 11 import BASE_URL, { REQUEST_DEFAULT_PARAMS } from './config'
12 +import { routerStore } from '@/stores/router'
12 13
13 /** 14 /**
14 * @description 获取 sessionid 的工具函数 15 * @description 获取 sessionid 的工具函数
...@@ -246,13 +247,40 @@ service.interceptors.response.use( ...@@ -246,13 +247,40 @@ service.interceptors.response.use(
246 247
247 // 401 未授权处理 248 // 401 未授权处理
248 if (res.code === 401) { 249 if (res.code === 401) {
249 - // 跳转到登录页 250 + // 获取当前页面路径
250 - Taro.navigateTo({ 251 + const pages = Taro.getCurrentPages()
251 - url: '/pages/login/index' 252 + const currentPage = pages[pages.length - 1]
252 - }).catch(() => { 253 + const currentPath = currentPage?.route || ''
253 - // 如果跳转失败(如已经在登录页),则忽略 254 +
254 - console.warn('跳转登录页失败,可能已在登录页') 255 + // 保存当前路径到 router store(用于登录后跳转回原页面)
255 - }) 256 + if (currentPath && currentPath !== 'pages/login/index') {
257 + const store = routerStore() // 调用函数获取 store 实例
258 + store.add('/' + currentPath)
259 + console.log('401: 保存当前路径到 router store:', '/' + currentPath)
260 + } else {
261 + console.log('401: 当前路径为空或是登录页,不保存路径:', currentPath)
262 + }
263 +
264 + // 根据导航栈长度选择跳转方式
265 + if (pages.length <= 1) {
266 + // 如果导航栈只有1个页面,使用 reLaunch 清空栈并跳转到登录页
267 + // 这样可以避免"cannot navigate back"错误
268 + console.log('401: 导航栈只有1页,使用 reLaunch')
269 + Taro.reLaunch({
270 + url: '/pages/login/index'
271 + }).catch(() => {
272 + console.warn('reLaunch 到登录页失败')
273 + })
274 + } else {
275 + // 如果导航栈有多个页面,使用 redirectTo 替换当前页面
276 + console.log('401: 导航栈有多个页面,使用 redirectTo')
277 + Taro.redirectTo({
278 + url: '/pages/login/index'
279 + }).catch(() => {
280 + // 如果跳转失败(如已经在登录页),则忽略
281 + console.warn('跳转登录页失败,可能已在登录页')
282 + })
283 + }
256 } 284 }
257 285
258 // 处理特殊消息(不需要显示的错误) 286 // 处理特殊消息(不需要显示的错误)
......