فهرست منبع

新增401页面+认证loading页面

yuanmingze 3 ماه پیش
والد
کامیت
ffce785b68

+ 13 - 7
src/hooks/useInvoice.ts

@@ -29,7 +29,14 @@ export function useInvoice() {
   const isLoading = ref(false)
 
   // 已提交状态映射
-  const isSubmitStatusMap = ['PENDING', 'FINISHED']
+  const isSubmitStatusMap = ['PENDING']
+  const isSubmitEventStatusMap = [
+    'SUBMITTED',
+    'PENDING_PAYMENT',
+    'PAID',
+    'FINAL_INVOICE_SUBMITTED',
+    'INVOICE_DOWNLOADABLE',
+  ]
 
   // 请求参数
   const params = reactive<PushRecordIdRequest>({
@@ -45,12 +52,11 @@ export function useInvoice() {
       const res = await getStatusApi(params)
       if (res.code === 0) {
         statusMap.value = res.data as ToStatus
-        const invoiceStatus = res.data.invoiceStatus
-        if (invoiceStatus && isSubmitStatusMap.includes(invoiceStatus)) {
-          btnDisabled.value = true
-        } else {
-          btnDisabled.value = false
-        }
+        //  判断当前是否已提交
+        const { invoiceStatus, eventStatus } = res.data
+        const isInvoiceSubmitted = invoiceStatus && isSubmitStatusMap.includes(invoiceStatus)
+        const isEventSubmitted = eventStatus && isSubmitEventStatusMap.includes(eventStatus)
+        btnDisabled.value = isInvoiceSubmitted && isEventSubmitted
       }
     } catch (err) {
       console.error('获取状态失败', err)

+ 6 - 0
src/router/routes.ts

@@ -60,6 +60,12 @@ export const routes: RouteRecordRaw[] = [
     component: () => import('@/views/success/loginSuccess.vue'),
     meta: { title: '自然人开票', requiresAuth: false },
   },
+  {
+    path: '/pedding-face-recognition',
+    name: 'PeddingFaceRecognition',
+    component: () => import('@/views/pedding-face-recognition/index.vue'),
+    meta: { title: '获取认证状态', requiresAuth: true },
+  },
   {
     path: '/:pathMatch(.*)*',
     redirect: '/login',

+ 2 - 1
src/services/modules/faceRecognition/index.ts

@@ -1,7 +1,8 @@
 import http from '../../index'
 
 import type { PushRecordIdRequest } from '../invoiceInformation/type.d'
-export const getFaceAuthInfoApi = (data: PushRecordIdRequest) => {
+import type { GetFaceAuthInfoRequest } from './type.d'
+export const getFaceAuthInfoApi = (data: GetFaceAuthInfoRequest) => {
   return http.post({
     url: '/admin/invoice-order/get-face-auth-info',
     data,

+ 4 - 0
src/services/modules/faceRecognition/type.d.ts

@@ -0,0 +1,4 @@
+export interface GetFaceAuthInfoRequest {
+  pushRecordId: string
+  callbackUrl: string
+}

+ 19 - 5
src/services/request/index.ts

@@ -15,6 +15,7 @@ import { showToast } from 'vant'
 import { useUserStoreWithOut } from '@/stores'
 import { BASE_URL, TIME_OUT } from './config'
 import { serialize } from '@/utils/util'
+import router from '@/router'
 
 /* -------------------------------------------------------------------------- */
 /*                                  类型声明                                  */
@@ -95,6 +96,24 @@ const handleNetworkError = (response: AxiosResponse, silent?: boolean) => {
   // 默认错误信息
   let message = '未知错误'
 
+  const userStore = useUserStoreWithOut()
+
+  // 先处理401登录失效问题
+  if (status === 401) {
+    showToast('您的登录状态已失效,请重新登录')
+    abortAllRequests()
+    useUserStoreWithOut().LogOut()
+    // 🔁 跳转到登录页
+    setTimeout(() => {
+      router.replace({
+        path: '/login',
+        query: {
+          pushRecordId: userStore.pushRecordId,
+        },
+      })
+    }, 1500)
+  }
+
   // ✅ 优先使用后端业务错误(即 code !== 0)
   if (backendCode && backendCode !== 0) {
     message = backendMsg || '业务处理失败'
@@ -117,11 +136,6 @@ const handleNetworkError = (response: AxiosResponse, silent?: boolean) => {
     case 400:
       message = '错误的请求'
       break
-    case 401:
-      message = '未授权,请重新登录'
-      abortAllRequests()
-      useUserStoreWithOut().LogOut()
-      break
     case 403:
       message = '拒绝访问'
       break

+ 8 - 55
src/views/face-recognition/index.vue

@@ -24,25 +24,21 @@
 
 <script setup lang="ts">
 import StepProgress from '@/components/StepProgress.vue'
-import { getFaceAuthInfoApi, getFaceAuthResultApi } from '@/services/modules/faceRecognition'
-import { ref, reactive, onBeforeMount, onMounted, onUnmounted } from 'vue'
-import { showToast } from 'vant'
-import { useInvoice } from '@/hooks/useInvoice'
+import { getFaceAuthInfoApi } from '@/services/modules/faceRecognition'
+import { ref, onBeforeMount } from 'vue'
 import { useUserStore } from '@/stores/modules/user'
-import type { PushRecordIdRequest } from '@/services/modules/invoiceInformation/type.d.ts'
 
 const userStore = useUserStore()
 const etsUrl = ref('')
 
-const params = reactive<PushRecordIdRequest>({
-  pushRecordId: userStore.pushRecordId,
-})
-
-// --- 初始化 Hooks ---
-const { submitInvoiceApply } = useInvoice()
 // 获取认证链接
 const getConfirmInvoiceInfo = async () => {
-  const res = await getFaceAuthInfoApi(params)
+  const callbackUrl = window.location.origin + '/h5/pedding-face-recognition'
+  const obj = {
+    pushRecordId: userStore.pushRecordId,
+    callbackUrl: callbackUrl,
+  }
+  const res = await getFaceAuthInfoApi(obj)
   if (res.code === 0 && res.data?.faceAuthUrl) {
     etsUrl.value = res.data.faceAuthUrl
   } else {
@@ -50,51 +46,8 @@ const getConfirmInvoiceInfo = async () => {
   }
 }
 
-// 获取认证结果
-const getFaceAuthResult = async () => {
-  try {
-    const res = await getFaceAuthResultApi(params)
-    if (res.code === 0 && res.data?.success) {
-      submitInvoiceApply()
-    } else {
-      showToast('认证失败,请刷新页面后重试')
-    }
-  } catch (err) {
-    console.error('获取认证结果失败', err)
-    showToast('网络错误,请稍后重试')
-  }
-}
-
-// 🧠 监听 iframe 发来的消息
-const handleMessage = (event: MessageEvent) => {
-  // 忽略 React DevTools 的心跳消息
-  if (event.data?.source === 'react-devtools-content-script') return
-
-  console.log('📩 收到 iframe 消息:', event.data)
-
-  if (event.data?.type === 'AUTH_DONE') {
-    showToast('认证完成,跳转下一步')
-    // 比如继续执行下一步逻辑
-  } else if (event.data?.type === 'AUTH_ERROR') {
-    showToast('认证失败:' + event.data.message)
-  }
-}
-
-onMounted(() => {
-  window.addEventListener('message', handleMessage)
-})
-
-onUnmounted(() => {
-  window.removeEventListener('message', handleMessage)
-})
-
 onBeforeMount(() => {
   getConfirmInvoiceInfo()
-
-  // 模拟获取认证结果
-  setTimeout(() => {
-    getFaceAuthResult()
-  }, 5000)
 })
 </script>
 

+ 98 - 0
src/views/pedding-face-recognition/index.vue

@@ -0,0 +1,98 @@
+<template>
+  <div class="loading-page" role="status" aria-live="polite" aria-label="页面加载中">
+    <!-- 顶部导航 -->
+    <van-nav-bar title="加载中" fixed :border="false" safe-area-inset-top class="nav" />
+
+    <!-- 中心区域(带淡淡的高光背景,不是卡片) -->
+    <div class="loading-body">
+      <div>
+        <van-loading type="circular" vertical :size="loadingSize" :color="loadingColor">
+          加载中…
+        </van-loading>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { getFaceAuthResultApi } from '@/services/modules/faceRecognition'
+import type { PushRecordIdRequest } from '@/services/modules/invoiceInformation/type.d.ts'
+import { reactive, onMounted } from 'vue'
+import { useUserStore } from '@/stores/modules/user'
+import { showToast } from 'vant'
+import { useInvoice } from '@/hooks/useInvoice'
+import { useRouter } from 'vue-router'
+const userStore = useUserStore()
+
+const router = useRouter()
+
+// --- 初始化 Hooks ---
+const { submitInvoiceApply } = useInvoice()
+
+const loadingSize = '10vw'
+const loadingColor = '#1677ff'
+
+const params = reactive<PushRecordIdRequest>({
+  pushRecordId: userStore.pushRecordId,
+})
+
+// 获取认证结果
+const getFaceAuthResult = async () => {
+  try {
+    const res = await getFaceAuthResultApi(params)
+    if (res.code === 0 && res.data?.success) {
+      const rzzt = res.data.rzzt
+      // 已认证或无需认证提交跳转
+      if (rzzt === 'NO_REQUIRED_AUTHENTICATION') {
+        submitInvoiceApply()
+      } else {
+        // 认证失败重定向到发票信息页面
+        router.replace({
+          path: '/invoice-information',
+        })
+      }
+    } else {
+      showToast('认证失败,请刷新页面后重试')
+    }
+  } catch (err) {
+    console.error('获取认证结果失败', err)
+    showToast('网络错误,请稍后重试')
+  }
+}
+
+onMounted(() => {
+  getFaceAuthResult()
+})
+</script>
+
+<style scoped>
+/* 基于 375 设计稿的 vw 适配 */
+html {
+  font-size: calc(100vw / 3.75);
+}
+
+.loading-page {
+  width: 100vw;
+  height: 100vh;
+  background: #f6f7fb; /* 整页浅灰底,非卡片 */
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+}
+
+/* 顶部导航固定 */
+.nav {
+  position: fixed;
+  top: 0;
+  width: 100vw;
+}
+
+/* 居中容器 */
+.loading-body {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding-top: 14vw; /* 预留固定导航空间 */
+}
+</style>