From 0d3462a8f5d6d353a3e073122513df6236416922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=B0=8F=E5=95=8A=E7=99=BD?= <2053890199@qq.com> Date: Sat, 15 Nov 2025 09:37:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E7=BB=93=E5=A9=9A?= =?UTF-8?q?=E8=AF=81=E9=A2=86=E5=8F=96=E6=B5=81=E7=A8=8B=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加dayjs依赖用于日期处理 - 新增Dialog组件用于消息提示 - 完善短信验证码发送与校验逻辑 - 实现OCR识别结婚证信息功能 - 完成领取前校验与提交接口对接 - 根据活动状态显示不同界面 --- package.json | 1 + pnpm-lock.yaml | 8 ++ src/components/Dialog.vue | 251 +++++++++++++++++++++++++++++++++++++ src/components/dialog.ts | 162 ++++++++++++++++++++++++ src/pages/home/index.vue | 207 ++++++++++++++++++------------ src/services/apiService.ts | 10 +- 6 files changed, 558 insertions(+), 81 deletions(-) create mode 100644 src/components/Dialog.vue create mode 100644 src/components/dialog.ts diff --git a/package.json b/package.json index 301245a..35d0e42 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@tailwindcss/vite": "^4.1.17", "axios": "^1.13.2", + "dayjs": "^1.11.19", "tailwindcss": "^4.1.17", "vue": "^3.5.24", "vue-router": "^4.6.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eea5cec..4264cfe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: axios: specifier: ^1.13.2 version: 1.13.2 + dayjs: + specifier: ^1.11.19 + version: 1.11.19 tailwindcss: specifier: ^4.1.17 version: 4.1.17 @@ -530,6 +533,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1203,6 +1209,8 @@ snapshots: csstype@3.1.3: {} + dayjs@1.11.19: {} + delayed-stream@1.0.0: {} detect-libc@2.1.2: {} diff --git a/src/components/Dialog.vue b/src/components/Dialog.vue new file mode 100644 index 0000000..e8cb813 --- /dev/null +++ b/src/components/Dialog.vue @@ -0,0 +1,251 @@ + + + + + \ No newline at end of file diff --git a/src/components/dialog.ts b/src/components/dialog.ts new file mode 100644 index 0000000..00227f9 --- /dev/null +++ b/src/components/dialog.ts @@ -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 +} \ No newline at end of file diff --git a/src/pages/home/index.vue b/src/pages/home/index.vue index 348c7b1..44060b0 100644 --- a/src/pages/home/index.vue +++ b/src/pages/home/index.vue @@ -26,69 +26,86 @@
- -
- - +
+ +
+ + + +
+ + +
+ + +
- -
- - -
- - -
-

结婚证信息:

-
-
- -
- 结婚证 +
+ +
+

结婚证信息:

