666 lines
14 KiB
Vue
666 lines
14 KiB
Vue
<template>
|
||
<view class="idcard-auth-page">
|
||
<!-- 顶部导航栏 -->
|
||
<uni-nav-bar
|
||
left-icon="left"
|
||
:title="currentStep === 1 ? '身份验证' : '添加银行卡'"
|
||
@clickLeft="goBack()"
|
||
fixed
|
||
color="#8B2316"
|
||
height="180rpx"
|
||
:border="false"
|
||
backgroundColor="#eeeeee"
|
||
>
|
||
</uni-nav-bar>
|
||
<!-- 内容区域 -->
|
||
<view class="content-area">
|
||
<!-- 进度指示器 -->
|
||
<view class="progress-bar">
|
||
<view class="barbox">
|
||
<view class="imgbox">
|
||
<up-image :src="currentStep >= 1 ? stepActiveImg : stepImg" width="46rpx" height="46rpx"></up-image>
|
||
<view class="desc" :class="{ active: currentStep >= 1 }">身份信息</view>
|
||
</view>
|
||
<view class="line" :class="{ active: currentStep >= 2 }"></view>
|
||
<view class="imgbox">
|
||
<up-image :src="currentStep >= 2 ? stepActiveImg : stepImg" width="46rpx" height="46rpx"></up-image>
|
||
<view class="desc" :class="{ active: currentStep >= 2 }">添加银行卡</view>
|
||
</view>
|
||
<view class="line" :class="{ active: currentStep >= 3 }"></view>
|
||
<view class="imgbox">
|
||
<up-image :src="currentStep >= 3 ? stepActiveImg : stepImg" width="46rpx" height="46rpx"></up-image>
|
||
<view class="desc" :class="{ active: currentStep >= 3 }">完成</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- <view class="progress-step active">
|
||
<view class="step-icon">
|
||
<uni-icons type="checkmarkempty" color="#ff0000" size="20"></uni-icons>
|
||
</view>
|
||
<text class="step-text">身份信息</text>
|
||
</view>
|
||
<view class="progress-line active"></view>
|
||
<view class="progress-step">
|
||
<view class="step-icon">
|
||
<uni-icons type="checkmarkempty" color="#cccccc" size="20"></uni-icons>
|
||
</view>
|
||
<text class="step-text">添加银行卡</text>
|
||
</view>
|
||
<view class="progress-line"></view>
|
||
<view class="progress-step">
|
||
<view class="step-icon">
|
||
<uni-icons type="checkmarkempty" color="#cccccc" size="20"></uni-icons>
|
||
</view>
|
||
<text class="step-text">完成</text>
|
||
</view> -->
|
||
</view>
|
||
|
||
<!-- 输入表单 -->
|
||
<view class="form-section">
|
||
<!-- 第一步:身份信息 -->
|
||
<view v-if="currentStep === 1">
|
||
<view class="form-item">
|
||
<text class="form-label">姓名</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入您的姓名"
|
||
v-model="formData.name"
|
||
placeholder-style="color: #cccccc"
|
||
/>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="form-label">身份证号</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入您的身份证号"
|
||
v-model="formData.idNumber"
|
||
placeholder-style="color: #cccccc"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 第二步:添加银行卡 -->
|
||
<view v-if="currentStep === 2">
|
||
<view class="form-item">
|
||
<text class="form-label">银行卡号</text>
|
||
<view class="input-container">
|
||
<input
|
||
class="form-input"
|
||
placeholder="仅限借记卡"
|
||
v-model="formData.cardNumber"
|
||
placeholder-style="color: #cccccc"
|
||
/>
|
||
<view class="info-icon">
|
||
<text class="icon-text">!</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="form-label">手机号</text>
|
||
<view class="input-container">
|
||
<input
|
||
class="form-input"
|
||
placeholder="银行预留手机号"
|
||
v-model="formData.mobile"
|
||
placeholder-style="color: #cccccc"
|
||
/>
|
||
<view class="info-icon">
|
||
<text class="icon-text">!</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提示文字 -->
|
||
<view class="warning-text">
|
||
平台不支持绑定多人银行卡,请务必绑定本人卡,实名认证信息,谨慎填写!
|
||
</view>
|
||
|
||
<!-- 下一步按钮 -->
|
||
<view class="bottom-actions">
|
||
<button class="next-btn" :class="{ loading: isLoading }" @click="onNextStep" :disabled="isLoading">
|
||
<text v-if="!isLoading">下一步</text>
|
||
<text v-else>验证中...</text>
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 短信验证码弹框 -->
|
||
<view v-if="showSmsDialog" class="sms-mask">
|
||
<view class="sms-dialog">
|
||
<view class="sms-title">短信验证码</view>
|
||
<view class="sms-subtitle">请输入手机{{ maskedMobile }}收到的验证码</view>
|
||
<view class="sms-input-row">
|
||
<input class="sms-input" v-model="smsCode" placeholder="请输入验证码" placeholder-style="color: #cccccc" />
|
||
<button class="sms-code-btn" :disabled="countdown > 0 || sendingCode" @click="onGetSmsCode">
|
||
<text v-if="countdown === 0">获取验证码</text>
|
||
<text v-else>{{ countdown }}s</text>
|
||
</button>
|
||
</view>
|
||
<view class="sms-actions">
|
||
<button class="sms-cancel" @click="onCancelSms">取消</button>
|
||
<button class="sms-confirm" @click="onConfirmSms">确定</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue';
|
||
import stepImg from "@/static/add_card_no.png"
|
||
import stepActiveImg from "@/static/add_card_yes.png"
|
||
import navTo from '@/utils/navTo';
|
||
import api from '@/api/api';
|
||
|
||
const formData = ref({
|
||
name: '',
|
||
idNumber: '',
|
||
cardNumber: '',
|
||
mobile: ''
|
||
});
|
||
|
||
const isLoading = ref(false);
|
||
const currentStep = ref(1); // 当前步骤,1表示身份信息,2表示添加银行卡
|
||
|
||
// 短信弹框相关
|
||
const showSmsDialog = ref(false);
|
||
const smsCode = ref('');
|
||
const sendingCode = ref(false);
|
||
const countdown = ref(0);
|
||
let countdownTimer = null;
|
||
|
||
const maskedMobile = computed(() => {
|
||
const m = formData.value.mobile || '';
|
||
if (m && m.length >= 7) {
|
||
return `${m.slice(0,3)}****${m.slice(-4)}`;
|
||
}
|
||
return m || '***********';
|
||
});
|
||
const goBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
|
||
// 身份证号格式验证
|
||
const validateIdNumber = (idNumber) => {
|
||
const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
|
||
return reg.test(idNumber);
|
||
};
|
||
|
||
// 姓名格式验证
|
||
const validateName = (name) => {
|
||
const reg = /^[\u4e00-\u9fa5]{2,10}$/;
|
||
return reg.test(name);
|
||
};
|
||
|
||
// 银行卡号验证
|
||
const validateCardNumber = (cardNumber) => {
|
||
const reg = /^\d{16,19}$/;
|
||
return reg.test(cardNumber);
|
||
};
|
||
|
||
// 手机号验证
|
||
const validateMobile = (mobile) => {
|
||
const reg = /^1[3-9]\d{9}$/;
|
||
return reg.test(mobile);
|
||
};
|
||
|
||
const onNextStep = async () => {
|
||
if (currentStep.value === 1) {
|
||
// 第一步:身份信息验证
|
||
if (!formData.value.name.trim()) {
|
||
uni.showToast({ title: '请输入姓名', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (!validateName(formData.value.name)) {
|
||
uni.showToast({ title: '请输入正确的姓名(2-10个汉字)', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (!formData.value.idNumber.trim()) {
|
||
uni.showToast({ title: '请输入身份证号', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (!validateIdNumber(formData.value.idNumber)) {
|
||
uni.showToast({ title: '请输入正确的身份证号', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
// 显示加载状态
|
||
isLoading.value = true;
|
||
|
||
try {
|
||
// 调用身份验证API
|
||
const res = {code:200}
|
||
|
||
if (res.code === 200) {
|
||
uni.showToast({ title: '身份验证成功', icon: 'none' });
|
||
currentStep.value = 2;
|
||
isLoading.value = false;
|
||
} else {
|
||
uni.showToast({ title: res.msg || '身份验证失败', icon: 'none' });
|
||
isLoading.value = false;
|
||
}
|
||
} catch (error) {
|
||
console.error('身份验证失败:', error);
|
||
uni.showToast({ title: '网络错误,请重试', icon: 'none' });
|
||
isLoading.value = false;
|
||
}
|
||
} else if (currentStep.value === 2) {
|
||
// 第二步:银行卡信息验证
|
||
if (!formData.value.cardNumber.trim()) {
|
||
uni.showToast({ title: '请输入银行卡号', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (!validateCardNumber(formData.value.cardNumber)) {
|
||
uni.showToast({ title: '请输入正确的银行卡号', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (!formData.value.mobile.trim()) {
|
||
uni.showToast({ title: '请输入手机号', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
if (!validateMobile(formData.value.mobile)) {
|
||
uni.showToast({ title: '请输入正确的手机号', icon: 'none' });
|
||
return;
|
||
}
|
||
|
||
// 弹出短信验证码弹框
|
||
showSmsDialog.value = true;
|
||
}
|
||
};
|
||
|
||
const onGetSmsCode = async () => {
|
||
if (countdown.value > 0) return;
|
||
const res = await api.smsSend({
|
||
mobile: formData.value.mobile,
|
||
type: 3
|
||
});
|
||
if (res.code === 200) {
|
||
uni.showToast({ title: '短信验证码发送成功', icon: 'none' });
|
||
// 开始60秒倒计时
|
||
countdown.value = 60;
|
||
if (countdownTimer) clearInterval(countdownTimer);
|
||
countdownTimer = setInterval(() => {
|
||
if (countdown.value > 0) {
|
||
countdown.value -= 1;
|
||
} else {
|
||
clearInterval(countdownTimer);
|
||
countdownTimer = null;
|
||
}
|
||
}, 1000);
|
||
} else {
|
||
uni.showToast({ title: res.msg || '短信验证码发送失败', icon: 'none' });
|
||
}
|
||
};
|
||
|
||
|
||
const onConfirmSms = async () => {
|
||
if (!smsCode.value) {
|
||
uni.showToast({ title: '请输入短信验证码', icon: 'none' });
|
||
return;
|
||
}
|
||
const res = await api.identificationBankCardNew({
|
||
phone_number: formData.value.mobile,
|
||
sms: smsCode.value,
|
||
id_number: formData.value.idNumber,
|
||
card_number: formData.value.cardNumber,
|
||
id_name: formData.value.name
|
||
});
|
||
if (res.code === 200) {
|
||
uni.showToast({ title: '银行卡添加成功', icon: 'none' });
|
||
navTo({
|
||
url: '/pages_app/idcardAuth/bankCardList'
|
||
});
|
||
} else {
|
||
uni.showToast({ title: res.msg || '银行卡添加失败', icon: 'none' });
|
||
}
|
||
};
|
||
|
||
const onCancelSms = () => {
|
||
showSmsDialog.value = false;
|
||
};
|
||
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.idcard-auth-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.content-area {
|
||
padding-top: 160rpx;
|
||
padding: 0rpx 0rpx 0;
|
||
}
|
||
|
||
.progress-bar {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #ffffff;
|
||
padding: 80rpx 20rpx 120rpx;
|
||
border-radius: 16rpx;
|
||
margin-bottom: 30rpx;
|
||
.barbox{
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
.imgbox{
|
||
flex:1;
|
||
|
||
position: relative;
|
||
.desc{
|
||
position: absolute;
|
||
left:-50%;
|
||
color:#d0d0d0;
|
||
top:80rpx;
|
||
font-size: 28rpx;
|
||
margin-left: -10rpx;
|
||
white-space: nowrap;
|
||
transform: translateY(-50%);
|
||
&.active{
|
||
color:#8B2316;
|
||
}
|
||
}
|
||
|
||
}
|
||
.imgbox:last-child{
|
||
.desc{
|
||
margin-left: 15rpx;
|
||
}
|
||
}
|
||
.line{
|
||
width:240rpx;
|
||
margin:0 -2rpx;
|
||
height: 16rpx;
|
||
background:#cccccc;
|
||
&.active{
|
||
background:#8B2316;
|
||
}
|
||
}
|
||
}
|
||
.steptext{
|
||
margin:0 30rpx;
|
||
display: flex;
|
||
width:900rpx;
|
||
justify-content: space-between;
|
||
align-items:center ;
|
||
}
|
||
.progress-step {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
flex: 1;
|
||
|
||
.step-icon {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
border-radius: 50%;
|
||
background: #f0f0f0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 16rpx;
|
||
|
||
&.active {
|
||
background: #ff0000;
|
||
}
|
||
}
|
||
|
||
.step-text {
|
||
font-size: 24rpx;
|
||
color: #666666;
|
||
|
||
&.active {
|
||
color: #000000;
|
||
}
|
||
}
|
||
|
||
&.active {
|
||
.step-icon {
|
||
background: #ff0000;
|
||
}
|
||
.step-text {
|
||
color: #000000;
|
||
}
|
||
}
|
||
}
|
||
|
||
.progress-line {
|
||
flex: 1;
|
||
height: 4rpx;
|
||
background: #f0f0f0;
|
||
margin: 0 20rpx;
|
||
|
||
&.active {
|
||
background: #ff0000;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
.form-section {
|
||
background: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 0;
|
||
|
||
|
||
.form-item {
|
||
margin-bottom: 30rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
border-bottom: 2rpx solid #eee;
|
||
padding: 0 30rpx 20rpx;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.form-label {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #000000;
|
||
width: 120rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-input {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 28rpx;
|
||
background: #ffffff;
|
||
border: none;
|
||
outline: none;
|
||
|
||
&:focus {
|
||
border-color: #8B2316;
|
||
}
|
||
}
|
||
|
||
.input-container {
|
||
display: flex;
|
||
align-items: center;
|
||
position: relative;
|
||
width: 80%;
|
||
|
||
.form-input {
|
||
flex: 9;
|
||
height: 80rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 28rpx;
|
||
background: #ffffff;
|
||
border: none;
|
||
border-radius: 0;
|
||
outline: none;
|
||
|
||
&:focus {
|
||
/* 去除边框后无需变更边框颜色 */
|
||
}
|
||
}
|
||
|
||
.info-icon {
|
||
flex:1;
|
||
position: absolute;
|
||
right: 20rpx;
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 50%;
|
||
background: #f0f0f0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.icon-text {
|
||
font-size: 20rpx;
|
||
color: #cccccc;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.warning-text {
|
||
background: #fff3cd;
|
||
border: 2rpx solid #ffeaa7;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
margin-bottom: 60rpx;
|
||
font-size: 26rpx;
|
||
color: #856404;
|
||
line-height: 1.6;
|
||
position: relative;
|
||
|
||
&::before {
|
||
content: "⚠️";
|
||
position: absolute;
|
||
left: 30rpx;
|
||
top: 30rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
padding-left: 80rpx;
|
||
}
|
||
|
||
.bottom-actions {
|
||
padding: 0 30rpx 40rpx;
|
||
|
||
.next-btn {
|
||
width: 100%;
|
||
height: 88rpx;
|
||
background: #8B2316;
|
||
color: #ffffff;
|
||
border: none;
|
||
border-radius: 8rpx;
|
||
font-size: 32rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.3s ease;
|
||
|
||
&:active {
|
||
background: #6B1A0F;
|
||
}
|
||
|
||
&.loading {
|
||
background: #cccccc;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
&:disabled {
|
||
background: #cccccc;
|
||
opacity: 0.7;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 短信弹框样式 */
|
||
.sms-mask {
|
||
position: fixed;
|
||
left: 0;
|
||
top: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: rgba(0,0,0,0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.sms-dialog {
|
||
width: 680rpx;
|
||
background: #ffffff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx;
|
||
}
|
||
|
||
.sms-title {
|
||
text-align: center;
|
||
font-size: 34rpx;
|
||
color: #000000;
|
||
font-weight: 600;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.sms-subtitle {
|
||
text-align: center;
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.sms-input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16rpx;
|
||
margin: 10rpx 0 30rpx;
|
||
}
|
||
|
||
.sms-input {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
border: 2rpx solid #eeeeee;
|
||
border-radius: 8rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.sms-code-btn {
|
||
width: 200rpx;
|
||
height: 88rpx;
|
||
border-radius: 8rpx;
|
||
background: #eeeeee;
|
||
color: #8B2316;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.sms-actions {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
.sms-cancel,
|
||
.sms-confirm {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
border-radius: 8rpx;
|
||
font-size: 32rpx;
|
||
}
|
||
|
||
.sms-cancel { background: #f5f5f5; color: #000000; margin-right: 20rpx; }
|
||
.sms-confirm { background: #8B2316; color: #ffffff; margin-left: 20rpx; }
|
||
</style>
|