|
|
@@ -44,7 +44,14 @@
|
|
|
|
|
|
<!-- 下一步按钮 -->
|
|
|
<div class="btn-section">
|
|
|
- <van-button block type="primary" round class="next-btn" :disabled="!isReady" @click="onNext">
|
|
|
+ <van-button
|
|
|
+ block
|
|
|
+ type="primary"
|
|
|
+ round
|
|
|
+ class="next-btn"
|
|
|
+ :disabled="!isReady"
|
|
|
+ @click="handleNextDebounced"
|
|
|
+ >
|
|
|
下一步
|
|
|
</van-button>
|
|
|
</div>
|
|
|
@@ -52,9 +59,19 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, computed } from 'vue'
|
|
|
+import { ref, computed, reactive, onMounted } from 'vue'
|
|
|
import { showToast, showFailToast } from 'vant'
|
|
|
import { sysFileUploadApi } from '@/services/modules/common'
|
|
|
+import { ocrApi } from '@/services/modules/identityUpload/index'
|
|
|
+import { useDebounceFn } from '@/utils/util'
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
+/** 背景图 */
|
|
|
+import frontBg from '@/assets/images/id-front-bg.png'
|
|
|
+import backBg from '@/assets/images/id-back-bg.png'
|
|
|
+// ✅ 使用封装好的 Hook
|
|
|
+import { useInvoice } from '@/hooks/useInvoice'
|
|
|
+// --- 初始化 Hooks ---
|
|
|
+const { getStatus, submitInvoiceApply, statusMap } = useInvoice()
|
|
|
|
|
|
/** 上传项类型 */
|
|
|
interface UploadFileItem {
|
|
|
@@ -72,9 +89,7 @@ interface UploadItem {
|
|
|
bg: string
|
|
|
}
|
|
|
|
|
|
-/** 背景图 */
|
|
|
-import frontBg from '@/assets/images/id-front-bg.png'
|
|
|
-import backBg from '@/assets/images/id-back-bg.png'
|
|
|
+const router = useRouter()
|
|
|
|
|
|
/** 上传列表 */
|
|
|
const uploadList = ref<UploadItem[]>([
|
|
|
@@ -82,7 +97,7 @@ const uploadList = ref<UploadItem[]>([
|
|
|
{ label: '请上传身份证国徽面', field: 'back', fileList: [], bg: backBg },
|
|
|
])
|
|
|
|
|
|
-/** 上传+OCR识别逻辑 */
|
|
|
+/** 上传 + OCR识别逻辑 */
|
|
|
const handleAfterRead = async (file: any, item: UploadItem, index: number) => {
|
|
|
const target = uploadList.value[index]
|
|
|
if (!target) return
|
|
|
@@ -98,14 +113,11 @@ const handleAfterRead = async (file: any, item: UploadItem, index: number) => {
|
|
|
if (res.code === 0 && res.data?.url) {
|
|
|
const baseUrl = import.meta.env.VITE_APP_URL
|
|
|
const fullUrl = res.data.url.startsWith('http') ? res.data.url : `${baseUrl}${res.data.url}`
|
|
|
-
|
|
|
target.fileList = [{ url: fullUrl, status: 'done', fileId: res.data.fileId }]
|
|
|
showToast(`${item.label} 上传成功`)
|
|
|
-
|
|
|
- // 调用OCR识别
|
|
|
- const ocrRes = await handleOcrCheck(item.field, res.data.fileId, fullUrl)
|
|
|
+ // 调用 OCR 接口(传原始文件 + side)
|
|
|
+ const ocrRes = await handleOcrCheck(item.field, file.file)
|
|
|
if (!ocrRes.success) {
|
|
|
- // OCR 识别失败:清除该图片并提示
|
|
|
target.fileList = []
|
|
|
showFailToast(`识别${item.label}失败,请重新上传`)
|
|
|
}
|
|
|
@@ -121,44 +133,68 @@ const handleAfterRead = async (file: any, item: UploadItem, index: number) => {
|
|
|
}
|
|
|
|
|
|
/** 模拟OCR识别接口逻辑(你可以替换成实际API) */
|
|
|
-const handleOcrCheck = async (
|
|
|
- side: 'front' | 'back',
|
|
|
- fileId: string,
|
|
|
- url: string,
|
|
|
-): Promise<{ success: boolean }> => {
|
|
|
- // 示例:这里可以根据 side 调用不同的 OCR 接口
|
|
|
- // 例如 front 调用 /ocr/idcard/front back 调用 /ocr/idcard/back
|
|
|
+/** OCR识别逻辑 */
|
|
|
+const handleOcrCheck = async (side: 'front' | 'back', file: File) => {
|
|
|
try {
|
|
|
- console.log('识别请求 =>', { side, fileId, url })
|
|
|
- // 模拟延时识别
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 800))
|
|
|
+ // 真实调用
|
|
|
+ const res = await ocrApi({ file, side })
|
|
|
+ console.log('res', res)
|
|
|
+ if (res.code === 0) {
|
|
|
+ // 当是人面向
|
|
|
+ if (side === 'front') {
|
|
|
+ submitInfo.idCard = res.data.idcard
|
|
|
+ submitInfo.idCardFrontImg = res.data.imgUrl
|
|
|
+ } else {
|
|
|
+ submitInfo.idcardPeriodStart = res.data.begin
|
|
|
+ submitInfo.idcardPeriodEnd = res.data.end
|
|
|
+ submitInfo.idCardBackImg = res.data.imgUrl
|
|
|
+ }
|
|
|
+ console.log('submitInfo', submitInfo)
|
|
|
|
|
|
- // ✅ 模拟成功 / ❌ 模拟失败
|
|
|
- const success = Math.random() > 0.2 // 80% 成功率示例
|
|
|
- return { success }
|
|
|
+ return { success: true }
|
|
|
+ }
|
|
|
+ return { success: false }
|
|
|
} catch (err) {
|
|
|
console.error('OCR error', err)
|
|
|
return { success: false }
|
|
|
}
|
|
|
}
|
|
|
+const submitInfo = reactive({
|
|
|
+ id: '',
|
|
|
+ idCard: '',
|
|
|
+ idcardPeriodStart: '',
|
|
|
+ idcardPeriodEnd: '',
|
|
|
+ idCardFrontImg: '',
|
|
|
+ idCardBackImg: '',
|
|
|
+})
|
|
|
|
|
|
/** 是否完成上传 */
|
|
|
-const isReady = computed(() => uploadList.value.every((item) => item.fileList.length > 0))
|
|
|
+// ✅ 校验:每个字段必须有值(非空字符串)
|
|
|
+const isReady = computed(() => {
|
|
|
+ return Object.values(submitInfo).every(
|
|
|
+ (value) => value !== '' && value !== null && value !== undefined,
|
|
|
+ )
|
|
|
+})
|
|
|
|
|
|
-/** 下一步按钮点击 */
|
|
|
-const onNext = () => {
|
|
|
+const handleNext = async () => {
|
|
|
if (!isReady.value) return showToast('请先上传身份证正反面')
|
|
|
|
|
|
- // 拼装最终提交数据
|
|
|
- const result = uploadList.value.map((item) => ({
|
|
|
- field: item.field,
|
|
|
- fileId: item.fileList[0]?.fileId,
|
|
|
- url: item.fileList[0]?.url,
|
|
|
- }))
|
|
|
+ // 需要人脸识别->跳转人脸识别
|
|
|
+ if (statusMap.value.eventStatus === 'PROCESSED') {
|
|
|
+ return router.push({ path: '/face-recognition' })
|
|
|
+ }
|
|
|
|
|
|
- console.log('提交数据:', result)
|
|
|
- showToast('上传完成,进入下一步')
|
|
|
+ // 其他情况,直接提交
|
|
|
+ await submitInvoiceApply()
|
|
|
}
|
|
|
+/** 防抖包装 */
|
|
|
+const handleNextDebounced = useDebounceFn(handleNext, 1000)
|
|
|
+
|
|
|
+/** 下一步按钮点击 */
|
|
|
+
|
|
|
+onMounted(async () => {
|
|
|
+ await Promise.all([getStatus()])
|
|
|
+})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|