+
+
+ +
+ 结婚证 +
+ +
+
+
+
+ 结婚证字号: + {{ 11111 }} +
+
+ 男方姓名: + {{ 11111 }} +
+
+ 女方姓名: + {{ 11111 }}
-
-
-
- 结婚证字号: - {{ 11111 }} -
-
- 男方姓名: - {{ 11111 }} -
-
- 女方姓名: - {{ 11111 }} -
-
-
- - + + +
-
+
活动已结束
-
感谢您的参与,敬请期待下一次活动
-
活动时间:2024年1月1日 - 2024年6月30日
+
【{{ activityInfo.activityName }}】活动已结束,感谢您的参与,敬请期待下一次活动
+
活动时间:{{ activityInfo.activityStartTime }} - {{ + activityInfo.activityEndTime }}
+
+
+ +
+ +
+
活动获取失败
+
活动获取失败,请稍后重试
+
活动时间:--
@@ -101,20 +118,40 @@ import message from '../../components/message'; import apiService from '../../services/apiService' import { ref, onMounted } from 'vue' +import dayjs from 'dayjs' const ocrUploadId = ref(); // 表单数据 const formData = ref({ phone: '', - verificationCode: '' + smsCode: '', + verifyCode: false, // 验证状态 + marriageNo: '', // 结婚证号 + husbandName: '', // 男方姓名 + wifeName: '', // 女方姓名 + registerDate: '', // 登记日期 }) +const activityInfo = ref({}); // 活动信息 onMounted(() => { // 检查活动是否已结束 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({ mobile: phone, - type: 0, + type: 3, }).then(() => { + formData.value.verifyCode = false; + formData.value.smsCode = ''; + // 开始倒计时 countdown.value = 60 @@ -161,19 +201,20 @@ const sendVerificationCode = () => { // 验证验证码 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位数字验证码') return } // 调用验证短信验证码接口 - apiService.login({ + apiService.verifySms({ mobile: formData.value.phone, - smsCode: verificationCode, + smsCode: smsCode, + type: 3, }).then(() => { - message.success('验证码验证成功') + formData.value.verifyCode = true; }).catch((error) => { console.error('验证验证码失败:', error) message.error('验证码验证失败,请重试') @@ -196,27 +237,27 @@ const handleImageChange = () => { formFormData.append('file', file) apiService.uploadOcrImage(formFormData).then((response: any) => { - apiService.sendSms({ + apiService.parseOcrInfo({ mobile: formData.value.phone, - type: 2, // 2 表示OCR识别 code 获取 - }).then((res) => { - apiService.parseOcrInfo({ - mobile: formData.value.phone, - smsCode: res.data, - uploadId: response.data.uploadId, - }).then((res) => { - console.log(res); - }).catch((error) => { - console.error('解析OCR信息失败:', error) - message.error('解析OCR信息失败,请稍后重试') - }) + 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; + } else { + message.error('解析OCR信息失败,请稍后重试'); + ocrUploadId.value && (ocrUploadId.value.value = ''); + } }).catch((error) => { - console.error('发送验证码失败:', error) - message.error('验证码发送失败,请稍后重试') + message.error('解析OCR信息失败,请稍后重试') + ocrUploadId.value && (ocrUploadId.value.value = ''); }) }).catch((error) => { - console.error('上传OCR图片失败:', error) message.error('图片上传失败,请稍后重试') + ocrUploadId.value && (ocrUploadId.value.value = ''); }) } @@ -224,14 +265,22 @@ const handleImageChange = () => { // 提交表单 const submitForm = () => { - if (!formData.value.phone || !formData.value.verificationCode) { + if (!formData.value.phone || !formData.value.smsCode) { message.error('请完善所有信息') return } - // 模拟提交 - console.log('提交表单数据:', formData.value) - message.success('提交成功,正在处理您的请求...') + apiService.receiveCheck({ + marriageNo: formData.value.marriageNo, + 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('领取失败,请稍后重试') + }) } // 组件卸载时清理定时器 diff --git a/src/services/apiService.ts b/src/services/apiService.ts index f2f8b0c..69f1608 100644 --- a/src/services/apiService.ts +++ b/src/services/apiService.ts @@ -53,7 +53,8 @@ export interface MarriageCodeListVO { export interface CommSmsDTO { mobile: string - type: 0 | 1 | 2; // 0=登录;1=兑换领取;2=OCR识别 + smsCode?: string + type: 0 | 1 | 2 | 3; // 0=登录;1=兑换领取;2=OCR识别;3=本流程使用 } // API服务实现 @@ -79,6 +80,11 @@ export const apiService = { return request.post('/marriage/common/sms', data) }, + // 校验短信验证码 + verifySms(data: CommSmsDTO) { + return request.post('/marriage/common/checkCode', data) + }, + // OCR识别并返回证件信息 parseOcrInfo(data: { mobile: string @@ -91,7 +97,7 @@ export const apiService = { // 领取流程相关接口 // 领取前校验(生成二维码前的校验与预览) receiveCheck(data: MarriageCodeDTO) { - return request.post('/marriage/receiveCheck', data) + return request.post('/marriage/receiveCheck2', data) }, // 确认领取(落库并置为已核销)