fix(frontend): recover login redirect navigation

This commit is contained in:
MMEXA
2026-05-26 23:22:30 +08:00
parent 4933ae9014
commit a9e6a7d644
4 changed files with 156 additions and 15 deletions
@@ -265,6 +265,7 @@ import { oauthApi, type OAuthProviderInfo } from '@/api/oauth'
import { getClientDeviceId } from '@/utils/deviceId'
import { getApiUrl } from '@/utils/url'
import { getOAuthIcon } from '@/utils/oauth-icons'
import { navigateAfterLogin } from '@/features/auth/utils/loginRedirect'
const props = defineProps<{
modelValue: boolean
@@ -361,15 +362,7 @@ async function handleLogin(event?: Event) {
if (success) {
const targetPath = consumeStoredRedirectPath() ?? (authStore.canAccessAdmin ? '/admin/dashboard' : '/dashboard')
try {
const navigationFailure = await router.push(targetPath)
if (navigationFailure) {
throw navigationFailure
}
} catch {
showError('登录成功,但跳转失败,请刷新页面或手动进入控制台')
return
}
await navigateAfterLogin(router, targetPath)
showSuccess('登录成功,正在跳转...')
@@ -1,5 +1,6 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createApp, defineComponent, h, nextTick, type App } from 'vue'
import { createMemoryHistory, createRouter } from 'vue-router'
import LoginDialog from '../LoginDialog.vue'
@@ -29,12 +30,16 @@ const oauthApiMocks = vi.hoisted(() => ({
getProviders: vi.fn(),
}))
vi.mock('vue-router', () => ({
useRoute: () => routeMock,
useRouter: () => ({
push: routerPushMock,
}),
}))
vi.mock('vue-router', async (importOriginal) => {
const actual = await importOriginal<typeof import('vue-router')>()
return {
...actual,
useRoute: () => routeMock,
useRouter: () => ({
push: routerPushMock,
}),
}
})
vi.mock('@/stores/auth', () => ({
useAuthStore: () => authStoreMock,
@@ -156,6 +161,21 @@ async function settle() {
}
}
async function createDuplicatedNavigationFailure(path: string) {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{
path,
component: defineComponent({ setup: () => () => null }),
},
],
})
await router.push(path)
return router.push(path)
}
beforeEach(() => {
authStoreMock.loading = false
authStoreMock.error = ''
@@ -242,4 +262,26 @@ describe('LoginDialog password manager contract', () => {
expect(sessionStorage.getItem('redirectPath')).toBeNull()
expect(toastMocks.success).toHaveBeenCalledWith('登录成功,正在跳转...')
})
it('treats duplicated router navigation after successful auth as a completed login', async () => {
authStoreMock.login.mockResolvedValue(true)
routerPushMock.mockResolvedValue(await createDuplicatedNavigationFailure('/dashboard'))
const root = mountLoginDialog()
await settle()
const form = root.querySelector('form')
const username = root.querySelector<HTMLInputElement>('input[name="username"]')
const password = root.querySelector<HTMLInputElement>('input[name="password"]')
username!.value = '[email protected]'
password!.value = 'secret-from-manager'
form!.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))
await settle()
expect(authStoreMock.login).toHaveBeenCalledWith('[email protected]', 'secret-from-manager', 'local')
expect(routerPushMock).toHaveBeenCalledWith('/dashboard')
expect(toastMocks.error).not.toHaveBeenCalled()
expect(toastMocks.success).toHaveBeenCalledWith('登录成功,正在跳转...')
expect(root.querySelector('[data-testid="dialog"]')).toBeNull()
})
})