diff --git a/web/src/api/backend/index.ts b/web/src/api/backend/index.ts index 4bf7902..951b444 100644 --- a/web/src/api/backend/index.ts +++ b/web/src/api/backend/index.ts @@ -13,12 +13,17 @@ export function index() { } export function login(method: 'get' | 'post', params: object = {}, axiosConfig: Record = {}) { - return createAxios({ - url: url + 'login', - data: params, - method: method, - ...axiosConfig, - }) + return createAxios( + { + url: url + 'login', + data: params, + method: method, + ...axiosConfig, + }, + { + cancelDuplicateRequest: false, + } + ) } export function totpVerify(params: object = {}) { diff --git a/web/src/utils/axios.ts b/web/src/utils/axios.ts index c6a12c5..834d2ce 100644 --- a/web/src/utils/axios.ts +++ b/web/src/utils/axios.ts @@ -204,10 +204,12 @@ function createAxios>(axiosConfig: AxiosRequest } if (response.data.code == 303) { const isAdminAppFlag = isAdminApp() + const loginRouteName = isAdminAppFlag ? 'adminLogin' : 'userLogin' + const dataType = response.data.data?.type let routerPath = isAdminAppFlag ? adminBaseRoute.path : memberCenterBaseRoutePath // 需要登录,清理 token,转到登录页 - if (response.data.data.type == 'need login') { + if (dataType == 'need login') { if (isAdminAppFlag) { adminInfo.removeToken() } else { @@ -215,7 +217,12 @@ function createAxios>(axiosConfig: AxiosRequest } routerPath += '/login' } - router.push({ path: routerPath }) + + const alreadyOnLogin = router.currentRoute.value.name === loginRouteName + const targetIsLogin = routerPath.endsWith('/login') + if (!alreadyOnLogin || (!targetIsLogin && dataType === 'logged in')) { + router.push({ path: routerPath }) + } } // code不等于1, 页面then内的具体逻辑就不执行了 return Promise.reject(response.data) diff --git a/web/src/views/backend/login.vue b/web/src/views/backend/login.vue index e196684..88adcb6 100644 --- a/web/src/views/backend/login.vue +++ b/web/src/views/backend/login.vue @@ -173,6 +173,7 @@ import toggleDark from '/@/utils/useDark' import { fullUrl } from '/@/utils/common' import { adminBaseRoutePath } from '/@/router/static/adminBase' import type { FormItemRule } from 'element-plus' +import { ElMessage } from 'element-plus' let timer: number let loginPrefetchController: AbortController | null = null @@ -183,6 +184,57 @@ const abortLoginPrefetch = () => { } } +const unwrapLoginApiData = (res: anyObj | undefined): anyObj => { + if (!res || typeof res !== 'object') { + return {} + } + if (res.code !== undefined && res.data !== undefined && typeof res.data === 'object') { + return res.data as anyObj + } + return res as anyObj +} + +const handleLoginApiResult = (res: anyObj | undefined) => { + if (!res || typeof res !== 'object') { + return + } + const code = res.code + const data = unwrapLoginApiData(res) + const msg = typeof res.msg === 'string' ? res.msg : '' + + if (code === 303 && data.type === 'logged in') { + router.replace({ path: adminBaseRoutePath }) + return + } + + if (data.userInfo?.token) { + navigateAfterLogin(data.userInfo) + return + } + + const tempToken = data.tempToken || data.temp_token + if (data.type === 'need_totp' && tempToken) { + state.step = 'totp' + state.tempToken = tempToken + state.username = data.username || form.username + totpForm.code = '' + if (msg) { + ElMessage.info(msg) + } + return + } + if (data.type === 'need_bind_totp' && tempToken) { + state.step = 'bind' + state.tempToken = tempToken + state.username = data.username || form.username + totpForm.code = '' + if (msg) { + ElMessage.info(msg) + } + loadBindInfo() + } +} + const config = useConfig() const adminInfo = useAdminInfo() toggleDark(config.layout.isDark) @@ -277,7 +329,8 @@ onMounted(() => { loginPrefetchController = new AbortController() login('get', {}, { signal: loginPrefetchController.signal }) .then((res) => { - state.showCaptcha = res.data.captcha + const data = unwrapLoginApiData(res) + state.showCaptcha = Boolean(data.captcha) nextTick(() => focusInput()) }) .catch((err) => { @@ -312,25 +365,13 @@ const onSubmit = (captchaInfo = '') => { form.captchaInfo = captchaInfo login('post', form) .then((res) => { - const data = res?.data - if (data?.userInfo?.token) { - navigateAfterLogin(data.userInfo) + handleLoginApiResult(res) + }) + .catch((err) => { + if (err?.code === 'ERR_CANCELED' || err?.message === 'canceled') { return } - if (data?.type === 'need_totp' && data?.tempToken) { - state.step = 'totp' - state.tempToken = data.tempToken - state.username = data.username || form.username - totpForm.code = '' - return - } - if (data?.type === 'need_bind_totp' && data?.tempToken) { - state.step = 'bind' - state.tempToken = data.tempToken - state.username = data.username || form.username - totpForm.code = '' - loadBindInfo() - } + handleLoginApiResult(err as anyObj) }) .finally(() => { state.submitLoading = false