Browse Source

登录状态修改

yuanmingze 1 week ago
parent
commit
01ea18bd1d
1 changed files with 87 additions and 69 deletions
  1. 87 69
      src/views/login/index.vue

+ 87 - 69
src/views/login/index.vue

@@ -5,10 +5,6 @@
     <div v-if="restoringSession" class="login-content">
       <van-loading vertical>正在恢复登录状态…</van-loading>
     </div>
-    <div v-else-if="userStore.access_token && formData.pushRecordId" class="login-content">
-      <div class="text">暂时无法恢复开票流程,请重试。</div>
-      <div class="login-btn btn" @click="initializeLogin">重试</div>
-    </div>
     <div v-else class="login-content">
       <div class="text">感谢您参加{{ pubName }}项目,请您登录系统开具发票。</div>
 
@@ -116,7 +112,7 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, watch } from 'vue'
+import { ref, reactive, watch, onUnmounted } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { showFailToast, showSuccessToast, showToast, showLoadingToast } from 'vant'
 import {
@@ -126,11 +122,7 @@ import {
   registerUrlApi,
 } from '@/services/modules/login'
 import { getStatusApi } from '@/services/modules/invoiceInformation'
-import type {
-  SendTtsSmsRequest,
-  LoginEtssmsRequest,
-  RegisterUrlRequest,
-} from '@/services/modules/login/type.d'
+import type { LoginEtssmsRequest, RegisterUrlRequest } from '@/services/modules/login/type.d'
 import { useUserStore } from '@/stores/modules/user'
 import { useDebounceFn } from '@/utils/util'
 import { REGISTER_URL_KEY } from '@/constants/storage'
@@ -140,6 +132,12 @@ const userStore = useUserStore()
 const router = useRouter()
 const route = useRoute()
 const restoringSession = ref(false)
+const loggingIn = ref(false)
+let loginAttemptId = 0
+
+// 离开登录页或切换链接后,忽略上一轮请求的结果。
+const isCurrentLogin = (attemptId: number, pushRecordId: string) =>
+  attemptId === loginAttemptId && route.name === 'Login' && userStore.pushRecordId === pushRecordId
 
 const passwordType = ref<'text' | 'password'>('password')
 const switchPasswordType = () => {
@@ -154,12 +152,6 @@ const formData = reactive<LoginEtssmsRequest>({
   password: '',
 })
 
-const sendEtsSms = reactive<SendTtsSmsRequest>({
-  mobile: '',
-  pushRecordId: '',
-  password: '',
-})
-
 /* 限制手机号只能输入数字,最多 30 位 */
 const onInputMobile = (val: string) => {
   formData.mobile = val.replace(/\D/g, '').slice(0, 30)
@@ -192,7 +184,11 @@ const changePasswordDCancel = () => {
 }
 /* 发送验证码 */
 const sendCode = async () => {
-  if (sending.value) return
+  if (sending.value || isCounting.value || loggingIn.value || restoringSession.value) return
+  if (!formData.pushRecordId) {
+    showToast('登录链接存在不完整或者输入错误')
+    return
+  }
   if (!/^1\d{10}$/.test(formData.mobile)) {
     showToast('请输入正确的手机号')
     return
@@ -203,10 +199,11 @@ const sendCode = async () => {
   }
   sending.value = true
   const toast = showLoadingToast({ message: '发送中…', forbidClick: true, duration: 3000 })
-  sendEtsSms.mobile = formData.mobile
-  sendEtsSms.password = formData.password
+  const attemptId = loginAttemptId
+  const { mobile, password, pushRecordId } = formData
   try {
-    const res = await sendEtsSmsApi(sendEtsSms)
+    const res = await sendEtsSmsApi({ mobile, password, pushRecordId })
+    if (!isCurrentLogin(attemptId, pushRecordId)) return
     if (res.code === 0 && res.data) {
       showSuccessToast('发送成功')
       startCountDown()
@@ -217,6 +214,7 @@ const sendCode = async () => {
     toast.close()
   } catch (err: any) {
     toast.close()
+    if (!isCurrentLogin(attemptId, pushRecordId)) return
 
     const { code, message } = err
     if (code === 401) {
@@ -227,7 +225,7 @@ const sendCode = async () => {
 
     showFailToast(err?.message || '获取验证码失败')
   } finally {
-    sending.value = false
+    if (isCurrentLogin(attemptId, pushRecordId)) sending.value = false
   }
 }
 
@@ -255,12 +253,14 @@ const loginConfirm = () => {
 }
 
 // 首次登录与缓存恢复使用同一套业务状态判断,避免已提交业务重新进入开票页。
-const continueInvoice = async (pushRecordId: string) => {
+const continueInvoice = async (pushRecordId: string, attemptId: number) => {
   const token = userStore.access_token
+  const ready = await getPubName(pushRecordId, attemptId)
+  if (!ready || userStore.access_token !== token) return
+  if (!userStore.hasValidSession()) return
   const statusRes = await getStatusApi({ pushRecordId })
   if (
-    route.name !== 'Login' ||
-    userStore.pushRecordId !== pushRecordId ||
+    !isCurrentLogin(attemptId, pushRecordId) ||
     userStore.access_token !== token ||
     !userStore.hasValidSession()
   )
@@ -286,6 +286,11 @@ const continueInvoice = async (pushRecordId: string) => {
 
 /* 登录逻辑 */
 const loginBtn = useDebounceFn(async () => {
+  if (route.name !== 'Login' || loggingIn.value || restoringSession.value || sending.value) return
+  if (!formData.pushRecordId) {
+    showFailToast('登录链接存在不完整或者输入错误')
+    return
+  }
   if (!formData.mobile) {
     showFailToast('请输入手机号')
     return
@@ -302,6 +307,8 @@ const loginBtn = useDebounceFn(async () => {
     showFailToast('请勾选阅读并同意')
     return
   }
+  loggingIn.value = true
+  const attemptId = ++loginAttemptId
   const toast = showLoadingToast({
     message: '登录中…',
     forbidClick: true,
@@ -309,24 +316,31 @@ const loginBtn = useDebounceFn(async () => {
   })
 
   const pushRecordId = formData.pushRecordId
+  let token = userStore.access_token
   try {
-    const res: any = await loginEtssmsApi(formData)
-    if (route.name !== 'Login' || userStore.pushRecordId !== pushRecordId) return
+    const res: any = await loginEtssmsApi({ ...formData })
+    if (!isCurrentLogin(attemptId, pushRecordId)) return
     if (res?.access_token) {
       userStore.setAccessToken(res.access_token, res.expires_in)
+      token = userStore.access_token
       formData.password = ''
       formData.code = ''
-      sendEtsSms.password = ''
-      if (await getPubName(pushRecordId)) await continueInvoice(pushRecordId)
+      resetCountDown()
+      await continueInvoice(pushRecordId, attemptId)
     } else if (res.code == 401) {
       loginMessage.value = res.msg
       loginDialog.value = true
     } else if (res.code == 402) {
       changePasswordDialog.value = true
+    } else {
+      showFailToast(res.msg || '登录失败,请重试')
     }
   } catch (err: any) {
+    if (!isCurrentLogin(attemptId, pushRecordId) || userStore.access_token !== token) return
+    userStore.LogOut()
     showFailToast(err?.message || '登录失败,请重试')
   } finally {
+    if (attemptId === loginAttemptId) loggingIn.value = false
     toast.close()
   }
 }, 800)
@@ -353,27 +367,17 @@ const registerUrl = useDebounceFn(async () => {
 // 项目名称
 const pubName = ref('')
 
-const getPubName = async (pushRecordId: string) => {
-  try {
-    const res = await getPubNameApi({
-      pushRecordId,
-    })
-    if (route.name !== 'Login' || userStore.pushRecordId !== pushRecordId) return false
-    if (res.code === 0) {
-      pubName.value = res.data.projectName
-      registerUrlParams.value.areaId = res.data.cityCode
-      userStore.setNeedFaceId(res.data.needFaceId)
-      registerUrlParams.value.pushRecordId = pushRecordId
-      return true
-    } else {
-      codeMessage.value = res.msg
-      showDialog.value = true
-      if (userStore.access_token) showFailToast(res.msg || '获取项目信息失败,请重试')
-    }
-  } catch (err: any) {
-    showFailToast(err?.message || '获取项目信息失败,请重试')
+const getPubName = async (pushRecordId: string, attemptId: number) => {
+  const res = await getPubNameApi({ pushRecordId })
+  if (!isCurrentLogin(attemptId, pushRecordId)) return false
+  if (Number(res.code) !== 0 || !res.data) {
+    throw new Error(res.msg || '获取项目信息失败,请重新登录')
   }
-  return false
+  pubName.value = res.data.projectName
+  registerUrlParams.value.areaId = res.data.cityCode
+  userStore.setNeedFaceId(res.data.needFaceId)
+  registerUrlParams.value.pushRecordId = pushRecordId
+  return true
 }
 /* 协议跳转 */
 const toAgreement = (type: string) => {
@@ -383,29 +387,36 @@ const toAgreement = (type: string) => {
   })
 }
 
+const resetCountDown = () => {
+  clearInterval(timer)
+  timer = undefined
+  isCounting.value = false
+  countDown.value = 60
+}
+
 const resetLoginState = () => {
   formData.mobile = ''
   formData.password = ''
   formData.code = ''
-  sendEtsSms.password = ''
   agree.value = false
-  isCounting.value = false
-  countDown.value = 60
+  resetCountDown()
   sending.value = false
   showDialog.value = false
   loginDialog.value = false
   changePasswordDialog.value = false
 
-  if (timer) {
-    clearInterval(timer)
-    timer = undefined
-  }
+  pubName.value = ''
+  registerUrlParams.value.areaId = ''
+  registerUrlParams.value.pushRecordId = ''
+  registerUrlParams.value.returnUrl = ''
 }
 
-let initializationId = 0
 const initializeLogin = async () => {
+  const attemptId = ++loginAttemptId
+  restoringSession.value = false
+  loggingIn.value = false
+  sending.value = false
   if (route.name !== 'Login') return
-  const currentInitialization = ++initializationId
   if (route.query.from === 'reject') {
     resetLoginState()
     return router.replace({
@@ -421,27 +432,29 @@ const initializeLogin = async () => {
       : typeof route.query.pushRecordId === 'string'
         ? route.query.pushRecordId
         : ''
+  if (formData.pushRecordId !== pushRecordId) resetLoginState()
+  userStore.setPushRecordId(pushRecordId)
+  formData.pushRecordId = pushRecordId
   if (!pushRecordId) {
-    formData.pushRecordId = ''
-    sendEtsSms.pushRecordId = ''
+    userStore.LogOut()
     showToast('登录链接存在不完整或者输入错误')
     return
   }
-  if (formData.pushRecordId !== pushRecordId) resetLoginState()
-  userStore.setPushRecordId(pushRecordId)
-  sendEtsSms.pushRecordId = pushRecordId
-  formData.pushRecordId = pushRecordId
   registerUrlParams.value.returnUrl = `${location.origin}${import.meta.env.BASE_URL}login?pushRecordId=${encodeURIComponent(pushRecordId)}`
   restoringSession.value = userStore.hasValidSession()
+  const token = userStore.access_token
   try {
-    const ready = await getPubName(pushRecordId)
-    if (currentInitialization !== initializationId) return
-    if (ready && userStore.hasValidSession()) await continueInvoice(pushRecordId)
+    if (restoringSession.value) {
+      await continueInvoice(pushRecordId, attemptId)
+    } else {
+      await getPubName(pushRecordId, attemptId)
+    }
   } catch (err: any) {
-    // 网络异常保留 token 供重试;真正的失效由请求拦截器统一清理。
-    showFailToast(err?.message || '恢复登录状态失败,请重试')
+    if (!isCurrentLogin(attemptId, pushRecordId) || userStore.access_token !== token) return
+    userStore.LogOut()
+    showFailToast(err?.message || '恢复登录状态失败,请重新登录')
   } finally {
-    if (currentInitialization === initializationId) restoringSession.value = false
+    if (attemptId === loginAttemptId) restoringSession.value = false
   }
 }
 
@@ -455,6 +468,11 @@ watch(
   },
   { immediate: true },
 )
+
+onUnmounted(() => {
+  loginAttemptId++
+  clearInterval(timer)
+})
 </script>
 
 <style lang="scss" scoped>