|
@@ -1,38 +1,60 @@
|
|
|
-import { ref, reactive } from 'vue'
|
|
|
|
|
-import { useRouter, useRoute } from 'vue-router'
|
|
|
|
|
|
|
+import { ref, reactive, computed } from 'vue'
|
|
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
import { showToast, showSuccessToast } from 'vant'
|
|
import { showToast, showSuccessToast } from 'vant'
|
|
|
import { getStatusApi, submitInvoiceApplyApi } from '@/services/modules/invoiceInformation'
|
|
import { getStatusApi, submitInvoiceApplyApi } from '@/services/modules/invoiceInformation'
|
|
|
-import type { PushRecordIdRequest } from '@/services/modules/invoiceInformation/type.d.ts'
|
|
|
|
|
-import { useUserStore } from '@/stores/modules/user'
|
|
|
|
|
import { getFaceAuthResultApi } from '@/services/modules/faceRecognition'
|
|
import { getFaceAuthResultApi } from '@/services/modules/faceRecognition'
|
|
|
|
|
+import type { PushRecordIdRequest } from '@/services/modules/invoiceInformation/type'
|
|
|
|
|
+import { useUserStore } from '@/stores/modules/user'
|
|
|
|
|
+
|
|
|
|
|
+/* ========================== 类型定义 ========================== */
|
|
|
|
|
|
|
|
interface ToStatus {
|
|
interface ToStatus {
|
|
|
- /** 待处理事件 */
|
|
|
|
|
eventStatus?: string
|
|
eventStatus?: string
|
|
|
- /** 开票状态 */
|
|
|
|
|
invoiceStatus?: string
|
|
invoiceStatus?: string
|
|
|
- /** 是否上传身份证图片 */
|
|
|
|
|
isIdImgReady?: boolean
|
|
isIdImgReady?: boolean
|
|
|
- [property: string]: any
|
|
|
|
|
|
|
+ [key: string]: any
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * 发票状态 Hook
|
|
|
|
|
- * 用于复用:状态查询 + 提交开票申请
|
|
|
|
|
- */
|
|
|
|
|
|
|
+/* ========================== 主 Hook ========================== */
|
|
|
|
|
+
|
|
|
export function useInvoice() {
|
|
export function useInvoice() {
|
|
|
const userStore = useUserStore()
|
|
const userStore = useUserStore()
|
|
|
const router = useRouter()
|
|
const router = useRouter()
|
|
|
|
|
|
|
|
- const route = useRoute()
|
|
|
|
|
|
|
+ /* ------------------ 基础状态 ------------------ */
|
|
|
|
|
|
|
|
- // state
|
|
|
|
|
- const btnDisabled = ref(false)
|
|
|
|
|
- const statusMap = ref<ToStatus>({})
|
|
|
|
|
const isLoading = ref(false)
|
|
const isLoading = ref(false)
|
|
|
|
|
+ const statusMap = ref<ToStatus>({})
|
|
|
|
|
+
|
|
|
|
|
+ // 状态层面是否允许继续流程
|
|
|
|
|
+ const statusReady = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+ // 人脸认证接口是否已经返回成功
|
|
|
|
|
+ const faceAuthReady = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+ // 人脸是否“通过或无需认证”
|
|
|
|
|
+ const faceAuthResult = ref(false)
|
|
|
|
|
+
|
|
|
|
|
+ /* ------------------ UI 控制层 ------------------ */
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 仅用于按钮禁用控制
|
|
|
|
|
+ * 不参与真实业务逻辑
|
|
|
|
|
+ */
|
|
|
|
|
+ const btnDisabled = computed(() => {
|
|
|
|
|
+ return !(statusReady.value && faceAuthReady.value)
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ /* ------------------ 请求参数 ------------------ */
|
|
|
|
|
+
|
|
|
|
|
+ const params = reactive<PushRecordIdRequest>({
|
|
|
|
|
+ pushRecordId: userStore.pushRecordId,
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ /* ------------------ 常量映射 ------------------ */
|
|
|
|
|
|
|
|
- // 已提交状态映射
|
|
|
|
|
const isSubmitStatusMap = ['PENDING', 'FINISHED', 'RED_INK_ENTRY', 'NOT_APPROVED', 'DISCARD']
|
|
const isSubmitStatusMap = ['PENDING', 'FINISHED', 'RED_INK_ENTRY', 'NOT_APPROVED', 'DISCARD']
|
|
|
|
|
+
|
|
|
const isSubmitEventStatusMap = [
|
|
const isSubmitEventStatusMap = [
|
|
|
'SUBMITTED',
|
|
'SUBMITTED',
|
|
|
'PENDING_PAYMENT',
|
|
'PENDING_PAYMENT',
|
|
@@ -43,46 +65,28 @@ export function useInvoice() {
|
|
|
'INVOICE_DOWNLOADABLE',
|
|
'INVOICE_DOWNLOADABLE',
|
|
|
]
|
|
]
|
|
|
|
|
|
|
|
- // 请求参数
|
|
|
|
|
- const params = reactive<PushRecordIdRequest>({
|
|
|
|
|
- pushRecordId: userStore.pushRecordId,
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- const faceAuthResult = ref(false)
|
|
|
|
|
-
|
|
|
|
|
- // 获取认证结果
|
|
|
|
|
- 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') {
|
|
|
|
|
- faceAuthResult.value = true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err: any) {
|
|
|
|
|
- console.error('获取认证结果失败', err)
|
|
|
|
|
- showToast(err!.message || '获取认证结果失败,请稍后重试')
|
|
|
|
|
- btnDisabled.value = true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /* ============================================================
|
|
|
|
|
+ * 状态获取层
|
|
|
|
|
+ * ============================================================ */
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 获取开票状态
|
|
|
|
|
- */
|
|
|
|
|
const getStatus = async () => {
|
|
const getStatus = async () => {
|
|
|
|
|
+ if (!params.pushRecordId) return
|
|
|
|
|
+
|
|
|
isLoading.value = true
|
|
isLoading.value = true
|
|
|
try {
|
|
try {
|
|
|
const res = await getStatusApi(params)
|
|
const res = await getStatusApi(params)
|
|
|
- if (res.code === 0) {
|
|
|
|
|
- statusMap.value = res.data as ToStatus
|
|
|
|
|
- // 判断当前是否已提交
|
|
|
|
|
- const { invoiceStatus, eventStatus } = res.data
|
|
|
|
|
- const isInvoiceSubmitted = invoiceStatus && isSubmitStatusMap.includes(invoiceStatus)
|
|
|
|
|
- const isEventSubmitted = eventStatus && isSubmitEventStatusMap.includes(eventStatus)
|
|
|
|
|
- btnDisabled.value = isInvoiceSubmitted && isEventSubmitted
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ if (res.code !== 0) return
|
|
|
|
|
+
|
|
|
|
|
+ statusMap.value = res.data as ToStatus
|
|
|
|
|
+
|
|
|
|
|
+ const { invoiceStatus, eventStatus } = res.data
|
|
|
|
|
+
|
|
|
|
|
+ const isInvoiceSubmitted = !!invoiceStatus && isSubmitStatusMap.includes(invoiceStatus)
|
|
|
|
|
+
|
|
|
|
|
+ const isEventSubmitted = !!eventStatus && isSubmitEventStatusMap.includes(eventStatus)
|
|
|
|
|
+
|
|
|
|
|
+ // 只有未提交状态才允许继续
|
|
|
|
|
+ statusReady.value = !(isInvoiceSubmitted && isEventSubmitted)
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
console.error('获取状态失败', err)
|
|
console.error('获取状态失败', err)
|
|
|
showToast('获取开票状态失败,请稍后重试')
|
|
showToast('获取开票状态失败,请稍后重试')
|
|
@@ -91,48 +95,95 @@ export function useInvoice() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- /**
|
|
|
|
|
- * 提交开票申请
|
|
|
|
|
- */
|
|
|
|
|
- const submitInvoiceApply = async () => {
|
|
|
|
|
|
|
+ /* ============================================================
|
|
|
|
|
+ * 人脸认证状态层
|
|
|
|
|
+ * ============================================================ */
|
|
|
|
|
+
|
|
|
|
|
+ const getFaceAuthResult = async () => {
|
|
|
|
|
+ if (!params.pushRecordId) return
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getFaceAuthResultApi(params)
|
|
|
|
|
+
|
|
|
|
|
+ if (res.code !== 0 || !res.data?.success) return
|
|
|
|
|
+
|
|
|
|
|
+ faceAuthReady.value = true
|
|
|
|
|
+
|
|
|
|
|
+ if (res.data.rzzt === 'NO_REQUIRED_AUTHENTICATION') {
|
|
|
|
|
+ faceAuthResult.value = true
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (err: any) {
|
|
|
|
|
+ console.error('获取认证结果失败', err)
|
|
|
|
|
+ showToast(err?.message || '获取认证结果失败,请稍后重试')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ============================================================
|
|
|
|
|
+ * 核心提交逻辑(纯业务)
|
|
|
|
|
+ * ⚠ 不依赖任何 UI 计算属性
|
|
|
|
|
+ * ============================================================ */
|
|
|
|
|
+
|
|
|
|
|
+ const doSubmitInvoice = async (): Promise<boolean> => {
|
|
|
|
|
+ if (!params.pushRecordId) {
|
|
|
|
|
+ showToast('参数异常')
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
- // 1. 提交流程
|
|
|
|
|
const res = await submitInvoiceApplyApi(params)
|
|
const res = await submitInvoiceApplyApi(params)
|
|
|
- // 2. 成功逻辑
|
|
|
|
|
|
|
+
|
|
|
if (res.code === 0 && res.data) {
|
|
if (res.code === 0 && res.data) {
|
|
|
showSuccessToast('提交成功')
|
|
showSuccessToast('提交成功')
|
|
|
|
|
+
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
userStore.LogOut()
|
|
userStore.LogOut()
|
|
|
router.replace({ path: '/success' })
|
|
router.replace({ path: '/success' })
|
|
|
}, 800)
|
|
}, 800)
|
|
|
- return
|
|
|
|
|
|
|
+
|
|
|
|
|
+ return true
|
|
|
}
|
|
}
|
|
|
- // 3. 失败逻辑(接口返回的错误)
|
|
|
|
|
- showToast(res.msg)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ showToast(res.msg || '提交失败')
|
|
|
|
|
+ return false
|
|
|
} catch (err: any) {
|
|
} catch (err: any) {
|
|
|
- // 4. 异常逻辑
|
|
|
|
|
console.error('提交失败', err)
|
|
console.error('提交失败', err)
|
|
|
- showToast(err.message)
|
|
|
|
|
|
|
+ showToast(err?.message || '提交失败')
|
|
|
|
|
+ return false
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 5. 失败后统一处理 —— 判断当前页面是否需要跳转
|
|
|
|
|
- if (route.path !== '/h5/invoice-information/index') {
|
|
|
|
|
- setTimeout(() => {
|
|
|
|
|
- router.replace({
|
|
|
|
|
- path: '/invoice-information',
|
|
|
|
|
- })
|
|
|
|
|
- }, 1800)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ /* ============================================================
|
|
|
|
|
+ * UI 场景提交(按钮点击)
|
|
|
|
|
+ * ============================================================ */
|
|
|
|
|
+
|
|
|
|
|
+ const submitInvoiceWithGuard = async () => {
|
|
|
|
|
+ if (btnDisabled.value) return false
|
|
|
|
|
+ return await doSubmitInvoice()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /* ============================================================
|
|
|
|
|
+ * 流程场景提交(loading页/自动流程)
|
|
|
|
|
+ * ============================================================ */
|
|
|
|
|
+
|
|
|
|
|
+ const submitInvoiceForce = async () => {
|
|
|
|
|
+ return await doSubmitInvoice()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* ========================== 暴露 API ========================== */
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
- params,
|
|
|
|
|
|
|
+ /* 状态 */
|
|
|
statusMap,
|
|
statusMap,
|
|
|
- btnDisabled,
|
|
|
|
|
isLoading,
|
|
isLoading,
|
|
|
faceAuthResult,
|
|
faceAuthResult,
|
|
|
|
|
+ btnDisabled,
|
|
|
|
|
+
|
|
|
|
|
+ /* 状态获取 */
|
|
|
getStatus,
|
|
getStatus,
|
|
|
- submitInvoiceApply,
|
|
|
|
|
getFaceAuthResult,
|
|
getFaceAuthResult,
|
|
|
|
|
+
|
|
|
|
|
+ /* 提交 */
|
|
|
|
|
+ submitInvoiceWithGuard, // 按钮用
|
|
|
|
|
+ submitInvoiceForce, // 流程自动用
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|