2025-11-26 23:55:59 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="h-full overflow-hidden">
|
|
|
|
|
|
<!-- 顶部头图 -->
|
|
|
|
|
|
<div class="w-full overflow-hidden relative">
|
|
|
|
|
|
<img src="/头图.png" alt="活动宣传" class="w-full h-auto" />
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 顶部Logo和标题 -->
|
|
|
|
|
|
<div class="absolute top-0 left-0 py-4 h-full w-full flex flex-col items-start justify-center px-4">
|
|
|
|
|
|
<div class="flex items-start mb-4">
|
|
|
|
|
|
<img src="/logo.png" alt="中国福利彩票" class="h-8 pl-2" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<img src="/ziti.png" alt="宁福您彩 新婚送福" class="h-14" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 表单区域 -->
|
|
|
|
|
|
<div class="p-4 -mt-10 z-10 relative">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="relative overflow-hidden rounded-2xl border border-[#FFD7DF] bg-gradient-to-br from-[#FFF7F9] via-white to-[#FFE4EA] shadow-[0_12px_32px_rgba(232,66,77,0.08)] p-6 sm:p-7 backdrop-blur-sm">
|
|
|
|
|
|
<div class="absolute -right-10 -top-16 h-40 w-40 rounded-full bg-[#FFE3EA] blur-3xl opacity-70"></div>
|
|
|
|
|
|
<div class="absolute -left-14 -bottom-16 h-40 w-40 rounded-full bg-[#FFD6E2] blur-3xl opacity-60"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="relative z-10">
|
|
|
|
|
|
<!-- 标题 -->
|
|
|
|
|
|
<div class="flex items-center justify-center text-xl text-[#E8424D] mb-6">
|
|
|
|
|
|
<img src="/爱心点缀1.png" alt="" class="h-5">
|
|
|
|
|
|
<span class="px-2">领取专属喜礼</span>
|
|
|
|
|
|
<img src="/爱心点缀2.png" alt="" class="h-5">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="![2, 3].includes(activityInfo.status)">
|
|
|
|
|
|
<!-- 手机号输入 -->
|
|
|
|
|
|
<div class="mb-5 flex items-center gap-3 border border-gray-200 rounded-lg px-4 bg-white/85 shadow-[0_6px_14px_rgba(232,66,77,0.06)]">
|
|
|
|
|
|
<input :disabled="formData.verifyCode" v-model="formData.phone" type="tel" placeholder="请输入手机号码"
|
|
|
|
|
|
maxlength="11" class="outline-none focus:border-[#E8424D] transition-colors flex-1 h-14 w-full" />
|
|
|
|
|
|
|
|
|
|
|
|
<button @click="formData.verifyCode ? clearPhone() : sendVerificationCode()"
|
|
|
|
|
|
:disabled="countdown > 0 || formData.phone.length !== 11"
|
|
|
|
|
|
class="h-11 rounded-lg text-sm! text-[#E8424D]! transition-all disabled:text-[#8a8a8a]!">
|
|
|
|
|
|
{{ countdown > 0 ? `${countdown}秒后重发` : formData.verifyCode ? '清除' : '获取验证码' }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 验证码输入 -->
|
|
|
|
|
|
<div class="mb-5 flex items-center gap-3 border border-gray-200 rounded-lg px-4 bg-white/85 shadow-[0_6px_14px_rgba(232,66,77,0.06)]">
|
|
|
|
|
|
<input :disabled="formData.verifyCode" v-model="formData.smsCode" type="text" placeholder="请输入验证码"
|
|
|
|
|
|
maxlength="6" class="outline-none focus:border-[#E8424D] transition-colors flex-1 h-14 w-full" />
|
|
|
|
|
|
<button v-if="!formData.verifyCode" @click="verifyCode" :disabled="formData.smsCode.length !== 6"
|
|
|
|
|
|
class="h-8 px-4 bg-[#E8424D]! text-white! rounded-lg text-sm! transition-all hover:opacity-90 active:scale-98 disabled:bg-[#8a8a8a]!">
|
|
|
|
|
|
验证
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="formData.verifyCode && !formData.qrCode">
|
|
|
|
|
|
<!-- 结婚证信息 -->
|
|
|
|
|
|
<div class="mb-6">
|
|
|
|
|
|
<div class="text-base text-gray-800 mb-3 font-medium flex items-center justify-between">
|
|
|
|
|
|
<span>结婚证信息:</span>
|
|
|
|
|
|
<div v-if="formData.marriageNo" class="text-xs" @click="resetMarriageInfo">重新扫描</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="!formData.marriageNo"
|
|
|
|
|
|
class="border border-dashed border-gray-200 rounded-lg p-5 flex items-center justify-center bg-white/80">
|
|
|
|
|
|
<div class="relative inline-block" @click="handleScanClick">
|
|
|
|
|
|
<img src="/扫描.png" alt="" class="h-20">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div v-if="formData.marriageNo"
|
|
|
|
|
|
class="w-full flex flex-col items-start border border-gray-200 rounded-lg p-5 bg-white/85 shadow-[0_6px_14px_rgba(232,66,77,0.06)]">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<span class="inline-block w-25 text-right">结婚证字号:</span>
|
|
|
|
|
|
<span class="pl-2">{{ formData.marriageNo }}</span>
|
|
|
|
|
|
</div>
|
2025-11-26 23:27:25 +08:00
|
|
|
|
<div class="mt-2">
|
|
|
|
|
|
<span class="inline-block w-25 text-right">男方姓名:</span>
|
|
|
|
|
|
<span class="pl-2">{{ formData.husbandName }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-2">
|
|
|
|
|
|
<span class="inline-block w-25 text-right">男方身份证:</span>
|
|
|
|
|
|
<span class="pl-2 break-all">{{ formData.husbandId || '-' }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-2">
|
|
|
|
|
|
<span class="inline-block w-25 text-right">女方姓名:</span>
|
|
|
|
|
|
<span class="pl-2">{{ formData.wifeName }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-2">
|
|
|
|
|
|
<span class="inline-block w-25 text-right">女方身份证:</span>
|
|
|
|
|
|
<span class="pl-2 break-all">{{ formData.wifeId || '-' }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-2">
|
|
|
|
|
|
<span class="inline-block w-25 text-right">登记日期:</span>
|
|
|
|
|
|
<span class="pl-2">{{ formData.registerDate || '-' }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-11-26 23:55:59 +08:00
|
|
|
|
<div v-if="eligibilityError" class="mt-3 text-sm text-[#E8424D] bg-[#FFF5F5] border border-[#FFD7DF] rounded-lg p-3 leading-6">
|
|
|
|
|
|
{{ eligibilityError }}
|
|
|
|
|
|
</div>
|
2025-11-26 23:27:25 +08:00
|
|
|
|
</div>
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
2025-11-26 23:27:25 +08:00
|
|
|
|
<!-- 提交按钮 -->
|
2025-11-26 23:55:59 +08:00
|
|
|
|
<button @click="goToIdUpload" :disabled="isNextDisabled"
|
2025-11-26 23:27:25 +08:00
|
|
|
|
class="w-full h-14 bg-gradient-to-r from-[#E8424D]! to-[#FF7A7A]! text-white! rounded-full text-lg font-bold transition-all hover:opacity-90 active:scale-98 disabled:from-[#8a8a8a]! disabled:to-[#8a8a8a]!">
|
|
|
|
|
|
<div class="text-lg">下一步</div>
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
<div v-if="formData.qrCode">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<h3 class="text-base text-gray-800 mb-1 font-bold">您已成功领取喜礼</h3>
|
|
|
|
|
|
<span class="text-sm text-gray-600">下面是您的专属核销二维码,请在领取站点出示给工作人员扫码核销。</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-4 flex flex-col items-center">
|
|
|
|
|
|
<div class="flex justify-center items-center relative h-[210px] w-[210px]" ref="qrCodeWrapper">
|
|
|
|
|
|
<div v-if="formData.status == 1"
|
|
|
|
|
|
class="h-[210px] w-[210px] bg-white/90 rounded-md flex justify-center items-center absolute z-10">
|
|
|
|
|
|
<span class="text-[#E8424D] text-lg font-bold">二维码已核销</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<qrcode-vue class="absolute opacity-0 pointer-events-none" :value="formData.qrCode" :size="200"
|
|
|
|
|
|
level="H" render-as="canvas" />
|
|
|
|
|
|
<img v-if="qrImageSrc" :src="qrImageSrc" alt="核销二维码"
|
|
|
|
|
|
class="h-[210px] w-[210px] rounded-md shadow-[0_10px_24px_rgba(232,66,77,0.18)] bg-white object-contain" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mt-3 text-xs text-gray-500">长按二维码保存至相册</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-if="activityInfo.status === 2">
|
|
|
|
|
|
<!-- 活动结束状态 -->
|
|
|
|
|
|
<div class="mt-6 p-4 border border-[#E8424D] bg-[#FFF5F5] rounded-lg text-center">
|
|
|
|
|
|
<div class="text-[#E8424D] text-lg font-semibold mb-2">活动已结束</div>
|
|
|
|
|
|
<div class="text-gray-600 text-sm">【{{ activityInfo.activityName }}】活动已结束,感谢您的参与,敬请期待下一次活动</div>
|
|
|
|
|
|
<div class="mt-4 text-xs text-gray-500">活动时间:{{ activityInfo.activityStartTime }} - {{
|
|
|
|
|
|
activityInfo.activityEndTime }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div v-else-if="activityInfo.status === 3">
|
|
|
|
|
|
<!-- 活动失败状态 -->
|
|
|
|
|
|
<div class="mt-6 p-4 border border-[#FF7A7A] bg-[#FFF5F5] rounded-lg text-center">
|
|
|
|
|
|
<div class="text-[#FF7A7A] text-lg font-semibold mb-2">活动获取失败</div>
|
|
|
|
|
|
<div class="text-gray-600 text-sm">活动获取失败,请稍后重试</div>
|
|
|
|
|
|
<div class="mt-4 text-xs text-gray-500">活动时间:--</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 使用说明 -->
|
|
|
|
|
|
<div class="p-4 pt-0" v-if="![2, 3].includes(activityInfo.status)">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="relative overflow-hidden rounded-2xl border border-[#FFD7DF] bg-gradient-to-br from-[#FFF7F9] via-white to-[#FFE4EA] shadow-[0_12px_32px_rgba(232,66,77,0.08)]">
|
|
|
|
|
|
<div class="absolute -right-10 -top-16 h-40 w-40 rounded-full bg-[#FFE3EA] blur-3xl opacity-70"></div>
|
|
|
|
|
|
<div class="absolute -left-14 -bottom-16 h-40 w-40 rounded-full bg-[#FFD6E2] blur-3xl opacity-60"></div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="relative px-5 pt-6 pb-3">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="inline-flex items-center gap-2 px-4 h-11 rounded-full bg-white/90 border border-[#F7B9C2] text-[#E8424D] font-semibold shadow-[0_10px_22px_rgba(232,66,77,0.12)]">
|
|
|
|
|
|
<span
|
|
|
|
|
|
class="inline-flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-[#E8424D] via-[#F55F6E] to-[#FF9AAE] text-white shadow-md">
|
|
|
|
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
|
|
|
|
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
|
|
|
|
|
</svg>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span class="text-base tracking-wide">使用说明</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="relative px-5 pb-5">
|
|
|
|
|
|
<div class="bg-white/90 backdrop-blur-sm rounded-xl p-4 shadow-[0_10px_24px_rgba(232,66,77,0.08)]">
|
|
|
|
|
|
<div class="space-y-4">
|
|
|
|
|
|
<div class="flex items-start gap-3">
|
|
|
|
|
|
<div class="h-9 w-9 shrink-0 rounded-full bg-gradient-to-br from-[#E8424D] to-[#FF7A7A] text-white flex items-center justify-center font-bold shadow-md">
|
|
|
|
|
|
1
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-sm text-[#7A5967] leading-6">
|
|
|
|
|
|
{{ activityInfo.activityStartTime || '-' }}至{{ activityInfo.activityEndTime || '-' }},在宁夏全区民政婚姻登记机构(含涉外机构)办理结婚登记的新人可参与活动。
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-start gap-3">
|
|
|
|
|
|
<div class="h-9 w-9 shrink-0 rounded-full bg-gradient-to-br from-[#E8424D] to-[#FF7A7A] text-white flex items-center justify-center font-bold shadow-md">
|
|
|
|
|
|
2
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-sm text-[#7A5967] leading-6">
|
|
|
|
|
|
凭结婚证到全区指定福利彩票销售点兑换即开型福利彩票,赠票总额{{ activityInfo.totalMoney > 10000 ? (activityInfo.totalMoney / 10000).toFixed(0) + '万' : activityInfo.totalMoney }}元,赠完即止。
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-start gap-3">
|
|
|
|
|
|
<div class="h-9 w-9 shrink-0 rounded-full bg-gradient-to-br from-[#E8424D] to-[#FF7A7A] text-white flex items-center justify-center font-bold shadow-md">
|
|
|
|
|
|
3
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-sm text-[#7A5967] leading-6">
|
|
|
|
|
|
每对新人限使用一次线上二维码兑换券,请妥善保管二维码,核销后即失效。
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex items-start gap-3">
|
|
|
|
|
|
<div class="h-9 w-9 shrink-0 rounded-full bg-gradient-to-br from-[#E8424D] to-[#FF7A7A] text-white flex items-center justify-center font-bold shadow-md">
|
|
|
|
|
|
4
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-sm text-[#7A5967] leading-6">
|
|
|
|
|
|
本活动最终解释权归宁夏福利彩票发行中心所有,如有疑问请咨询现场工作人员。
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<input type="file" ref="ocrUploadId" class="hidden" accept="image/*" @change="handleImageChange" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2025-11-26 23:27:25 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import apiService from '../../services/apiService'
|
2025-11-26 23:55:59 +08:00
|
|
|
|
import { ref, onMounted, nextTick, computed, watch } from 'vue'
|
2025-11-26 23:27:25 +08:00
|
|
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
|
import dayjs from 'dayjs'
|
|
|
|
|
|
import QrcodeVue from 'qrcode.vue'
|
|
|
|
|
|
import { showLoading, hideLoading } from '../../components/loading'
|
|
|
|
|
|
import { showDialog } from 'vant';
|
|
|
|
|
|
import 'vant/es/dialog/style';
|
|
|
|
|
|
|
|
|
|
|
|
const ocrUploadId = ref<HTMLInputElement>();
|
|
|
|
|
|
const qrCodeWrapper = ref<HTMLDivElement | null>(null);
|
|
|
|
|
|
const qrImageSrc = ref('');
|
|
|
|
|
|
const router = useRouter();
|
2025-11-26 23:55:59 +08:00
|
|
|
|
const eligibilityError = ref('');
|
2025-11-26 23:27:25 +08:00
|
|
|
|
|
|
|
|
|
|
const STORAGE_KEY_USER = 'userAs';
|
|
|
|
|
|
const STORAGE_KEY_MARRIAGE = 'marriageOcr';
|
|
|
|
|
|
|
|
|
|
|
|
type MarriageStorage = {
|
|
|
|
|
|
marriageNo?: string
|
|
|
|
|
|
husbandName?: string
|
|
|
|
|
|
wifeName?: string
|
|
|
|
|
|
registerDate?: string
|
|
|
|
|
|
certificateHolder?: string
|
|
|
|
|
|
wifeId?: string
|
|
|
|
|
|
husbandId?: string
|
|
|
|
|
|
wifeGender?: string | null
|
|
|
|
|
|
husbandGender?: string | null
|
|
|
|
|
|
wifeBirthDate?: string
|
|
|
|
|
|
husbandBirthDate?: string
|
|
|
|
|
|
wifeNationality?: string
|
|
|
|
|
|
husbandNationality?: string
|
|
|
|
|
|
phone: string
|
|
|
|
|
|
smsCode: string
|
|
|
|
|
|
}
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
const syncQrImage = () => {
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
const canvas = qrCodeWrapper.value?.querySelector('canvas') as HTMLCanvasElement | null;
|
|
|
|
|
|
if (!canvas) {
|
|
|
|
|
|
qrImageSrc.value = '';
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
try {
|
|
|
|
|
|
qrImageSrc.value = canvas.toDataURL('image/png');
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('生成二维码图片失败:', error);
|
|
|
|
|
|
qrImageSrc.value = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 表单数据
|
|
|
|
|
|
const formData = ref({
|
|
|
|
|
|
phone: '',
|
|
|
|
|
|
smsCode: '',
|
|
|
|
|
|
verifyCode: false, // 验证状态
|
2025-11-26 23:27:25 +08:00
|
|
|
|
marriageNo: '', // 结婚证号
|
|
|
|
|
|
husbandName: '', // 男方姓名
|
|
|
|
|
|
wifeName: '', // 女方姓名
|
|
|
|
|
|
registerDate: '', // 登记日期
|
|
|
|
|
|
certificateHolder: '',
|
|
|
|
|
|
husbandId: '',
|
|
|
|
|
|
wifeId: '',
|
|
|
|
|
|
husbandGender: '',
|
|
|
|
|
|
wifeGender: '',
|
|
|
|
|
|
husbandBirthDate: '',
|
|
|
|
|
|
wifeBirthDate: '',
|
|
|
|
|
|
husbandNationality: '',
|
|
|
|
|
|
wifeNationality: '',
|
|
|
|
|
|
qrCode: '', // 二维码
|
|
|
|
|
|
status: 0, // 状态 0-未核销 1-已核销
|
|
|
|
|
|
})
|
2025-11-26 23:55:59 +08:00
|
|
|
|
const isNextDisabled = computed(() => !formData.value.marriageNo || !!eligibilityError.value);
|
|
|
|
|
|
|
|
|
|
|
|
const activityInfo = ref<any>({}); // 活动信息
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
// 检查活动是否已结束
|
|
|
|
|
|
showLoading();
|
|
|
|
|
|
apiService.getCurrentActivity().then((response: any) => {
|
|
|
|
|
|
if (response.data) {
|
|
|
|
|
|
activityInfo.value = {
|
|
|
|
|
|
...response.data,
|
2025-11-27 11:45:40 +08:00
|
|
|
|
activityStartTime: dayjs(response.data.activityStartTime).format('YYYY年MM月DD日'),
|
|
|
|
|
|
activityEndTime: dayjs(response.data.activityEndTime).format('YYYY年MM月DD日'),
|
|
|
|
|
|
// activityStartTime: '2026年1月1日',
|
|
|
|
|
|
// activityEndTime: '2026年12月31日',
|
2025-11-26 23:55:59 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if (new Date(response.data.activityEndTime) < new Date()) {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: '活动已结束',
|
|
|
|
|
|
});
|
|
|
|
|
|
activityInfo.value.status = 2;
|
|
|
|
|
|
} else {
|
2025-11-26 23:27:25 +08:00
|
|
|
|
const user = JSON.parse(localStorage.getItem(STORAGE_KEY_USER) || '{}');
|
|
|
|
|
|
if (user.phone && user.smsCode) {
|
|
|
|
|
|
formData.value.phone = user.phone;
|
|
|
|
|
|
formData.value.smsCode = user.smsCode;
|
|
|
|
|
|
verifyCode(false);
|
|
|
|
|
|
}
|
2025-11-26 23:55:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
activityInfo.value.status = 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: error.msg || '获取活动信息失败,请稍后重试',
|
|
|
|
|
|
});
|
|
|
|
|
|
activityInfo.value.status = 3;
|
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const pickYear = (value?: string) => {
|
|
|
|
|
|
if (!value) return '';
|
|
|
|
|
|
return value.trim().slice(0, 4);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const computeEligibilityError = () => {
|
2025-11-27 11:28:32 +08:00
|
|
|
|
if (!formData.value.marriageNo ||
|
|
|
|
|
|
!formData.value.registerDate ||
|
|
|
|
|
|
!formData.value.husbandId ||
|
|
|
|
|
|
!formData.value.wifeId ||
|
|
|
|
|
|
!formData.value.wifeName ||
|
|
|
|
|
|
!formData.value.husbandName) {
|
|
|
|
|
|
return '结婚登记号信息提取失败,请重试!';
|
|
|
|
|
|
}
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
if (!formData.value.marriageNo.startsWith('640')) {
|
2025-11-27 10:46:02 +08:00
|
|
|
|
return '此结婚登记号不符合本次活动,谢谢!';
|
2025-11-26 23:55:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const registerYear = pickYear(formData.value.registerDate);
|
|
|
|
|
|
const startYear = pickYear(activityInfo.value.activityStartTime);
|
|
|
|
|
|
const endYear = pickYear(activityInfo.value.activityEndTime);
|
|
|
|
|
|
|
|
|
|
|
|
if (![startYear, endYear].includes(registerYear)) {
|
2025-11-27 10:46:02 +08:00
|
|
|
|
return `此结婚登记号不符合本次活动,谢谢!`;
|
2025-11-26 23:55:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => [
|
|
|
|
|
|
formData.value.marriageNo,
|
|
|
|
|
|
formData.value.registerDate,
|
2025-11-27 11:28:32 +08:00
|
|
|
|
formData.value.wifeId,
|
|
|
|
|
|
formData.value.wifeName,
|
|
|
|
|
|
formData.value.husbandId,
|
|
|
|
|
|
formData.value.husbandName,
|
2025-11-26 23:55:59 +08:00
|
|
|
|
activityInfo.value.activityStartTime,
|
|
|
|
|
|
activityInfo.value.activityEndTime,
|
|
|
|
|
|
],
|
|
|
|
|
|
() => {
|
|
|
|
|
|
const nextReason = computeEligibilityError();
|
|
|
|
|
|
const hasNewReason = nextReason && nextReason !== eligibilityError.value;
|
|
|
|
|
|
eligibilityError.value = nextReason;
|
|
|
|
|
|
if (hasNewReason) {
|
|
|
|
|
|
showDialog({ message: nextReason });
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 倒计时
|
|
|
|
|
|
const countdown = ref(0)
|
|
|
|
|
|
let countdownTimer: number | null = null
|
|
|
|
|
|
|
2025-11-26 23:27:25 +08:00
|
|
|
|
const clearPhone = () => {
|
|
|
|
|
|
formData.value.phone = '';
|
|
|
|
|
|
formData.value.smsCode = '';
|
|
|
|
|
|
formData.value.verifyCode = false;
|
|
|
|
|
|
formData.value.marriageNo = '';
|
|
|
|
|
|
formData.value.husbandName = '';
|
|
|
|
|
|
formData.value.wifeName = '';
|
|
|
|
|
|
formData.value.registerDate = '';
|
|
|
|
|
|
formData.value.certificateHolder = '';
|
|
|
|
|
|
formData.value.husbandId = '';
|
|
|
|
|
|
formData.value.wifeId = '';
|
|
|
|
|
|
formData.value.husbandGender = '';
|
|
|
|
|
|
formData.value.wifeGender = '';
|
|
|
|
|
|
formData.value.husbandBirthDate = '';
|
|
|
|
|
|
formData.value.wifeBirthDate = '';
|
|
|
|
|
|
formData.value.husbandNationality = '';
|
|
|
|
|
|
formData.value.wifeNationality = '';
|
|
|
|
|
|
formData.value.qrCode = '';
|
|
|
|
|
|
formData.value.status = 0;
|
|
|
|
|
|
qrImageSrc.value = '';
|
|
|
|
|
|
countdown.value = 0;
|
|
|
|
|
|
if (countdownTimer) {
|
|
|
|
|
|
clearInterval(countdownTimer);
|
|
|
|
|
|
countdownTimer = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY_USER);
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY_MARRIAGE);
|
|
|
|
|
|
if (ocrUploadId.value) ocrUploadId.value.value = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 发送验证码
|
|
|
|
|
|
const sendVerificationCode = () => {
|
2025-11-26 23:55:59 +08:00
|
|
|
|
const { phone } = formData.value
|
|
|
|
|
|
|
|
|
|
|
|
// 简单手机号验证
|
|
|
|
|
|
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: '请输入正确的手机号码',
|
|
|
|
|
|
});
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
showLoading();
|
2025-11-26 23:27:25 +08:00
|
|
|
|
// 调用发送短信验证码接口
|
|
|
|
|
|
apiService.sendSms({
|
|
|
|
|
|
mobile: phone,
|
|
|
|
|
|
type: 3,
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY_MARRIAGE);
|
|
|
|
|
|
formData.value.verifyCode = false;
|
|
|
|
|
|
formData.value.smsCode = '';
|
|
|
|
|
|
formData.value.qrCode = '';
|
|
|
|
|
|
|
|
|
|
|
|
// 开始倒计时
|
2025-11-26 23:55:59 +08:00
|
|
|
|
countdown.value = 60
|
|
|
|
|
|
|
|
|
|
|
|
if (countdownTimer) {
|
|
|
|
|
|
clearInterval(countdownTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
countdownTimer = window.setInterval(() => {
|
|
|
|
|
|
if (countdown.value > 0) {
|
|
|
|
|
|
countdown.value--
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if (countdownTimer) {
|
|
|
|
|
|
clearInterval(countdownTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 1000)
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
console.error('发送验证码失败:', error)
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: error.msg || '验证码发送失败,请稍后重试',
|
|
|
|
|
|
});
|
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证验证码
|
|
|
|
|
|
const verifyCode = (flag: any) => {
|
|
|
|
|
|
const { smsCode } = formData.value
|
|
|
|
|
|
|
|
|
|
|
|
if (smsCode.length !== 6 || !/^\d{6}$/.test(smsCode)) {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: '请输入6位数字验证码',
|
|
|
|
|
|
});
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
showLoading();
|
|
|
|
|
|
// 调用验证短信验证码接口
|
2025-11-26 23:27:25 +08:00
|
|
|
|
apiService.verifySms({
|
|
|
|
|
|
mobile: formData.value.phone,
|
|
|
|
|
|
smsCode: smsCode,
|
|
|
|
|
|
type: 3,
|
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
|
localStorage.setItem(STORAGE_KEY_USER, JSON.stringify({
|
|
|
|
|
|
phone: formData.value.phone,
|
|
|
|
|
|
smsCode: formData.value.smsCode,
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
formData.value.verifyCode = true;
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 验证成功后重置倒计时
|
|
|
|
|
|
countdown.value = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 验证成功如果有二维码,保存到表单
|
2025-11-26 23:27:25 +08:00
|
|
|
|
if (res?.data?.code) {
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
...formData.value,
|
|
|
|
|
|
qrCode: res.data.code,
|
|
|
|
|
|
status: res.data.status,
|
2025-11-26 23:55:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
syncQrImage();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
...formData.value,
|
|
|
|
|
|
marriageNo: "",
|
|
|
|
|
|
husbandName: "",
|
|
|
|
|
|
wifeName: "",
|
|
|
|
|
|
registerDate: "",
|
2025-11-26 23:27:25 +08:00
|
|
|
|
qrCode: ""
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
restoreMarriageInfo();
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY_USER);
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY_MARRIAGE);
|
|
|
|
|
|
|
|
|
|
|
|
flag && showDialog({
|
|
|
|
|
|
message: error.msg || '验证码验证失败,请重试',
|
|
|
|
|
|
});
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
...formData.value,
|
|
|
|
|
|
smsCode: "",
|
|
|
|
|
|
marriageNo: "",
|
|
|
|
|
|
husbandName: "",
|
|
|
|
|
|
wifeName: "",
|
|
|
|
|
|
registerDate: "",
|
|
|
|
|
|
certificateHolder: "",
|
|
|
|
|
|
husbandId: "",
|
|
|
|
|
|
wifeId: "",
|
|
|
|
|
|
husbandGender: "",
|
|
|
|
|
|
wifeGender: "",
|
|
|
|
|
|
husbandBirthDate: "",
|
|
|
|
|
|
wifeBirthDate: "",
|
|
|
|
|
|
husbandNationality: "",
|
|
|
|
|
|
wifeNationality: "",
|
|
|
|
|
|
qrCode: ""
|
|
|
|
|
|
}
|
|
|
|
|
|
qrImageSrc.value = '';
|
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const restoreMarriageInfo = () => {
|
|
|
|
|
|
const user = JSON.parse(localStorage.getItem(STORAGE_KEY_USER) || '{}');
|
|
|
|
|
|
const marriageStorage: MarriageStorage | null = (() => {
|
|
|
|
|
|
const raw = localStorage.getItem(STORAGE_KEY_MARRIAGE);
|
|
|
|
|
|
if (!raw) return null;
|
|
|
|
|
|
try {
|
|
|
|
|
|
return JSON.parse(raw);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
if (!marriageStorage || marriageStorage.smsCode !== user.smsCode) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
...formData.value,
|
|
|
|
|
|
marriageNo: marriageStorage.marriageNo || '',
|
|
|
|
|
|
husbandName: marriageStorage.husbandName || '',
|
|
|
|
|
|
wifeName: marriageStorage.wifeName || '',
|
|
|
|
|
|
registerDate: marriageStorage.registerDate || '',
|
|
|
|
|
|
certificateHolder: marriageStorage.certificateHolder || '',
|
|
|
|
|
|
husbandId: marriageStorage.husbandId || '',
|
|
|
|
|
|
wifeId: marriageStorage.wifeId || '',
|
|
|
|
|
|
husbandGender: marriageStorage.husbandGender || '',
|
|
|
|
|
|
wifeGender: marriageStorage.wifeGender || '',
|
|
|
|
|
|
husbandBirthDate: marriageStorage.husbandBirthDate || '',
|
|
|
|
|
|
wifeBirthDate: marriageStorage.wifeBirthDate || '',
|
|
|
|
|
|
husbandNationality: marriageStorage.husbandNationality || '',
|
|
|
|
|
|
wifeNationality: marriageStorage.wifeNationality || '',
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const persistMarriageInfo = () => {
|
|
|
|
|
|
const user = JSON.parse(localStorage.getItem(STORAGE_KEY_USER) || '{}');
|
|
|
|
|
|
if (!user?.smsCode || !formData.value.marriageNo) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const payload: MarriageStorage = {
|
|
|
|
|
|
phone: user.phone,
|
|
|
|
|
|
smsCode: user.smsCode,
|
|
|
|
|
|
marriageNo: formData.value.marriageNo,
|
|
|
|
|
|
husbandName: formData.value.husbandName,
|
|
|
|
|
|
wifeName: formData.value.wifeName,
|
|
|
|
|
|
registerDate: formData.value.registerDate,
|
|
|
|
|
|
certificateHolder: formData.value.certificateHolder,
|
|
|
|
|
|
husbandId: formData.value.husbandId,
|
|
|
|
|
|
wifeId: formData.value.wifeId,
|
|
|
|
|
|
husbandGender: formData.value.husbandGender,
|
|
|
|
|
|
wifeGender: formData.value.wifeGender,
|
|
|
|
|
|
husbandBirthDate: formData.value.husbandBirthDate,
|
|
|
|
|
|
wifeBirthDate: formData.value.wifeBirthDate,
|
|
|
|
|
|
husbandNationality: formData.value.husbandNationality,
|
|
|
|
|
|
wifeNationality: formData.value.wifeNationality,
|
|
|
|
|
|
};
|
|
|
|
|
|
localStorage.setItem(STORAGE_KEY_MARRIAGE, JSON.stringify(payload));
|
|
|
|
|
|
}
|
|
|
|
|
|
// 重置结婚证信息
|
|
|
|
|
|
function resetMarriageInfo() {
|
|
|
|
|
|
formData.value = {
|
|
|
|
|
|
...formData.value,
|
|
|
|
|
|
marriageNo: "",
|
|
|
|
|
|
husbandName: "",
|
|
|
|
|
|
wifeName: "",
|
|
|
|
|
|
registerDate: "",
|
|
|
|
|
|
certificateHolder: "",
|
|
|
|
|
|
husbandId: "",
|
|
|
|
|
|
wifeId: "",
|
|
|
|
|
|
husbandGender: "",
|
|
|
|
|
|
wifeGender: "",
|
|
|
|
|
|
husbandBirthDate: "",
|
|
|
|
|
|
wifeBirthDate: "",
|
|
|
|
|
|
husbandNationality: "",
|
|
|
|
|
|
wifeNationality: "",
|
|
|
|
|
|
qrCode: ""
|
|
|
|
|
|
}
|
|
|
|
|
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
|
|
|
|
|
localStorage.removeItem(STORAGE_KEY_MARRIAGE);
|
|
|
|
|
|
|
|
|
|
|
|
ocrUploadId.value?.click();
|
|
|
|
|
|
}
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理扫描点击事件
|
|
|
|
|
|
const handleScanClick = () => {
|
|
|
|
|
|
ocrUploadId.value?.click()
|
|
|
|
|
|
}
|
|
|
|
|
|
const handleImageChange = () => {
|
|
|
|
|
|
const file = ocrUploadId.value?.files?.[0]
|
|
|
|
|
|
if (!file) {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: '请选择图片文件',
|
|
|
|
|
|
});
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 调用上传OCR图片接口
|
|
|
|
|
|
const formFormData = new FormData()
|
|
|
|
|
|
formFormData.append('file', file)
|
|
|
|
|
|
|
|
|
|
|
|
showLoading();
|
|
|
|
|
|
apiService.uploadOcrImage(formFormData).then((response: any) => {
|
2025-11-26 23:27:25 +08:00
|
|
|
|
apiService.marriageParseOcrInfo({
|
|
|
|
|
|
mobile: formData.value.phone,
|
|
|
|
|
|
smsCode: formData.value.smsCode,
|
|
|
|
|
|
uploadId: response.data.uploadId,
|
|
|
|
|
|
}).then((res: any) => {
|
|
|
|
|
|
if (res?.data?.parsed?.marriageNo) {
|
|
|
|
|
|
formData.value.marriageNo = res?.data?.parsed?.marriageNo || '';
|
|
|
|
|
|
formData.value.husbandName = res?.data?.parsed?.husbandName || '';
|
|
|
|
|
|
formData.value.wifeName = res?.data?.parsed?.wifeName || '';
|
|
|
|
|
|
formData.value.registerDate = res?.data?.parsed?.registerDate || '';
|
|
|
|
|
|
formData.value.certificateHolder = res?.data?.parsed?.certificateHolder || '';
|
|
|
|
|
|
formData.value.husbandId = res?.data?.parsed?.husbandId || '';
|
|
|
|
|
|
formData.value.wifeId = res?.data?.parsed?.wifeId || '';
|
|
|
|
|
|
formData.value.husbandGender = res?.data?.parsed?.husbandGender || '';
|
|
|
|
|
|
formData.value.wifeGender = res?.data?.parsed?.wifeGender || '';
|
|
|
|
|
|
formData.value.husbandBirthDate = res?.data?.parsed?.husbandBirthDate || '';
|
|
|
|
|
|
formData.value.wifeBirthDate = res?.data?.parsed?.wifeBirthDate || '';
|
|
|
|
|
|
formData.value.husbandNationality = res?.data?.parsed?.husbandNationality || '';
|
|
|
|
|
|
formData.value.wifeNationality = res?.data?.parsed?.wifeNationality || '';
|
|
|
|
|
|
persistMarriageInfo();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
showDialog({
|
2025-11-27 10:46:02 +08:00
|
|
|
|
message: '结婚证上传失败,请重试!',
|
2025-11-26 23:27:25 +08:00
|
|
|
|
});
|
|
|
|
|
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
2025-11-26 23:55:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
showDialog({
|
2025-11-27 10:46:02 +08:00
|
|
|
|
message: error?.msg || '结婚证上传失败,请重试!',
|
2025-11-26 23:55:59 +08:00
|
|
|
|
});
|
|
|
|
|
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
|
|
|
|
|
}).finally(() => {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
})
|
|
|
|
|
|
}).catch((error) => {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: error?.msg || '图片上传失败,请稍后重试',
|
|
|
|
|
|
});
|
|
|
|
|
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
2025-11-26 23:27:25 +08:00
|
|
|
|
hideLoading();
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const goToIdUpload = () => {
|
|
|
|
|
|
if (!formData.value.marriageNo) {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: '请先完成结婚证识别',
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-26 23:55:59 +08:00
|
|
|
|
if (eligibilityError.value) {
|
|
|
|
|
|
showDialog({
|
|
|
|
|
|
message: eligibilityError.value,
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-11-26 23:27:25 +08:00
|
|
|
|
persistMarriageInfo();
|
|
|
|
|
|
router.push('/idcard');
|
|
|
|
|
|
}
|
2025-11-26 23:55:59 +08:00
|
|
|
|
|
|
|
|
|
|
// 组件卸载时清理定时器
|
|
|
|
|
|
import { onUnmounted } from 'vue'
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
if (countdownTimer) {
|
|
|
|
|
|
clearInterval(countdownTimer)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|