feat(首页): 添加扫码获取代金券信息功能并优化表单

- 新增getDetail接口用于获取代金券详情
- 扫码后自动填充表单数据并禁用输入框
- 调整表单字段顺序,将扫码框移至顶部
- 移除提交时的验证码校验逻辑
This commit is contained in:
前端小啊白 2025-11-15 16:43:33 +08:00
parent 56392aedc2
commit b3e53c2848
3 changed files with 656 additions and 33 deletions

View File

@ -34,3 +34,8 @@ export const leftOverCount = (params) => {
export const wechatConfig = (params) => { export const wechatConfig = (params) => {
return ApiService.post("/marriage/wechat/getParam", params); return ApiService.post("/marriage/wechat/getParam", params);
}; };
// 回显数据
export const getDetail = (params) => {
return ApiService.get("/marriage/codeInfo", params);
};

View File

@ -30,28 +30,6 @@
</div> </div>
<div class="content-box"> <div class="content-box">
<div class="content-main"> <div class="content-main">
<div class="content-item">
<div class="content-title">结婚证字号</div>
<div class="input-box">
<input
type="text"
v-model="form.marriageNo"
class="input-item"
placeholder="请输入结婚证字号数字"
/>
</div>
</div>
<div class="content-item">
<div class="content-title">领取人姓名</div>
<div class="input-box">
<input
type="text"
v-model="form.receiveName"
class="input-item"
placeholder="请输入领取人姓名"
/>
</div>
</div>
<div class="content-item"> <div class="content-item">
<div class="content-title1">刮刮乐代金券二维码</div> <div class="content-title1">刮刮乐代金券二维码</div>
<div class="scan-box"> <div class="scan-box">
@ -71,10 +49,35 @@
</div> </div>
</div> </div>
</div> </div>
<div class="content-item">
<div class="content-title">结婚证字号</div>
<div class="input-box">
<input
disabled
type="text"
v-model="form.marriageNo"
class="input-item"
placeholder="请输入结婚证字号数字"
/>
</div>
</div>
<div class="content-item">
<div class="content-title">领取人姓名</div>
<div class="input-box">
<input
disabled
type="text"
v-model="form.receiveName"
class="input-item"
placeholder="请输入领取人姓名"
/>
</div>
</div>
<div class="content-item"> <div class="content-item">
<div class="content-title">领取人手机号</div> <div class="content-title">领取人手机号</div>
<div class="input-box"> <div class="input-box">
<input <input
disabled
type="text" type="text"
class="input-item" class="input-item"
v-model="form.receiveMobile" v-model="form.receiveMobile"
@ -117,6 +120,7 @@ import {
leftOverCount, leftOverCount,
receiveCheck, receiveCheck,
wechatConfig, wechatConfig,
getDetail,
} from "@/api/index.js"; } from "@/api/index.js";
import wx from "weixin-js-sdk"; import wx from "weixin-js-sdk";
const phoneRegex = /^1[3-9]\d{9}$/; const phoneRegex = /^1[3-9]\d{9}$/;
@ -240,9 +244,16 @@ const handleScan = () => {
success: (res) => { success: (res) => {
const result = res.resultStr; // const result = res.resultStr; //
form.code = result; form.code = result;
console.log("扫码结果:", result);
// getDetail({ code: form.code }).then((res) => {
// alert(`: ${result}`); if (res.code == 200) {
form.marriageNo = res.data.marriageNo;
form.receiveName = res.data.receiveName;
form.receiveMobile = res.data.receiveMobile;
} else {
showToast(res.msg);
}
});
}, },
fail: (err) => { fail: (err) => {
if (err.errMsg.includes("permission denied")) { if (err.errMsg.includes("permission denied")) {
@ -286,14 +297,8 @@ function submit() {
...form, ...form,
salesNo: Local.get("userInfo").salesNo, salesNo: Local.get("userInfo").salesNo,
}; };
receiveCheck(params).then((res) => {
if (res.code == 200) {
Local.set("marriageInfo", params); Local.set("marriageInfo", params);
router.push("/Signature"); router.push("/Signature");
} else {
showToast(res.msg);
}
});
} }
onMounted(() => { onMounted(() => {
getNumber(); getNumber();

View File

@ -0,0 +1,613 @@
<template>
<div class="index">
<div class="index_page">
<img class="logo" src="@/assets/img/logo.png" alt="" />
<img class="title" src="@/assets/img/title.png" alt="" />
<img
class="rule"
@click="toRule"
src="@/assets/img/rule-btn.png"
alt=""
/>
<img
class="record"
@click="toRecord"
src="@/assets/img/record-btn.png"
alt=""
/>
<div class="money">
<img class="remain" src="@/assets/img/remain.png" alt="" />
<van-rolling-text
class="my-rolling-text"
:start-num="number"
:height="fontSize * 0.84"
:target-num="num"
direction="up"
ref="rollingTextRef"
/>
<div class="item1"></div>
<div class="refresh" @click="getNumber"></div>
</div>
<div class="content-box">
<div class="content-main">
<div class="content-item">
<div class="content-title">结婚证字号</div>
<div class="input-box">
<input
type="text"
v-model="form.marriageNo"
class="input-item"
placeholder="请输入结婚证字号数字"
/>
</div>
</div>
<div class="content-item">
<div class="content-title">领取人姓名</div>
<div class="input-box">
<input
type="text"
v-model="form.receiveName"
class="input-item"
placeholder="请输入领取人姓名"
/>
</div>
</div>
<div class="content-item">
<div class="content-title1">刮刮乐代金券二维码</div>
<div class="scan-box">
<p
class="line_clamp1"
:style="{ color: form.code ? '#000' : '#c79f9c' }"
>
{{ form.code ? form.code : "请扫码识别代金券二维码" }}
</p>
<div class="scan-btn" @click="handleScan">
<img
src="@/assets/img/scan-icon.png"
class="scan-icon"
alt=""
/>
<p>扫码识别</p>
</div>
</div>
</div>
<div class="content-item">
<div class="content-title">领取人手机号</div>
<div class="input-box">
<input
type="text"
class="input-item"
v-model="form.receiveMobile"
placeholder="请输入领取人手机号"
maxlength="11"
oninput="if(value.length>11)value=value.slice(0,11)"
/>
</div>
</div>
<div class="content-item">
<div class="content-title">验证码</div>
<div class="input-box code-box">
<input
type="text"
class="input-box1"
v-model="form.smsCode"
placeholder="请输入验证码"
maxlength="6"
oninput="if(value.length>6)value=value.slice(0,6)"
/>
<div class="code" @click="getCode">{{ codeTitle }}</div>
</div>
</div>
<div class="login-btn" @click="submit">提交</div>
</div>
</div>
<img class="person-icon" src="@/assets/img/person-icon.png" alt="" />
<img class="ggl-icon1" src="@/assets/img/ggl-icon1.png" alt="" />
<img class="ggl-icon2" src="@/assets/img/ggl-icon2.png" alt="" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, reactive } from "vue";
import { useRouter } from "vue-router";
import { showToast } from "vant";
import { Local } from "@/utils/storage.js";
import {
getSms,
leftOverCount,
receiveCheck,
wechatConfig,
} from "@/api/index.js";
import wx from "weixin-js-sdk";
const phoneRegex = /^1[3-9]\d{9}$/;
const router = useRouter();
const codeTitle = ref("获取验证码");
const codeTime = ref(60);
const disabled = ref(false);
const number = ref("00000");
const num = ref("00000");
const fontSize = ref(0);
const rollingTextRef = ref(null);
const currentUrl = ref(encodeURIComponent(window.location.href.split("#")[0]));
const form = reactive({
receiveMobile: "",
smsCode: "",
code: "",
marriageNo: "",
receiveName: "",
});
// HTMLfontSize
let fontSizeObserver = null;
const login = () => {
router.push({ name: "Login" });
};
const toRule = () => {
router.push({ name: "Rule" });
};
const toRecord = () => {
router.push({ name: "Record" });
};
function timeWait(time) {
disabled.value = true;
setTimeout(function () {
if (time >= 0) {
codeTitle.value = "重新获取(" + time + "s)";
time--;
timeWait(time);
} else {
codeTitle.value = "获取验证码";
codeTime.value = 60;
disabled.value = false;
}
}, 1000);
}
function getCode() {
if (disabled.value) return;
//
if (form.receiveMobile == "") {
showToast("请输入手机号");
return;
} else if (!phoneRegex.test(form.receiveMobile)) {
showToast("请输入正确的手机号格式");
return;
}
disabled.value = true;
const params = {
mobile: form.receiveMobile,
type: 1,
};
getSms(params).then((res) => {
if (res.code == 200) {
showToast("验证码发送成功");
timeWait(codeTime.value);
} else {
showToast(res.msg);
disabled.value = false;
}
});
}
function getNumber() {
leftOverCount({}).then((res) => {
if (res.code == 200) {
// 50
number.value = num.value;
num.value = String(res.data).padStart(5, "0");
} else {
showToast(res.msg);
}
});
}
//
const initWechatConfig = async () => {
try {
const response = await wechatConfig();
// const config = await response.json();
if (response.code == 200) {
wx.config({
debug: false, // false
appId: response.data.appId,
timestamp: response.data.timestamp,
nonceStr: response.data.nonceStr,
signature: response.data.signature,
jsApiList: ["scanQRCode"], // 使JS
});
wx.ready(() => {
console.log("微信JS-SDK配置完成");
});
wx.error((err) => {
console.error("微信JS-SDK配置失败:", err);
showToast("微信初始化失败,请刷新页面重试");
});
}
} catch (error) {
console.error("获取微信配置失败:", error);
}
};
let scanBtn = ref(false);
//
const handleScan = () => {
if(scanBtn.value) return;
scanBtn.value = true;
setTimeout(() => {
scanBtn.value = false;
}, 1000);
wx.scanQRCode({
needResult: 1, // 1
scanType: ["qrCode", "barCode"], //
success: (res) => {
const result = res.resultStr; //
form.code = result;
console.log("扫码结果:", result);
//
// alert(`: ${result}`);
},
fail: (err) => {
if (err.errMsg.includes("permission denied")) {
showToast("扫码权限被拒绝,请允许访问相机");
} else {
showToast("扫码失败,请重试");
}
},
});
};
function submit() {
// marriageNo
if (form.marriageNo == "") {
showToast("请输入结婚证字号");
return;
}
//
if (form.receiveName == "") {
showToast("请输入领取人姓名");
return;
}
//
if (form.code == "") {
showToast("请扫描代金券二维码");
return;
}
//
if (form.receiveMobile == "") {
showToast("请输入领取人手机号");
return;
} else if (!phoneRegex.test(form.receiveMobile)) {
showToast("请输入正确的手机号格式");
return;
}
//
if (form.smsCode == "") {
showToast("请输入验证码");
return;
}
const params = {
...form,
salesNo: Local.get("userInfo").salesNo,
};
receiveCheck(params).then((res) => {
if (res.code == 200) {
Local.set("marriageInfo", params);
router.push("/Signature");
} else {
showToast(res.msg);
}
});
}
onMounted(() => {
getNumber();
initWechatConfig();
// MutationObserverstyle
fontSizeObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === "style") {
const htmlElement = document.documentElement;
// fontSize
const fontSizeWithUnit = window
.getComputedStyle(htmlElement)
.getPropertyValue("font-size");
// 使parseFloat
const currentFontSize = parseFloat(fontSizeWithUnit);
fontSize.value = currentFontSize;
// fontSize
}
});
});
const htmlElement = document.documentElement;
// fontSize
const fontSizeWithUnit = window
.getComputedStyle(htmlElement)
.getPropertyValue("font-size");
// 使parseFloat
const currentFontSize = parseFloat(fontSizeWithUnit);
fontSize.value = currentFontSize;
// style
fontSizeObserver.observe(document.documentElement, {
attributes: true,
attributeFilter: ["style"],
});
rollingTextRef.value.start();
});
onUnmounted(() => {
//
if (fontSizeObserver) {
fontSizeObserver.disconnect();
fontSizeObserver = null;
}
});
</script>
<style scoped lang="scss">
@keyframes titleAnimate {
0% {
transform: scale(0.9);
}
100% {
transform: scale(0.99);
}
}
input {
border: none;
margin: 0 auto;
font-size: 0.32rem;
&::placeholder {
color: #c79f9c;
}
}
.index {
background-color: #f46f71;
width: 100vw;
height: 100vh;
}
.index_page {
width: 100%;
min-height: 16.32rem;
background: url("@/assets/img/bg.png") top center / 100% 16.32rem no-repeat;
overflow: hidden;
position: relative;
.logo {
width: 3.32rem;
height: 0.66rem;
margin-top: 0.26rem;
margin-left: 0.3rem;
}
.rule {
width: 0.55rem;
height: 1.66rem;
position: absolute;
top: 1.26rem;
left: 0;
cursor: pointer;
}
.record {
width: 0.55rem;
height: 1.66rem;
position: absolute;
top: 1.26rem;
right: 0;
cursor: pointer;
}
.title {
width: 6.45rem;
height: 1.94rem;
margin: 0 auto;
display: block;
animation: titleAnimate linear 1.5s alternate infinite;
}
}
.money {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
margin: 0.32rem auto 0;
.remain {
width: 1.02rem;
height: 0.53rem;
margin-right: 0.1rem;
}
.my-rolling-text {
// --van-rolling-text-background: #1989fa;
--van-rolling-text-color: white;
--van-rolling-text-font-size: 24px;
--van-rolling-text-gap: 6px;
--van-rolling-text-item-border-radius: 5px;
--van-rolling-text-item-width: 40px;
}
.item1 {
width: 0.6rem;
height: 0.84rem;
line-height: 0.84rem;
font-weight: bold;
text-align: center;
overflow: hidden;
font-size: 0.36rem;
color: #ffffff;
background: linear-gradient(0deg, #ff2020, #ff7a4e);
box-shadow: 0px 0.06rem 0.1rem 0px rgba(252, 59, 13, 0.45);
border-radius: 0.1rem;
border: 0.02rem solid #ffffff;
}
.refresh {
width: 0.54rem;
height: 0.52rem;
background: url("@/assets/img/refresh.png") top center / 100% 100% no-repeat;
margin-left: 0.1rem;
}
}
.content-box {
width: 6.9rem;
background: #f6b8b8;
border-radius: 0.3rem;
margin: 0.46rem auto 0;
overflow: hidden;
padding: 0.2rem;
box-sizing: border-box;
.content-main {
width: 100%;
height: 100%;
background: #fef5f5;
border-radius: 0.2rem;
margin: 0 auto 0;
overflow: hidden;
padding-bottom: 0.5rem;
.login-btn {
width: 3.2rem;
height: 0.8rem;
background: #ea3a3a;
border-radius: 0.4rem;
font-weight: bold;
font-size: 0.32rem;
color: #ffffff;
text-align: center;
line-height: 0.8rem;
margin: 0.6rem auto 0;
}
.content-item {
width: 100%;
text-align: center;
margin-bottom: 0.18rem;
&:nth-child(1) {
margin-top: 0.28rem;
}
.content-title {
width: 3.2rem;
height: 0.53rem;
background: url("@/assets/img/title-bg.png") top center / 100% 100%
no-repeat;
font-weight: bold;
font-size: 0.3rem;
color: #97221a;
line-height: 0.53rem;
text-align: center;
margin: 0 auto 0;
}
.content-title1 {
width: 3.8rem;
height: 0.53rem;
background: url("@/assets/img/title-bg1.png") top center / 100% 100%
no-repeat;
font-weight: bold;
font-size: 0.3rem;
color: #97221a;
line-height: 0.53rem;
text-align: center;
margin: 0.28rem auto 0;
}
.input-box {
width: 5.76rem;
height: 0.9rem;
background: #ffffff;
border-radius: 0.2rem;
font-size: 0.32rem;
color: #333333;
padding-left: 0.3rem;
box-sizing: border-box;
margin: 0 auto;
&.code-box {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 auto;
.code {
font-size: 0.32rem;
color: #ea3a3a;
margin-right: 0.3rem;
}
.input-box1 {
width: 2.8rem;
margin-left: 0;
}
}
.input-item {
width: 100%;
height: 100%;
background: #ffffff;
border-radius: 0.2rem;
}
}
.scan-box {
width: 5.76rem;
height: 0.9rem;
background: #f2dede;
border-radius: 0.2rem;
font-size: 0.28rem;
color: #c79f9c;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 0.2rem 0 0.3rem;
box-sizing: border-box;
margin: 0 auto;
.scan-btn {
display: flex;
align-items: center;
width: 1.99rem;
height: 0.62rem;
background: #ffffff;
border-radius: 0.1rem;
justify-content: space-evenly;
margin-left: 0.1rem;
.scan-icon {
width: 0.3rem;
height: 0.3rem;
}
p {
font-size: 0.32rem;
color: #ea3a3a;
}
}
> p {
font-size: 0.32rem;
flex: 1;
text-align: left;
}
}
}
}
}
.person-icon {
width: 2.3rem;
height: 2.9rem;
position: absolute;
left: 0.22rem;
bottom: 0;
}
.ggl-icon1 {
width: 0.96rem;
height: 1.22rem;
position: absolute;
top: 4.19rem;
left: 0.08rem;
}
.ggl-icon2 {
width: 1.2rem;
height: 1.17rem;
position: absolute;
top: 11.9rem;
right: 0;
}
::v-deep(.van-rolling-text-item) {
width: auto;
margin-right: 0;
width: 0.6rem;
height: 0.84rem !important;
background: linear-gradient(0deg, #ff2020, #ff7a4e);
box-shadow: 0px 0.06rem 0.1rem 0px rgba(252, 59, 13, 0.45);
border-radius: 0.1rem;
border: 0.02rem solid #ffffff;
}
::v-deep(.van-rolling-text-item__box) {
line-height: 0.84rem;
font-weight: bold;
text-align: center;
overflow: hidden;
font-size: 0.71rem;
color: #ffffff;
}
::v-deep(.van-rolling-text-item__item) {
height: 0.84rem !important;
line-height: 0.84rem !important;
overflow: hidden;
}
</style>