feat: 实现结婚证领取流程完整功能
- 添加dayjs依赖用于日期处理 - 新增Dialog组件用于消息提示 - 完善短信验证码发送与校验逻辑 - 实现OCR识别结婚证信息功能 - 完成领取前校验与提交接口对接 - 根据活动状态显示不同界面
This commit is contained in:
parent
0ba15bd9db
commit
0d3462a8f5
|
|
@ -11,6 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tailwindcss/vite": "^4.1.17",
|
"@tailwindcss/vite": "^4.1.17",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.13.2",
|
||||||
|
"dayjs": "^1.11.19",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"vue": "^3.5.24",
|
"vue": "^3.5.24",
|
||||||
"vue-router": "^4.6.3"
|
"vue-router": "^4.6.3"
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,9 @@ importers:
|
||||||
axios:
|
axios:
|
||||||
specifier: ^1.13.2
|
specifier: ^1.13.2
|
||||||
version: 1.13.2
|
version: 1.13.2
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.19
|
||||||
|
version: 1.11.19
|
||||||
tailwindcss:
|
tailwindcss:
|
||||||
specifier: ^4.1.17
|
specifier: ^4.1.17
|
||||||
version: 4.1.17
|
version: 4.1.17
|
||||||
|
|
@ -530,6 +533,9 @@ packages:
|
||||||
csstype@3.1.3:
|
csstype@3.1.3:
|
||||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||||
|
|
||||||
|
dayjs@1.11.19:
|
||||||
|
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
|
||||||
|
|
||||||
delayed-stream@1.0.0:
|
delayed-stream@1.0.0:
|
||||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||||
engines: {node: '>=0.4.0'}
|
engines: {node: '>=0.4.0'}
|
||||||
|
|
@ -1203,6 +1209,8 @@ snapshots:
|
||||||
|
|
||||||
csstype@3.1.3: {}
|
csstype@3.1.3: {}
|
||||||
|
|
||||||
|
dayjs@1.11.19: {}
|
||||||
|
|
||||||
delayed-stream@1.0.0: {}
|
delayed-stream@1.0.0: {}
|
||||||
|
|
||||||
detect-libc@2.1.2: {}
|
detect-libc@2.1.2: {}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,251 @@
|
||||||
|
<template>
|
||||||
|
<Teleport to="body">
|
||||||
|
<Transition name="dialog">
|
||||||
|
<div v-if="visible" class="dialog-overlay" @click.self="handleOverlayClick">
|
||||||
|
<div class="dialog-container">
|
||||||
|
<!-- 对话框头部 -->
|
||||||
|
<div class="dialog-header" v-if="title || $slots.title">
|
||||||
|
<slot name="title">{{ title }}</slot>
|
||||||
|
<button v-if="closable" class="dialog-close" @click="handleClose">
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M12 4L4 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
<path d="M4 4L12 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 对话框内容 -->
|
||||||
|
<div class="dialog-body">
|
||||||
|
<slot>{{ message }}</slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 对话框底部按钮区域 -->
|
||||||
|
<div class="dialog-footer" v-if="showFooter">
|
||||||
|
<slot name="footer">
|
||||||
|
<button v-if="showCancelButton" class="dialog-button dialog-button-cancel" @click="handleCancel">
|
||||||
|
{{ cancelButtonText }}
|
||||||
|
</button>
|
||||||
|
<button class="dialog-button dialog-button-confirm" @click="handleConfirm">
|
||||||
|
{{ confirmButtonText }}
|
||||||
|
</button>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, defineEmits } from 'vue'
|
||||||
|
|
||||||
|
// Props 定义
|
||||||
|
const props = defineProps({
|
||||||
|
// 是否显示对话框
|
||||||
|
visible: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
// 对话框标题
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 对话框消息内容(简单文本内容)
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 是否显示关闭按钮
|
||||||
|
closable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 是否显示底部按钮区域
|
||||||
|
showFooter: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 是否显示取消按钮
|
||||||
|
showCancelButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 确认按钮文本
|
||||||
|
confirmButtonText: {
|
||||||
|
type: String,
|
||||||
|
default: '确认'
|
||||||
|
},
|
||||||
|
// 取消按钮文本
|
||||||
|
cancelButtonText: {
|
||||||
|
type: String,
|
||||||
|
default: '取消'
|
||||||
|
},
|
||||||
|
// 点击遮罩层是否可以关闭对话框
|
||||||
|
closeOnClickOverlay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Emits 定义
|
||||||
|
const emit = defineEmits<{
|
||||||
|
// 关闭事件
|
||||||
|
close: []
|
||||||
|
// 确认事件
|
||||||
|
confirm: []
|
||||||
|
// 取消事件
|
||||||
|
cancel: []
|
||||||
|
}>()
|
||||||
|
|
||||||
|
// 处理关闭操作
|
||||||
|
const handleClose = () => {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理确认操作
|
||||||
|
const handleConfirm = () => {
|
||||||
|
emit('confirm')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理取消操作
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('cancel')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理点击遮罩层
|
||||||
|
const handleOverlayClick = () => {
|
||||||
|
if (props.closeOnClickOverlay) {
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 遮罩层样式 */
|
||||||
|
.dialog-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对话框容器样式 */
|
||||||
|
.dialog-container {
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||||
|
width: 90%;
|
||||||
|
max-width: 500px;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对话框头部样式 */
|
||||||
|
.dialog-header {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 关闭按钮样式 */
|
||||||
|
.dialog-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-close:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对话框内容样式 */
|
||||||
|
.dialog-body {
|
||||||
|
padding: 20px;
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对话框底部样式 */
|
||||||
|
.dialog-footer {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.dialog-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: none;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-button-cancel {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-button-cancel:hover {
|
||||||
|
background-color: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-button-confirm {
|
||||||
|
background-color: #1890ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-button-confirm:hover {
|
||||||
|
background-color: #40a9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动画样式 */
|
||||||
|
.dialog-enter-active,
|
||||||
|
.dialog-leave-active {
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-enter-from,
|
||||||
|
.dialog-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-enter-active .dialog-container,
|
||||||
|
.dialog-leave-active .dialog-container {
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-enter-from .dialog-container {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-leave-to .dialog-container {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,162 @@
|
||||||
|
import { createApp, h, nextTick } from 'vue'
|
||||||
|
import Dialog from './Dialog.vue'
|
||||||
|
|
||||||
|
// 对话框配置接口
|
||||||
|
export interface DialogOptions {
|
||||||
|
// 对话框标题
|
||||||
|
title?: string
|
||||||
|
// 对话框内容
|
||||||
|
message?: string
|
||||||
|
// 是否显示关闭按钮
|
||||||
|
closable?: boolean
|
||||||
|
// 是否显示底部按钮区域
|
||||||
|
showFooter?: boolean
|
||||||
|
// 是否显示取消按钮
|
||||||
|
showCancelButton?: boolean
|
||||||
|
// 确认按钮文本
|
||||||
|
confirmButtonText?: string
|
||||||
|
// 取消按钮文本
|
||||||
|
cancelButtonText?: string
|
||||||
|
// 点击遮罩层是否可以关闭对话框
|
||||||
|
closeOnClickOverlay?: boolean
|
||||||
|
// 确认回调函数
|
||||||
|
onConfirm?: () => void
|
||||||
|
// 取消回调函数
|
||||||
|
onCancel?: () => void
|
||||||
|
// 关闭回调函数
|
||||||
|
onClose?: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dialog 实例接口
|
||||||
|
interface DialogInstance {
|
||||||
|
show: () => void
|
||||||
|
hide: () => void
|
||||||
|
close: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建并显示一个对话框
|
||||||
|
* @param options 对话框配置选项
|
||||||
|
* @returns 对话框实例,可用于手动控制显示和隐藏
|
||||||
|
*/
|
||||||
|
export function showDialog(options: DialogOptions): DialogInstance {
|
||||||
|
// 默认配置
|
||||||
|
const defaultOptions: DialogOptions = {
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
closable: true,
|
||||||
|
showFooter: true,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
closeOnClickOverlay: true,
|
||||||
|
onConfirm: () => {},
|
||||||
|
onCancel: () => {},
|
||||||
|
onClose: () => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并配置
|
||||||
|
const mergedOptions = { ...defaultOptions, ...options }
|
||||||
|
|
||||||
|
// 创建一个容器元素
|
||||||
|
const container = document.createElement('div')
|
||||||
|
|
||||||
|
// 对话框状态
|
||||||
|
let visible = true
|
||||||
|
|
||||||
|
// 创建应用实例
|
||||||
|
const app = createApp({
|
||||||
|
render() {
|
||||||
|
return h(Dialog, {
|
||||||
|
visible,
|
||||||
|
title: mergedOptions.title,
|
||||||
|
message: mergedOptions.message,
|
||||||
|
closable: mergedOptions.closable,
|
||||||
|
showFooter: mergedOptions.showFooter,
|
||||||
|
showCancelButton: mergedOptions.showCancelButton,
|
||||||
|
confirmButtonText: mergedOptions.confirmButtonText,
|
||||||
|
cancelButtonText: mergedOptions.cancelButtonText,
|
||||||
|
closeOnClickOverlay: mergedOptions.closeOnClickOverlay,
|
||||||
|
onConfirm: () => {
|
||||||
|
mergedOptions.onConfirm?.()
|
||||||
|
hideDialog()
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
mergedOptions.onCancel?.()
|
||||||
|
hideDialog()
|
||||||
|
},
|
||||||
|
onClose: () => {
|
||||||
|
mergedOptions.onClose?.()
|
||||||
|
hideDialog()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 隐藏对话框
|
||||||
|
function hideDialog() {
|
||||||
|
visible = false
|
||||||
|
// 等待动画完成后卸载组件
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
app.unmount()
|
||||||
|
if (container.parentNode) {
|
||||||
|
container.parentNode.removeChild(container)
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示对话框(默认为显示状态)
|
||||||
|
function showDialog() {
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 挂载组件
|
||||||
|
app.mount(container)
|
||||||
|
document.body.appendChild(container)
|
||||||
|
|
||||||
|
// 返回控制方法
|
||||||
|
return {
|
||||||
|
show: showDialog,
|
||||||
|
hide: hideDialog,
|
||||||
|
close: hideDialog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示一个确认对话框
|
||||||
|
* @param message 确认消息
|
||||||
|
* @param title 对话框标题
|
||||||
|
* @param onConfirm 确认回调
|
||||||
|
* @returns 对话框实例
|
||||||
|
*/
|
||||||
|
export function showConfirmDialog(message: string, title: string = '确认', onConfirm?: () => void): DialogInstance {
|
||||||
|
return showDialog({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
showCancelButton: true,
|
||||||
|
onConfirm
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示一个提示对话框
|
||||||
|
* @param message 提示消息
|
||||||
|
* @param title 对话框标题
|
||||||
|
* @returns 对话框实例
|
||||||
|
*/
|
||||||
|
export function showAlertDialog(message: string, title: string = '提示'): DialogInstance {
|
||||||
|
return showDialog({
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
showCancelButton: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认导出
|
||||||
|
export default {
|
||||||
|
show: showDialog,
|
||||||
|
confirm: showConfirmDialog,
|
||||||
|
alert: showAlertDialog
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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">
|
<div class="mb-5 flex items-center gap-3 border border-gray-200 rounded-lg px-4">
|
||||||
<input v-model="formData.phone" type="tel" placeholder="请输入手机号码" maxlength="11"
|
<input v-model="formData.phone" type="tel" placeholder="请输入手机号码" maxlength="11"
|
||||||
|
|
@ -33,24 +35,27 @@
|
||||||
|
|
||||||
<button @click="sendVerificationCode" :disabled="countdown > 0 || formData.phone.length !== 11"
|
<button @click="sendVerificationCode" :disabled="countdown > 0 || formData.phone.length !== 11"
|
||||||
class="h-11 rounded-lg text-sm text-[#E8424D] transition-all disabled:text-[#8a8a8a]">
|
class="h-11 rounded-lg text-sm text-[#E8424D] transition-all disabled:text-[#8a8a8a]">
|
||||||
{{ countdown > 0 ? `${countdown}秒后重发` : '获取验证码' }}
|
{{ countdown > 0 ? `${countdown}秒后重发` : formData.verifyCode ? '重新获取' : '获取验证码' }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 验证码输入 -->
|
<!-- 验证码输入 -->
|
||||||
<div class="mb-5 flex items-center gap-3 border border-gray-200 rounded-lg px-4">
|
<div class="mb-5 flex items-center gap-3 border border-gray-200 rounded-lg px-4">
|
||||||
<input v-model="formData.verificationCode" type="text" placeholder="请输入验证码" maxlength="6"
|
<input :disabled="formData.verifyCode" v-model="formData.smsCode" type="text" placeholder="请输入验证码"
|
||||||
class="outline-none focus:border-[#E8424D] transition-colors flex-1 h-11" />
|
maxlength="6" class="outline-none focus:border-[#E8424D] transition-colors flex-1 h-11" />
|
||||||
<button @click="verifyCode" :disabled="formData.verificationCode.length !== 6"
|
<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]">
|
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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="formData.verifyCode">
|
||||||
<!-- 结婚证信息 -->
|
<!-- 结婚证信息 -->
|
||||||
<div class="mb-6">
|
<div class="mb-6">
|
||||||
<h3 class="text-base text-gray-800 mb-3 font-medium">结婚证信息:</h3>
|
<h3 class="text-base text-gray-800 mb-3 font-medium">结婚证信息:</h3>
|
||||||
<div v-if="false" class="border border-gray-200 rounded-lg p-5 flex items-center justify-center">
|
<div v-if="!formData.marriageNo"
|
||||||
|
class="border border-gray-200 rounded-lg p-5 flex items-center justify-center">
|
||||||
<div class="relative inline-block" @click="handleScanClick">
|
<div class="relative inline-block" @click="handleScanClick">
|
||||||
<img src="/扫描.png" alt="" class="h-20">
|
<img src="/扫描.png" alt="" class="h-20">
|
||||||
<div
|
<div
|
||||||
|
|
@ -60,7 +65,8 @@
|
||||||
<input type="file" ref="ocrUploadId" class="hidden" accept="image/*" @change="handleImageChange" />
|
<input type="file" ref="ocrUploadId" class="hidden" accept="image/*" @change="handleImageChange" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full flex flex-col items-start border border-gray-200 rounded-lg p-5 bg-[#FAFAFA]">
|
<div v-if="formData.marriageNo"
|
||||||
|
class="w-full flex flex-col items-start border border-gray-200 rounded-lg p-5 bg-[#FAFAFA]">
|
||||||
<div>
|
<div>
|
||||||
<span class="inline-block w-25 text-right">结婚证字号:</span>
|
<span class="inline-block w-25 text-right">结婚证字号:</span>
|
||||||
<span class="pl-2">{{ 11111 }}</span>
|
<span class="pl-2">{{ 11111 }}</span>
|
||||||
|
|
@ -77,18 +83,29 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 提交按钮 -->
|
<!-- 提交按钮 -->
|
||||||
<button @click="submitForm"
|
<button @click="submitForm" :disabled="!formData.marriageNo"
|
||||||
class="w-full h-12 bg-gradient-to-r from-[#E8424D] to-[#FF7A7A] text-white rounded-full text-lg font-bold transition-all hover:opacity-90 active:scale-98">
|
class="w-full h-12 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]">
|
||||||
提交领取喜礼
|
提交领取喜礼
|
||||||
</button>
|
</button>
|
||||||
</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="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-[#E8424D] text-lg font-semibold mb-2">活动已结束</div>
|
||||||
<div class="text-gray-600 text-sm">感谢您的参与,敬请期待下一次活动</div>
|
<div class="text-gray-600 text-sm">【{{ activityInfo.activityName }}】活动已结束,感谢您的参与,敬请期待下一次活动</div>
|
||||||
<div class="mt-4 text-xs text-gray-500">活动时间:2024年1月1日 - 2024年6月30日</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>
|
||||||
|
|
||||||
|
|
@ -101,20 +118,40 @@
|
||||||
import message from '../../components/message';
|
import message from '../../components/message';
|
||||||
import apiService from '../../services/apiService'
|
import apiService from '../../services/apiService'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
const ocrUploadId = ref<HTMLInputElement>();
|
const ocrUploadId = ref<HTMLInputElement>();
|
||||||
|
|
||||||
// 表单数据
|
// 表单数据
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
phone: '',
|
phone: '',
|
||||||
verificationCode: ''
|
smsCode: '',
|
||||||
|
verifyCode: false, // 验证状态
|
||||||
|
marriageNo: '', // 结婚证号
|
||||||
|
husbandName: '', // 男方姓名
|
||||||
|
wifeName: '', // 女方姓名
|
||||||
|
registerDate: '', // 登记日期
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const activityInfo = ref<any>({}); // 活动信息
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 检查活动是否已结束
|
// 检查活动是否已结束
|
||||||
apiService.getCurrentActivity().then((response: any) => {
|
apiService.getCurrentActivity().then((response: any) => {
|
||||||
console.log(response);
|
if (response.data) {
|
||||||
|
activityInfo.value = {
|
||||||
|
...response.data,
|
||||||
|
activityStartTime: dayjs(response.data.activityStartTime).format('YYYY年MM月DD日'),
|
||||||
|
activityEndTime: dayjs(response.data.activityEndTime).format('YYYY年MM月DD日'),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (new Date(response.data.activityEndTime) < new Date()) {
|
||||||
|
message.error('活动已结束');
|
||||||
|
activityInfo.value.status = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
message.error('获取活动信息失败,请稍后重试')
|
||||||
|
activityInfo.value.status = 3;
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -135,8 +172,11 @@ const sendVerificationCode = () => {
|
||||||
// 调用发送短信验证码接口
|
// 调用发送短信验证码接口
|
||||||
apiService.sendSms({
|
apiService.sendSms({
|
||||||
mobile: phone,
|
mobile: phone,
|
||||||
type: 0,
|
type: 3,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
formData.value.verifyCode = false;
|
||||||
|
formData.value.smsCode = '';
|
||||||
|
|
||||||
// 开始倒计时
|
// 开始倒计时
|
||||||
countdown.value = 60
|
countdown.value = 60
|
||||||
|
|
||||||
|
|
@ -161,19 +201,20 @@ const sendVerificationCode = () => {
|
||||||
|
|
||||||
// 验证验证码
|
// 验证验证码
|
||||||
const verifyCode = () => {
|
const verifyCode = () => {
|
||||||
const { verificationCode } = formData.value
|
const { smsCode } = formData.value
|
||||||
|
|
||||||
if (verificationCode.length !== 6 || !/^\d{6}$/.test(verificationCode)) {
|
if (smsCode.length !== 6 || !/^\d{6}$/.test(smsCode)) {
|
||||||
message.error('请输入6位数字验证码')
|
message.error('请输入6位数字验证码')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用验证短信验证码接口
|
// 调用验证短信验证码接口
|
||||||
apiService.login({
|
apiService.verifySms({
|
||||||
mobile: formData.value.phone,
|
mobile: formData.value.phone,
|
||||||
smsCode: verificationCode,
|
smsCode: smsCode,
|
||||||
|
type: 3,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
message.success('验证码验证成功')
|
formData.value.verifyCode = true;
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('验证验证码失败:', error)
|
console.error('验证验证码失败:', error)
|
||||||
message.error('验证码验证失败,请重试')
|
message.error('验证码验证失败,请重试')
|
||||||
|
|
@ -196,27 +237,27 @@ const handleImageChange = () => {
|
||||||
formFormData.append('file', file)
|
formFormData.append('file', file)
|
||||||
|
|
||||||
apiService.uploadOcrImage(formFormData).then((response: any) => {
|
apiService.uploadOcrImage(formFormData).then((response: any) => {
|
||||||
apiService.sendSms({
|
|
||||||
mobile: formData.value.phone,
|
|
||||||
type: 2, // 2 表示OCR识别 code 获取
|
|
||||||
}).then((res) => {
|
|
||||||
apiService.parseOcrInfo({
|
apiService.parseOcrInfo({
|
||||||
mobile: formData.value.phone,
|
mobile: formData.value.phone,
|
||||||
smsCode: res.data,
|
smsCode: formData.value.smsCode,
|
||||||
uploadId: response.data.uploadId,
|
uploadId: response.data.uploadId,
|
||||||
}).then((res) => {
|
}).then((res: any) => {
|
||||||
console.log(res);
|
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;
|
||||||
|
} else {
|
||||||
|
message.error('解析OCR信息失败,请稍后重试');
|
||||||
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
||||||
|
}
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('解析OCR信息失败:', error)
|
|
||||||
message.error('解析OCR信息失败,请稍后重试')
|
message.error('解析OCR信息失败,请稍后重试')
|
||||||
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
||||||
})
|
})
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error('发送验证码失败:', error)
|
|
||||||
message.error('验证码发送失败,请稍后重试')
|
|
||||||
})
|
|
||||||
}).catch((error) => {
|
|
||||||
console.error('上传OCR图片失败:', error)
|
|
||||||
message.error('图片上传失败,请稍后重试')
|
message.error('图片上传失败,请稍后重试')
|
||||||
|
ocrUploadId.value && (ocrUploadId.value.value = '');
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -224,14 +265,22 @@ const handleImageChange = () => {
|
||||||
|
|
||||||
// 提交表单
|
// 提交表单
|
||||||
const submitForm = () => {
|
const submitForm = () => {
|
||||||
if (!formData.value.phone || !formData.value.verificationCode) {
|
if (!formData.value.phone || !formData.value.smsCode) {
|
||||||
message.error('请完善所有信息')
|
message.error('请完善所有信息')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模拟提交
|
apiService.receiveCheck({
|
||||||
console.log('提交表单数据:', formData.value)
|
marriageNo: formData.value.marriageNo,
|
||||||
message.success('提交成功,正在处理您的请求...')
|
receiveName: formData.value.husbandName,
|
||||||
|
receiveMobile: formData.value.phone,
|
||||||
|
code: formData.value.smsCode,
|
||||||
|
smsCode: formData.value.smsCode,
|
||||||
|
}).then((res: any) => {
|
||||||
|
console.log(res);
|
||||||
|
}).catch((error) => {
|
||||||
|
message.error('领取失败,请稍后重试')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 组件卸载时清理定时器
|
// 组件卸载时清理定时器
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@ export interface MarriageCodeListVO {
|
||||||
|
|
||||||
export interface CommSmsDTO {
|
export interface CommSmsDTO {
|
||||||
mobile: string
|
mobile: string
|
||||||
type: 0 | 1 | 2; // 0=登录;1=兑换领取;2=OCR识别
|
smsCode?: string
|
||||||
|
type: 0 | 1 | 2 | 3; // 0=登录;1=兑换领取;2=OCR识别;3=本流程使用
|
||||||
}
|
}
|
||||||
|
|
||||||
// API服务实现
|
// API服务实现
|
||||||
|
|
@ -79,6 +80,11 @@ export const apiService = {
|
||||||
return request.post('/marriage/common/sms', data)
|
return request.post('/marriage/common/sms', data)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 校验短信验证码
|
||||||
|
verifySms(data: CommSmsDTO) {
|
||||||
|
return request.post('/marriage/common/checkCode', data)
|
||||||
|
},
|
||||||
|
|
||||||
// OCR识别并返回证件信息
|
// OCR识别并返回证件信息
|
||||||
parseOcrInfo(data: {
|
parseOcrInfo(data: {
|
||||||
mobile: string
|
mobile: string
|
||||||
|
|
@ -91,7 +97,7 @@ export const apiService = {
|
||||||
// 领取流程相关接口
|
// 领取流程相关接口
|
||||||
// 领取前校验(生成二维码前的校验与预览)
|
// 领取前校验(生成二维码前的校验与预览)
|
||||||
receiveCheck(data: MarriageCodeDTO) {
|
receiveCheck(data: MarriageCodeDTO) {
|
||||||
return request.post<MarriageCodeVO>('/marriage/receiveCheck', data)
|
return request.post<MarriageCodeVO>('/marriage/receiveCheck2', data)
|
||||||
},
|
},
|
||||||
|
|
||||||
// 确认领取(落库并置为已核销)
|
// 确认领取(落库并置为已核销)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue