很多页面

This commit is contained in:
haomingming
2025-09-03 17:13:10 +08:00
parent 92bfc36d51
commit 84f59b20f6
26 changed files with 29536 additions and 616 deletions
+160
View File
@@ -0,0 +1,160 @@
<template>
<view class="bank-card-page">
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
title="常用银行卡"
@clickLeft="goBack()"
right-text="添加"
@clickRight="addBankCard"
fixed
color="#8B2316"
height="140rpx"
:border="false"
backgroundColor="#eeeeee"
>
</uni-nav-bar>
<!-- 银行卡列表 -->
<view class="card-list">
<view class="card-item" v-for="(card, index) in bankCards" :key="card.uuid">
<view class="card-logo">
<view class="logo-bg">
<text class="logo-text">{{ getBankLogo(card.open_bank) }}</text>
</view>
</view>
<view class="card-info">
<view class="card-number">尾号{{ card.card_number }}储蓄卡</view>
<view class="bank-name">{{ card.open_bank }}</view>
</view>
</view>
</view>
<!-- 底部导航指示器 -->
<view class="bottom-indicator"></view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import navTo from '@/utils/navTo';
import api from '@/api/api';
const bankCards = ref([]);
// 银行卡数据
const getBankCardList = () => {
api.bankCardList({}).then(res => {
if (res.code === 200 && res.data.bankList) {
bankCards.value = res.data.bankList;
}
});
};
const goBack = () => {
uni.navigateBack();
};
const addBankCard = () => {
navTo({
url: '/pages_app/idcardAuth/idcardAuth'
});
};
// 获取银行logo文字
const getBankLogo = (bankName) => {
const bankLogos = {
'工商银行': '工',
'中国银行': '中',
'建设银行': '建',
'农业银行': '农',
'交通银行': '交',
'招商银行': '招',
'民生银行': '民',
'兴业银行': '兴',
'浦发银行': '浦',
'光大银行': '光',
'华夏银行': '华',
'中信银行': '信',
'平安银行': '平',
'广发银行': '广',
'邮储银行': '邮'
};
return bankLogos[bankName] || bankName.charAt(0);
};
onMounted(() => {
getBankCardList();
});
</script>
<style lang="scss" scoped>
.bank-card-page {
min-height: 100vh;
background: #F5F5F5;
position: relative;
}
/* 银行卡列表样式 */
.card-list {
padding: 30rpx;
.card-item {
background: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
.card-logo {
margin-right: 30rpx;
.logo-bg {
width: 80rpx;
height: 80rpx;
background: #E60012;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 2rpx solid #E60012;
.logo-text {
color: #ffffff;
font-size: 32rpx;
font-weight: bold;
}
}
}
.card-info {
flex: 1;
.card-number {
font-size: 28rpx;
color: #000000;
margin-bottom: 8rpx;
font-weight: 500;
}
.bank-name {
font-size: 24rpx;
color: #666666;
}
}
}
}
/* 底部指示器 */
.bottom-indicator {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 120rpx;
height: 6rpx;
background: #333333;
border-radius: 3rpx;
}
</style>
+414 -52
View File
@@ -3,8 +3,8 @@
<!-- 顶部导航栏 -->
<uni-nav-bar
left-icon="left"
title="身份验证"
@cviewckLeft="goBack"
:title="currentStep === 1 ? '身份验证' : '添加银行卡'"
@clickLeft="goBack()"
fixed
color="#8B2316"
height="140rpx"
@@ -18,18 +18,18 @@
<view class="progress-bar">
<view class="barbox">
<view class="imgbox">
<up-image :src="stepImg" width="46rpx" height="46rpx" ></up-image>
<view class="desc ">身份信息</view>
<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"></view>
<view class="line" :class="{ active: currentStep >= 2 }"></view>
<view class="imgbox">
<up-image :src="stepImg" width="46rpx" height="46rpx" ></up-image>
<view class="desc">添加银行卡</view>
<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"></view>
<view class="line" :class="{ active: currentStep >= 3 }"></view>
<view class="imgbox">
<up-image :src="stepImg" width="46rpx" height="46rpx" ></up-image>
<view class="desc">完成</view>
<up-image :src="currentStep >= 3 ? stepActiveImg : stepImg" width="46rpx" height="46rpx"></up-image>
<view class="desc" :class="{ active: currentStep >= 3 }">完成</view>
</view>
</view>
@@ -57,23 +57,58 @@
<!-- 输入表单 -->
<view class="form-section">
<view class="form-item">
<text class="form-label">姓名</text>
<input
class="form-input"
placeholder="请输入您的姓名"
v-model="formData.name"
placeholder-style="color: #cccccc"
/>
<!-- 第一步:身份信息 -->
<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 class="form-item">
<text class="form-label">身份证号</text>
<input
class="form-input"
placeholder="请输入您的身份证号"
v-model="formData.idNumber"
placeholder-style="color: #cccccc"
/>
<!-- 第二步:添加银行卡 -->
<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>
@@ -84,38 +119,215 @@
<!-- 下一步按钮 -->
<view class="bottom-actions">
<button class="next-btn" @click="onNextStep">下一步</button>
<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 } from 'vue';
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: ''
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 onNextStep = () => {
if (!formData.value.name.trim()) {
uni.showToast({ title: '请输入姓名', icon: 'none' });
return;
}
if (!formData.value.idNumber.trim()) {
uni.showToast({ title: '请输入身份证号', icon: 'none' });
return;
}
uni.showToast({ title: '验证通过,跳转下一步', icon: 'success' });
// 这里可以跳转到下一步页面
// 身份证号格式验证
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: 'success' });
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: 'success' });
// 开始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: 'success' });
navTo({
url: '/pages_app/idcardAuth/bankCardList'
});
} else {
uni.showToast({ title: res.msg || '银行卡添加失败', icon: 'none' });
}
};
const onCancelSms = () => {
showSmsDialog.value = false;
};
</script>
<style lang="scss" scoped>
@@ -235,10 +447,11 @@ const onNextStep = () => {
}
}
.form-section {
background: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
padding: 0;
.form-item {
@@ -246,58 +459,207 @@ const onNextStep = () => {
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;
width: 120rpx;
font-weight: 500;
}
.form-input {
flex:1;
flex: 1;
height: 80rpx;
padding: 0 20rpx;
font-size: 28rpx;
background: #ffffff;
border: none;
outline: none;
&:focus {
border-color: #ff0000;
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: #ffffff;
background: #fff3cd;
border: 2rpx solid #ffeaa7;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 60rpx;
font-size: 26rpx;
color: #000000;
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 30rox 40rpx;
padding: 0 30rpx 40rpx;
.next-btn {
margin:0 30rpx;
width: 100%;
height: 88rpx;
background: #cccccc;
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: #8B2316;
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>