From 9e3b745da33c26fbc5d427ec7888549c07c93777 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 29 Apr 2026 10:16:07 +0800 Subject: [PATCH] fix(web): preserve next on 401, validate redirect target, catch navigation rejections --- web/src/components/layout/NavBar.vue | 2 +- web/src/router/index.ts | 7 ++++--- web/src/views/auth/LoginView.vue | 6 ++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/web/src/components/layout/NavBar.vue b/web/src/components/layout/NavBar.vue index 9a3bd94..75b4c94 100644 --- a/web/src/components/layout/NavBar.vue +++ b/web/src/components/layout/NavBar.vue @@ -29,7 +29,7 @@ async function onSelect(key: string) { await authApi.logout() } finally { auth.clearSession() - router.replace({ name: 'login' }) + router.replace({ name: 'login' }).catch(() => {}) } } } diff --git a/web/src/router/index.ts b/web/src/router/index.ts index bcbd635..dfca953 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -7,13 +7,12 @@ const routes: RouteRecordRaw[] = [ path: '/login', name: 'login', component: () => import('@/views/auth/LoginView.vue'), - meta: { layout: 'none' }, }, { path: '/', name: 'home', component: () => import('@/views/HomeView.vue'), - meta: { layout: 'app', requiresAuth: true }, + meta: { requiresAuth: true }, }, ] @@ -42,7 +41,9 @@ export function bindAuthToApi() { tokenProvider: () => auth.token, onUnauthorized: () => { auth.clearSession() - router.replace({ name: 'login' }) + const current = router.currentRoute.value + const query = current.name === 'login' ? undefined : { next: current.fullPath } + router.replace({ name: 'login', query }).catch(() => {}) }, }) } diff --git a/web/src/views/auth/LoginView.vue b/web/src/views/auth/LoginView.vue index 216aad1..5cd4260 100644 --- a/web/src/views/auth/LoginView.vue +++ b/web/src/views/auth/LoginView.vue @@ -40,8 +40,10 @@ async function onSubmit() { try { const out = await authApi.login(email.value, password.value) auth.setSession(out.token, out.user) - const next = (router.currentRoute.value.query.next as string) || '/' - await router.replace(next) + const raw = router.currentRoute.value.query.next + const next = + typeof raw === 'string' && raw.startsWith('/') && !raw.startsWith('//') ? raw : '/' + await router.replace(next).catch(() => {}) } catch (e) { error.value = e instanceof ApiException ? e.body.message : '登录失败' } finally {