实名认证+修改密码

This commit is contained in:
小林
2025-08-11 17:39:54 +08:00
parent b252caec77
commit 95216e2464
9 changed files with 1140 additions and 332 deletions

View File

@@ -2,7 +2,11 @@
"easycom": {
"^qiun-(.*)": "@qiun/ucharts/components/$1/$1.vue"
"^qiun-(.*)": "@qiun/ucharts/components/$1/$1.vue",
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [
{
@@ -39,7 +43,12 @@
"navigationBarTitleText": ""
}
},{
"path": "pages/more/bankCardDetail",
"path": "pages/more/changepwd",
"style": {
"navigationBarTitleText": ""
}
},{
"path": "pages/more/realauth",
"style": {
"navigationBarTitleText": ""
}

View File

@@ -18,7 +18,7 @@
<input
v-model="formData.cardNumber"
type="number"
placeholder="请输入银行卡号"
:placeholder="$t('bank.inputBankcardnum')"
maxlength="19"
@input="formatCardNumber"
class="card-input"
@@ -42,7 +42,7 @@
range-key="name"
>
<view class="picker">
{{ bankList[bankIndex]?.name || '请选择银行' }}
{{ bankList[bankIndex]?.name || $t('bank.selectBankcardname') }}
<uni-icons type="arrowdown" size="16" color="#999"></uni-icons>
</view>
</picker>
@@ -54,7 +54,7 @@
<input
v-model="formData.cardHolder"
type="text"
placeholder="请输入持卡人姓名"
:placeholder="$t('bank.cardpersonnamePlaceholder')"
maxlength="20"
class="common-input"
/>
@@ -62,11 +62,11 @@
<!-- 身份证号 -->
<view class="form-item">
<text class="form-label">{{$t(bank.idCard)}}</text>
<text class="form-label">{{$t('bank.idCard')}}</text>
<input
v-model="formData.idCard"
type="idcard"
placeholder="请输入身份证号"
:placeholder="$t('bank.idCardPlaceholder')"
maxlength="18"
class="common-input"
/>
@@ -78,7 +78,7 @@
<input
v-model="formData.phone"
type="number"
placeholder="请输入银行预留手机号"
:placeholder="$t('bank.phonePlaceholder')"
maxlength="11"
class="common-input"
/>
@@ -91,7 +91,7 @@
<input
v-model="formData.smsCode"
type="number"
placeholder="请输入验证码"
:placeholder="$t('bank.vericodePlaceholder')"
maxlength="6"
class="sms-input"
/>
@@ -163,7 +163,7 @@ export default {
},
computed: {
smsBtnText() {
return this.isCounting ? `${this.countdown}s后重新获取` : '获取验证码'
return this.isCounting ? `${this.countdown}s` : this.$t('bank.getverifycode')
},
canSendSms() {
return /^1[3-9]\d{9}$/.test(this.formData.phone)
@@ -240,19 +240,19 @@ export default {
handleSendSms() {
if (!this.canSendSms) {
uni.showToast({
title: '请输入正确的手机号',
title: "$t('bank.inputcorrectphone')",
icon: 'none'
})
return
}
uni.showLoading({ title: '发送中...' })
uni.showLoading({ title: "$t('bank.sendingcode')" })
// 模拟发送验证码
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '验证码已发送',
title: "$t('bank.codesent')",
icon: 'success'
})
this.startCountdown()
@@ -284,13 +284,13 @@ export default {
handleSubmit() {
if (!this.validateForm()) return
uni.showLoading({ title: '绑定中...' })
uni.showLoading({ title: "$t(bank.binding)" })
// 模拟API请求
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '绑定成功',
title: "$t('bank.bindSuccess')",
icon: 'success'
})
@@ -303,37 +303,37 @@ export default {
// 表单验证
validateForm() {
if (!this.formData.cardNumber || this.formData.cardNumber.replace(/\s/g, '').length < 16) {
uni.showToast({ title: '请输入正确的银行卡号', icon: 'none' })
uni.showToast({ title: "$t('bank.inputcorrectcardnum')", icon: 'none' })
return false
}
if (!this.formData.bankCode) {
uni.showToast({ title: '请选择银行', icon: 'none' })
uni.showToast({ title: "$t('bank.selectbank')", icon: 'none' })
return false
}
if (!this.formData.cardHolder) {
uni.showToast({ title: '请输入持卡人姓名', icon: 'none' })
uni.showToast({ title: "$t('bank.inputcorrectcardpersonname')", icon: 'none' })
return false
}
if (!/^\d{17}[\dXx]$/.test(this.formData.idCard)) {
uni.showToast({ title: '请输入正确的身份证号', icon: 'none' })
uni.showToast({ title: "$t('bank.inputcorrectidcardnum')", icon: 'none' })
return false
}
if (!/^1[3-9]\d{9}$/.test(this.formData.phone)) {
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
uni.showToast({ title: "$t('bank.inputcorrectphone')", icon: 'none' })
return false
}
if (!/^\d{6}$/.test(this.formData.smsCode)) {
uni.showToast({ title: '请输入正确的验证码', icon: 'none' })
uni.showToast({ title: "$t('bank.inputcorrectcode')", icon: 'none' })
return false
}
if (!this.formData.agreed) {
uni.showToast({ title: '请先阅读并同意协议', icon: 'none' })
uni.showToast({ title: "$t('bank.inputcorrectcode')", icon: 'none' })
return false
}

View File

@@ -3,7 +3,7 @@
<!-- 头部导航 -->
<view class="nav-bar">
<view class="back-btn" @click="navigateBack">
<image src="/src/static/pic/mark/more-left.png" mode="aspectFit"></image>
<image src="/src/static/pic/mark/more-left.png" mode="aspectFit"></image>
</view>
<text class="title">{{ $t('bank.myBank') }}</text>
<view class="right-placeholder"></view>
@@ -46,6 +46,52 @@
<view class="tips">
<text>{{$t('bank.longpressTip')}}</text>
</view>
<!-- 银行卡详情弹窗 -->
<uni-popup ref="cardPopup" type="center" :mask-click="false">
<view class="popup-content" v-if="currentCard">
<view class="popup-header">
<text class="popup-title">{{ $t('bank.bankdetail') }}</text>
<view class="close-btn" @click="closePopup">
<image src="/static/close.png" mode="aspectFit"></image>
</view>
</view>
<view class="card-info">
<view class="info-item">
<text class="info-label">{{$t('bank.bankname')}}</text>
<text class="info-value">{{currentCard.bankName}}</text>
</view>
<view class="info-item">
<text class="info-label">{{$t('bank.banknum')}}</text>
<text class="info-value">{{formatCardNumber(currentCard.cardNumber)}}</text>
</view>
<view class="info-item">
<text class="info-label">{{$t('bank.cardtype')}}</text>
<text class="info-value">{{currentCard.cardType}}</text>
</view>
<view class="info-item">
<text class="info-label">{{$t('bank.cardholder')}}</text>
<text class="info-value">{{currentCard.cardHolder}}</text>
</view>
<view class="info-item">
<text class="info-label">{{$t('bank.bindphone')}}</text>
<text class="info-value">{{currentCard.phone}}</text>
</view>
<view class="info-item">
<text class="info-label">{{ $t('bank.bindtime') }}</text>
<text class="info-value">{{currentCard.bindTime}}</text>
</view>
</view>
<button class="confirm-btn" @click="closePopup">{{$t('bank.certain')}}</button>
</view>
</uni-popup>
</view>
</template>
@@ -61,7 +107,7 @@ export default {
cardNumber: '6222021234567890123',
cardType: '储蓄卡',
cardHolder: '张三',
phone: '138****1234',
phone: '13812341234',
bindTime: '2023-01-01'
},
{
@@ -74,7 +120,8 @@ export default {
phone: '138****1234',
bindTime: '2023-02-15'
}
]
],
currentCard: null
}
},
methods: {
@@ -107,17 +154,28 @@ export default {
},
// 查看银行卡详情
viewCardDetail(card) {
uni.navigateTo({
url: `/pages/more/detail?id=${card.id}`
})
viewCardDetail(cards) {
this.currentCard = cards
this.$refs.cardPopup.open()
},
// 关闭弹窗
closePopup() {
this.$refs.cardPopup.close()
},
// 格式化卡号显示
formatCardNumber(number) {
if (!number) return ''
// 每4位加一个空格
return number.replace(/(\d{4})(?=\d)/g, '$1 ')
},
// 长按解绑
onLongPress(card) {
uni.showModal({
title: '解绑银行卡',
content: `确定要解绑${card.bankName}尾号${card.cardNumber.slice(-4)}的银行卡吗?`,
title: this.$t('bank.unbindtips'),
content: this.$t('bank.certainunbind')+`${card.bankName}`+this.$t('bank.endfour')+`${card.cardNumber.slice(-4)}`+this.$t('bank.card'),
success: (res) => {
if (res.confirm) {
this.unbindCard(card.id)
@@ -128,14 +186,14 @@ export default {
// 解绑银行卡
unbindCard(cardId) {
uni.showLoading({ title: '处理中...' })
uni.showLoading({ title: this.$t('bank.unbinding') })
// 模拟API请求
setTimeout(() => {
this.cards = this.cards.filter(card => card.id !== cardId)
uni.hideLoading()
uni.showToast({
title: '解绑成功',
title: this.$t('bank.unbindSuccess'),
icon: 'success'
})
}, 1000)
@@ -169,6 +227,7 @@ export default {
width: 40rpx;
height: 40rpx;
filter: invert(1);
margin-right: 40rpx;
}
}
@@ -178,6 +237,7 @@ export default {
color: #333;
flex: 1;
text-align: center;
margin-right: 20rpx;
}
.right-placeholder {
@@ -281,4 +341,68 @@ export default {
color: #999;
margin-top: 30rpx;
}
/* 弹窗样式 */
.popup-content {
width: 600rpx;
background-color: #fff;
border-radius: 20rpx;
padding: 40rpx;
box-sizing: border-box;
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
.popup-title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
.close-btn {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
image {
width: 30rpx;
height: 30rpx;
}
}
}
.card-info {
.info-item {
display: flex;
justify-content: space-between;
margin-bottom: 30rpx;
.info-label {
font-size: 28rpx;
color: #999;
}
.info-value {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
}
}
.confirm-btn {
margin-top: 50rpx;
background-color: #2979FF;
color: #fff;
border-radius: 50rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 32rpx;
}
}
</style>

View File

@@ -1,292 +0,0 @@
<template>
<view class="container">
<!-- 头部导航 -->
<view class="nav-bar">
<view class="back-btn" @click="navigateBack">
<image src="/src/static/pic/mark/more-left.png" mode="aspectFit"></image>
</view>
<text class="title">{{$t('bank.cardDetail')}}</text>
<view class="right-placeholder"></view>
</view>
<!-- 银行卡展示 -->
<view
class="card-display"
:style="{background: getCardBg(card.bankCode)}"
>
<view class="card-top">
<image class="bank-logo" :src="card.bankLogo" mode="aspectFit"></image>
<text class="bank-name">{{card.bankName}}</text>
</view>
<view class="card-middle">
<text class="card-type">{{card.cardType}}</text>
</view>
<view class="card-bottom">
<text class="card-number">{{formatCardNumber(card.cardNumber)}}</text>
</view>
</view>
<!-- 银行卡信息 -->
<view class="card-info">
<view class="info-item">
<text class="info-label">{{$t('bank.cardperson')}}</text>
<text class="info-value">{{card.cardHolder}}</text>
</view>
<view class="info-item">
<text class="info-label">{{$t('bank.cardphone')}}</text>
<text class="info-value">{{card.phone}}</text>
</view>
<view class="info-item">
<text class="info-label">{{ $t('bank.bindtime') }}</text>
<text class="info-value">{{card.bindTime}}</text>
</view>
</view>
<!-- 解绑按钮 -->
<view class="action-btn" @click="showUnbindDialog">
<text>{{$t(bank.unbindcard)}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
card: {
id: '',
bankName: '加载中...',
bankCode: '',
bankLogo: '',
cardNumber: '',
cardType: '',
cardHolder: '',
phone: '',
bindTime: '2023-01-01 12:00:00'
}
}
},
onLoad(options) {
if (options.id) {
// 模拟数据 - 实际项目中应该从服务器获取
this.card = {
id: options.id,
bankName: options.id === '1' ? 'this.$t("bank.ICBC")' : 'this.$t("bank.CMB")',
bankCode: options.id === '1' ? 'ICBC' : 'CMB',
bankLogo: options.id === '1' ? '/static/bank/icbc.png' : '/static/bank/cmb.png',
cardNumber: options.id === '1' ? '6222021234567890123' : '6225881234567890',
cardType: options.id === '1' ? '储蓄卡' : '信用卡',
cardHolder: '张三',
phone: '138****1234',
bindTime: options.id === '1' ? '2023-01-01 12:00:00' : '2023-02-15 14:30:00'
}
}
},
methods: {
// 返回上一页
navigateBack() {
uni.navigateBack({
delta: 1
})
},
// 获取银行卡背景色
getCardBg(bankCode) {
const colorMap = {
'ICBC': 'linear-gradient(135deg, #c33a3a, #e05a5a)',
'CCB': 'linear-gradient(135deg, #1a6fc9, #3a8de0)',
'BOC': 'linear-gradient(135deg, #c32136, #e04154)',
'ABC': 'linear-gradient(135deg, #019858, #03a65a)',
'CMB': 'linear-gradient(135deg, #c60a1e, #e62a3d)',
'BOCOM': 'linear-gradient(135deg, #003366, #004080)',
'default': 'linear-gradient(135deg, #666666, #888888)'
}
return colorMap[bankCode] || colorMap['default']
},
// 格式化银行卡号
formatCardNumber(number) {
if (!number) return ''
return number.replace(/(\d{4})(?=\d)/g, '$1 ')
},
// 显示解绑对话框
showUnbindDialog() {
uni.showModal({
title: '解绑银行卡',
content: `确定要解绑${this.card.bankName}尾号${this.card.cardNumber.slice(-4)}的银行卡吗?`,
success: (res) => {
if (res.confirm) {
this.unbindCard()
}
}
})
},
// 解绑银行卡
unbindCard() {
uni.showLoading({ title: '解绑中...' })
// 模拟API请求
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '解绑成功',
icon: 'success'
})
// 返回上一页并刷新
const pages = getCurrentPages()
if (pages.length > 1) {
const prevPage = pages[pages.length - 2]
prevPage.$vm.getBankCards && prevPage.$vm.getBankCards()
}
setTimeout(() => {
uni.navigateBack()
}, 1500)
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 20rpx 30rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
/* 导航栏样式 */
.nav-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 40rpx;
padding-top: var(--status-bar-height);
.back-btn {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
image {
width: 40rpx;
height: 40rpx;
filter: invert(1);
}
}
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
flex: 1;
text-align: center;
}
.right-placeholder {
width: 60rpx;
}
}
.card-display {
height: 300rpx;
border-radius: 20rpx;
padding: 40rpx;
margin-bottom: 40rpx;
color: #fff;
position: relative;
overflow: hidden;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.1);
.bank-logo {
width: 80rpx;
height: 80rpx;
margin-right: 20rpx;
}
.card-top {
display: flex;
align-items: center;
margin-bottom: 40rpx;
.bank-name {
font-size: 40rpx;
font-weight: bold;
}
}
.card-middle {
margin-bottom: 50rpx;
.card-type {
font-size: 32rpx;
background: rgba(255, 255, 255, 0.2);
padding: 8rpx 20rpx;
border-radius: 24rpx;
}
}
.card-bottom {
.card-number {
font-size: 44rpx;
letter-spacing: 4rpx;
}
}
&::after {
content: '';
position: absolute;
top: -60rpx;
right: -60rpx;
width: 240rpx;
height: 240rpx;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
}
}
.card-info {
background-color: #fff;
border-radius: 16rpx;
padding: 0 30rpx;
margin-bottom: 40rpx;
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
height: 100rpx;
border-bottom: 1rpx solid #f0f0f0;
&:last-child {
border-bottom: none;
}
.info-label {
font-size: 30rpx;
color: #666;
}
.info-value {
font-size: 30rpx;
color: #333;
}
}
}
.action-btn {
height: 90rpx;
line-height: 90rpx;
text-align: center;
background-color: #fff;
border-radius: 45rpx;
color: #ff3b30;
font-size: 32rpx;
margin-top: 40rpx;
}
</style>

View File

@@ -0,0 +1,379 @@
<template>
<view class="container">
<view class="nav-bar">
<view class="back-btn" @click="navigateBack">
<image src="/src/static/pic/mark/more-left.png" mode="aspectFit"></image>
</view>
<view class="title">{{ $t('changepwd.changepwdtitle') }}</view>
<view class="right-placeholder"></view>
</view>
<!-- 选项卡切换 -->
<view class="tabs">
<view
class="tab-item"
:class="{active: activeTab === 'login'}"
@click="switchTab('login')"
>
{{ $t('changepwd.changeloginPassword') }}
</view>
<view
class="tab-item"
:class="{active: activeTab === 'transaction'}"
@click="switchTab('transaction')"
>
{{ $t('changepwd.changetransactionPassword') }}
</view>
</view>
<!-- 登录密码修改 -->
<view class="form-container" v-if="activeTab === 'login'">
<view class="form-item">
<text class="label">{{$t('changepwd.oldLoginPwd')}}</text>
<input
class="input"
type="password"
v-model="loginForm.oldPassword"
:placeholder="$t('changepwd.oldLoginPwdPlaceholder')"
placeholder-class="placeholder"
/>
</view>
<view class="form-item">
<text class="label">{{$t('changepwd.newLoginPwd')}}</text>
<input
class="input"
type="password"
v-model="loginForm.newPassword"
:placeholder="$t('changepwd.newLoginPwdPlaceholder')"
placeholder-class="placeholder"
/>
</view>
<view class="form-item">
<text class="label">{{$t('changepwd.confirmLoginPwd')}}</text>
<input
class="input"
type="password"
v-model="loginForm.confirmPassword"
:placeholder="$t('changepwd.confirmLoginPwdPlaceholder')"
placeholder-class="placeholder"
/>
</view>
<button class="submit-btn" @click="handleLoginSubmit">{{ $t('changepwd.confirmupdate') }}</button>
</view>
<!-- 交易密码修改 -->
<view class="form-container" v-if="activeTab === 'transaction'">
<view class="form-item">
<text class="label">{{$t('changepwd.oldTransactionPwd')}}</text>
<input
class="input"
type="password"
v-model="transactionForm.oldPassword"
:placeholder="$t('changepwd.oldTransactionPwdPlaceholder')"
placeholder-class="placeholder"
/>
</view>
<view class="form-item">
<text class="label">{{$t('changepwd.newTransactionPwd')}}</text>
<input
class="input"
type="password"
v-model="transactionForm.newPassword"
:placeholder="$t('changepwd.newTransactionPwdPlaceholder')"
placeholder-class="placeholder"
/>
</view>
<view class="form-item">
<text class="label">{{$t('changepwd.confirmTransactionPwd')}}</text>
<input
class="input"
type="password"
v-model="transactionForm.confirmPassword"
:placeholder="$t('changepwd.confirmTransactionPwdPlaceholder')"
placeholder-class="placeholder"
/>
</view>
<button class="submit-btn" @click="handleTransactionSubmit">{{ $t('changepwd.confirmupdate') }}</button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
activeTab: 'login', // 当前激活的选项卡
loginForm: {
oldPassword: '',
newPassword: '',
confirmPassword: ''
},
transactionForm: {
oldPassword: '',
newPassword: '',
confirmPassword: ''
}
}
},
methods: {
navigateBack() {
uni.navigateBack()
},
// 切换选项卡
switchTab(tab) {
this.activeTab = tab
},
// 验证密码格式
validatePassword(password, type) {
if (!password) {
uni.showToast({
title: type === 'login' ? this.$t('changepwd.inputloginpwd') : this.$t('changepwd.inputtransactionpwd'),
icon: 'none'
})
return false
}
if (password.length < 6) {
uni.showToast({
title: this.$t('changepwd.pwdlength'),
icon: 'none'
})
return false
}
// 可以添加更复杂的密码规则验证
return true
},
// 处理登录密码提交
handleLoginSubmit() {
if (!this.validatePassword(this.loginForm.oldPassword, 'login')) return
if (!this.validatePassword(this.loginForm.newPassword, 'login')) return
if (this.loginForm.newPassword !== this.loginForm.confirmPassword) {
uni.showToast({
title: this.$t('changepwd.loginpasswordNotMatch'),
icon: 'none'
})
return
}
this.changePassword('login')
},
// 处理交易密码提交
handleTransactionSubmit() {
if (!this.validatePassword(this.transactionForm.oldPassword, 'transaction')) return
if (!this.validatePassword(this.transactionForm.newPassword, 'transaction')) return
if (this.transactionForm.newPassword !== this.transactionForm.confirmPassword) {
uni.showToast({
title: this.$t('changepwd.transactionpwdNotMatch'),
icon: 'none'
})
return
}
this.changePassword('transaction')
},
// 修改密码API调用
async changePassword(type) {
uni.showLoading({
title: this.$t('changepwd.changingPassword')
})
try {
const formData = type === 'login' ? this.loginForm : this.transactionForm
const url = type === 'login' ? '/api/user/changeLoginPassword' : '/api/user/changeTransactionPassword'
const res = await uni.request({
url,
method: 'POST',
data: {
oldPassword: formData.oldPassword,
newPassword: formData.newPassword
}
})
uni.hideLoading()
if (res[1].data.code === 200) {
uni.showToast({
title: type === 'login' ? this.$t('changepwd.changeloginpwdSuccess') : this.$t('changepwd.changetransactionpwdSuccess'),
icon: 'success'
})
// 清空表单
if (type === 'login') {
this.loginForm = {
oldPassword: '',
newPassword: '',
confirmPassword: ''
}
} else {
this.transactionForm = {
oldPassword: '',
newPassword: '',
confirmPassword: ''
}
}
// 修改成功后返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
} else {
uni.showToast({
title: res[1].data.message || this.$t('changepwd.changeFail'),
icon: 'none'
})
}
} catch (error) {
uni.hideLoading()
uni.showToast({
title: this.$t('changepwd.requestError'),
icon: 'none'
})
console.error(this.$t('changepwd.requestError'), error)
}
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 0;
min-height: 100vh;
background-color: #f5f5f5;
}
.nav-bar {
display: flex;
align-items: center;
margin-top: 10rpx;
height: 100rpx;
padding: 0 30rpx;
padding-top: var(--status-bar-height);
position: relative;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
.back-btn {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: flex-start;
position: relative;
image {
width: 100%;
height: 100%;
filter: invert(1);
}
}
.title {
position: absolute;
left: 0;
right: 0;
text-align: center;
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 88rpx;
z-index: 1;
pointer-events: none;
}
.right-placeholder {
width: 40rpx;
margin-left: auto;
}
}
/* 选项卡样式 */
.tabs {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
.tab-item {
flex: 1;
text-align: center;
padding: 24rpx 0;
font-size: 32rpx;
color: #666;
position: relative;
&.active {
color: #007aff;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 80rpx;
height: 6rpx;
background-color: #007aff;
border-radius: 3rpx;
}
}
}
}
.form-container {
margin-top: 20rpx;
background-color: #fff;
border-radius: 16rpx;
padding: 40rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.form-item {
margin-bottom: 40rpx;
}
.label {
display: block;
font-size: 28rpx;
color: #666;
margin-bottom: 16rpx;
}
.input {
height: 80rpx;
border: 1rpx solid #eee;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
}
.placeholder {
color: #ccc;
font-size: 28rpx;
}
.submit-btn {
margin-top: 60rpx;
background-color: #007aff;
color: #fff;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
}
</style>

View File

@@ -34,7 +34,7 @@
</view>
<!-- 实名认证 -->
<view class="menu-item" @click="navigateTo('/pages/user/realAuth')">
<view class="menu-item" @click="navigateTo('/pages/more/realauth')">
<view class="item-left">
<uni-icons type="person" size="20" color="#4a7dff"></uni-icons>
<text class="text">{{ $t('more.authentication') }}</text>
@@ -48,7 +48,7 @@
</view>
<!-- 修改密码 -->
<view class="menu-item" @click="navigateTo('/pages/user/changePassword')">
<view class="menu-item" @click="navigateTo('/pages/more/changepwd')">
<view class="item-left">
<uni-icons type="locked" size="20" color="#4a7dff"></uni-icons>
<text class="text">{{ $t('more.changePassword') }}</text>
@@ -200,6 +200,8 @@ export default {
uni.showModal({
title: this.$t('common.tip'),
content: this.$t('more.confirmLogout'),
cancelText: this.$t('more.cancelLogout'), // 取消按钮的文字
confirmText: this.$t('more.certainLogout'), // 确认按钮的文字
success: (res) => {
if (res.confirm) {
uni.showLoading({

502
src/pages/more/realauth.vue Normal file
View File

@@ -0,0 +1,502 @@
<template>
<view class="auth-container">
<!-- 导航栏 -->
<view class="nav-bar">
<view class="back-btn" @click="navigateBack">
<image src="/src/static/pic/mark/more-left.png" mode="aspectFit"></image>
</view>
<view class="title">{{ $t('realauth.realauthtitle') }}</view>
<view class="right-placeholder"></view>
</view>
<!-- 表单区域 -->
<view class="form-container">
<!-- 姓名输入 -->
<view class="form-item">
<text class="label">{{ $t('realauth.realname') }}</text>
<input
class="input"
type="text"
v-model="realName"
:placeholder="$t('realauth.realnameplaceholder')"
placeholder-class="placeholder"
/>
</view>
<!-- 身份证号输入 -->
<view class="form-item">
<text class="label">{{ $t('realauth.IDcardnum') }}</text>
<input
class="input"
type="idcard"
v-model="idCardNumber"
:placeholder="$t('realauth.IDcardnumplaceholder')"
placeholder-class="placeholder"
maxlength="18"
@input="formatIdCard"
/>
</view>
<!-- 身份证正面 -->
<view class="form-item">
<text class="label">{{ $t('realauth.IDcardfront') }}</text>
<view class="upload-area" @click="chooseImage('front')">
<image
v-if="idCardFront"
:src="idCardFront"
class="id-card-image"
mode="aspectFill"
></image>
<view v-else class="upload-placeholder">
<image src="/static/images/camera.png" class="camera-icon"></image>
<text class="upload-text">{{ $t('realauth.uploadIDcardfront') }}</text>
<text class="upload-tip">{{ $t('realauth.uploadIDcardfronttips') }}</text>
</view>
</view>
</view>
<!-- 身份证反面 -->
<view class="form-item">
<text class="label">{{ $t('realauth.IDcardback') }}</text>
<view class="upload-area" @click="chooseImage('back')">
<image
v-if="idCardBack"
:src="idCardBack"
class="id-card-image"
mode="aspectFill"
></image>
<view v-else class="upload-placeholder">
<image src="/static/images/camera.png" class="camera-icon"></image>
<text class="upload-text">{{ $t('realauth.uploadIDcardback') }}</text>
<text class="upload-tip">{{ $t('realauth.uploadIDcardbacktips') }}</text>
</view>
</view>
</view>
<!-- 手持身份证照片 -->
<view class="form-item">
<text class="label">{{ $t('realauth.handleIDcardphoto') }}</text>
<view class="upload-area" @click="chooseImage('handheld')">
<image
v-if="handheldPhoto"
:src="handheldPhoto"
class="id-card-image"
mode="aspectFill"
></image>
<view v-else class="upload-placeholder">
<image src="/static/images/camera.png" class="camera-icon"></image>
<text class="upload-text">{{ $t('realauth.uploadIDcardhandle') }}</text>
<text class="upload-tip">{{ $t('realauth.uploadIDcardhandletips') }}</text>
</view>
</view>
</view>
<!-- 错误提示 -->
<view class="error-message" v-if="errorMessage">
{{ errorMessage }}
</view>
<!-- 提交按钮 -->
<button
class="submit-btn"
@click="submitAuth"
:disabled="isSubmitting"
>
<text v-if="!isSubmitting">{{$t('realauth.submitrealauth')}}</text>
<text v-else>{{$t('realauth.submitting')}}</text>
</button>
<!-- 认证提示 -->
<view class="auth-tips">
<text class="tip-title">{{$t('realauth.realauthtips')}}</text>
<text class="tip-item">{{$t('realauth.firsttips')}}</text>
<text class="tip-item">{{$t('realauth.secondtips')}}</text>
<text class="tip-item">{{$t('realauth.thirdtips')}}</text>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
realName: '',
idCardNumber: '',
idCardFront: '',
idCardBack: '',
handheldPhoto: '',
errorMessage: '',
isSubmitting: false
}
},
methods: {
navigateBack() {
uni.navigateBack()
},
// 格式化身份证输入自动转换小写x为大写X
formatIdCard() {
if (this.idCardNumber && this.idCardNumber.length > 0) {
const lastChar = this.idCardNumber.slice(-1)
if (lastChar === 'x') {
this.idCardNumber = this.idCardNumber.slice(0, -1) + 'X'
}
}
},
// 选择图片
chooseImage(type) {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePath = res.tempFilePaths[0]
// 这里可以添加图片压缩处理
this.compressImage(tempFilePath).then(compressedPath => {
switch(type) {
case 'front':
this.idCardFront = compressedPath
break
case 'back':
this.idCardBack = compressedPath
break
case 'handheld':
this.handheldPhoto = compressedPath
break
}
})
},
fail: (err) => {
console.error('选择图片失败:', err)
uni.showToast({
title: this.$t('IDcard.selectpicfailed'),
icon: 'none'
})
}
})
},
// 图片压缩
compressImage(tempFilePath) {
return new Promise((resolve) => {
// 实际项目中可以使用uni.compressImage API
// 这里简单返回原路径模拟压缩
resolve(tempFilePath)
/* 实际压缩代码示例:
uni.compressImage({
src: tempFilePath,
quality: 80,
success: res => {
resolve(res.tempFilePath)
},
fail: () => {
resolve(tempFilePath) // 压缩失败返回原图
}
})
*/
})
},
// 验证表单
validateForm() {
if (!this.realName || this.realName.trim() === '') {
this.errorMessage = this.$t('realauth.inputrealname')
return false
}
if (!this.idCardNumber || this.idCardNumber.trim() === '') {
this.errorMessage = this.$t('realauth.inputnumber')
return false
}
const idReg = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/
if (!idReg.test(this.idCardNumber)) {
this.errorMessage = this.$t('realauth.errors.invalidIdCard')
return false
}
if (!this.idCardFront) {
this.errorMessage = this.$t('realauth.errors.idCardFront')
return false
}
if (!this.idCardBack) {
this.errorMessage = this.$t('realauth.errors.idCardBack')
return false
}
if (!this.handheldPhoto) {
this.errorMessage = this.$t('realauth.errors.handheldPhoto')
return false
}
this.errorMessage = ''
return true
},
// 提交认证
submitAuth() {
if (!this.validateForm()) return
this.isSubmitting = true
// 模拟API请求
setTimeout(() => {
uni.showToast({
title: '实名认证提交成功',
icon: 'success'
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
this.isSubmitting = false
}, 2000)
/*
实际项目中应该这样上传:
const uploadTasks = [
this.uploadFile(this.idCardFront, 'front'),
this.uploadFile(this.idCardBack, 'back'),
this.uploadFile(this.handheldPhoto, 'handheld')
]
Promise.all(uploadTasks).then(results => {
const [frontUrl, backUrl, handheldUrl] = results
uni.request({
url: '/api/user/realAuth',
method: 'POST',
data: {
realName: this.realName,
idCardNumber: this.idCardNumber,
idCardFront: frontUrl,
idCardBack: backUrl,
handheldPhoto: handheldUrl
},
success: (res) => {
if (res.data.code === 200) {
uni.showToast({ title: '认证成功' })
// 跳转或返回
} else {
this.errorMessage = res.data.message || '认证失败'
}
},
fail: (err) => {
this.errorMessage = '网络错误,请重试'
},
complete: () => {
this.isSubmitting = false
}
})
}).catch(err => {
this.errorMessage = '图片上传失败'
this.isSubmitting = false
})
*/
},
// 实际文件上传方法
uploadFile(filePath, type) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: '/api/upload',
filePath,
name: 'file',
formData: { type },
success: (res) => {
const data = JSON.parse(res.data)
if (data.code === 200) {
resolve(data.data.url)
} else {
reject(new Error(data.message))
}
},
fail: (err) => {
reject(err)
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.auth-container {
padding: 0;
min-height: 100vh;
background-color: #f5f5f5;
}
/* 导航栏样式 */
.nav-bar {
display: flex;
align-items: center;
height: 100rpx;
padding: 0 30rpx;
padding-top: var(--status-bar-height);
position: relative;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
.back-btn {
width: 40rpx;
height: 40rpx;
display: flex;
align-items: center;
justify-content: flex-start;
position: relative;
image {
width: 100%;
height: 100%;
filter: invert(1);
}
}
.title {
position: absolute;
left: 0;
right: 0;
text-align: center;
font-size: 36rpx;
font-weight: bold;
color: #333;
line-height: 88rpx;
z-index: 1;
pointer-events: none;
}
.right-placeholder {
width: 40rpx;
margin-left: auto;
}
}
.form-container {
padding: 30rpx;
}
.form-item {
margin-bottom: 40rpx;
}
.label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 20rpx;
font-weight: 500;
}
.input {
height: 80rpx;
background-color: #fff;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
border: 1rpx solid #eee;
}
.placeholder {
color: #ccc;
font-size: 28rpx;
}
/* 上传区域样式 */
.upload-area {
height: 300rpx;
background-color: #fff;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
border: 1rpx dashed #ccc;
overflow: hidden;
.id-card-image {
width: 100%;
height: 100%;
}
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.camera-icon {
width: 80rpx;
height: 80rpx;
margin-bottom: 20rpx;
}
.upload-text {
font-size: 28rpx;
color: #333;
margin-bottom: 10rpx;
}
.upload-tip {
font-size: 24rpx;
color: #999;
}
}
.error-message {
color: #ff4d4f;
font-size: 26rpx;
margin-bottom: 30rpx;
text-align: center;
}
.submit-btn {
margin-top: 60rpx;
background-color: #007aff;
color: #fff;
height: 88rpx;
line-height: 88rpx;
border-radius: 44rpx;
font-size: 32rpx;
&:disabled {
background-color: #b3d4fc;
}
}
/* 认证提示 */
.auth-tips {
margin-top: 60rpx;
padding: 30rpx;
background-color: #f9f9f9;
border-radius: 8rpx;
.tip-title {
display: block;
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 20rpx;
}
.tip-item {
display: block;
font-size: 24rpx;
color: #666;
margin-bottom: 15rpx;
line-height: 1.6;
&:before {
content: "•";
margin-right: 10rpx;
}
}
}
</style>

View File

@@ -270,7 +270,7 @@ export default {
},
validatePassword() {
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{6,}$/
if (this.password && !pwdReg.test(this.password)) {
this.setError(this.$t('register.errors.invalidPassword'))
return false
@@ -301,7 +301,7 @@ export default {
checkFormValidity() {
const idReg = /^\d{5}[0-9X]$/
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{8,}$/
const pwdReg = /^(?=.*[A-Za-z])(?=.*\d).{6,}$/
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
const isIdValid = this.idCardLast6 && idReg.test(this.idCardLast6)

View File

@@ -48,7 +48,7 @@
"register.emailPlaceholder": "请输入邮箱",
"register.errors.emailRequired": "邮箱不能为空",
"register.idCardLast6Error": "请输入正确的身份证号码",
"register.errors.invalidPassword": "密码至少8位,必须包含数字和字母",
"register.errors.invalidPassword": "密码至少6位,必须包含数字和字母",
"register.errors.emptyPassword": "密码不能为空",
"register.errors.confirmPasswordRequired": "请再次输入密码",
"register.errors.invalidEmail": "请输入正确的邮箱",
@@ -90,6 +90,8 @@
"more.languageChanged":"语言已切换成",
"more.close":"关闭",
"more.confirmLogout":"确定要退出登录吗?",
"more.cancelLogout":"取消",
"more.certainLogout":"确定",
"common.tip":"提示",
"common.loggingOut":"正在退出登录...",
@@ -124,9 +126,91 @@
"bank.and":"和",
"bank.bindcardagreement":"《银行卡绑定协议》",
"bank.submit":"提交",
"bank.inputBankcardnum":"请输入银行卡号",
"bank.selectBankcardname":"请选择银行",
"bank.cardpersonnamePlaceholder":"请输入持卡人姓名",
"bank.idCardPlaceholder":"请输入身份证号",
"bank.phonePlaceholder":"请输入预留手机号",
"bank.vericodePlaceholder":"请输入验证码",
"bank.unbindSuccess":"解绑成功",
"bank.getverifycode":"获取验证码",
"bank.inputcorrectphone":"请输入正确的手机号",
"bank.sendingcode":"正在发送...",
"bank.codesent":"验证码已发送",
"bank.inputcorrectcardnum":"请输入正确的银行卡号",
"bank.selectbank":"请选择银行",
"bank.inputcorrectcardpersonname":"请输入正确的持卡人姓名",
"bank.inputcorrectidcardnum":"请输入正确的身份证号",
"bank.inputcorrectcode":"请输入正确的验证码",
"bank.bankdetail":"银行卡详情",
"bank.banknum":"银行卡号",
"bank.cardtype":"银行卡类型",
"bank.cardholder":"持卡人",
"bank.bindphone":"绑定手机号",
"bank.certain":"确定",
"bank.binding":"正在绑定...",
"bank.unbindtips":"确定要解绑吗?",
"bank.unbinding":"正在解绑...",
"bank.certainunbind":"确定要解绑",
"bank.endfour":"后四位为",
"bank.card":"的银行卡吗",
"bank.ICBC":"中国工商银行",
"bank.CMB":"招商银行"
"bank.CMB":"招商银行",
"changepwd.changepwdtitle":"修改密码",
"changepwd.changeloginPassword":"修改登录密码",
"changepwd.changetransactionPassword":"修改交易密码",
"changepwd.oldLoginPwd":"原登录密码",
"changepwd.oldLoginPwdPlaceholder":"请输入原登录密码",
"changepwd.newLoginPwd":"新登录密码",
"changepwd.newLoginPwdPlaceholder":"请输入新登录密码",
"changepwd.confirmLoginPwd":"确认登录密码",
"changepwd.confirmLoginPwdPlaceholder":"请再次输入登录密码",
"changepwd.confirmupdate":"确认修改",
"changepwd.oldTransactionPwd":"原交易密码",
"changepwd.oldTransactionPwdPlaceholder":"请输入原交易密码",
"changepwd.newTransactionPwd":"新交易密码",
"changepwd.newTransactionPwdPlaceholder":"请输入新交易密码",
"changepwd.confirmTransactionPwd":"确认交易密码",
"changepwd.confirmTransactionPwdPlaceholder":"请再次输入新交易密码",
"changepwd.inputtransactionpwd":"请输入交易密码",
"changepwd.pwdlength":"密码长度不能小于6位",
"changepwd.loginpasswordNotMatch":"两次输入的登录密码不一致",
"changepwd.transactionpwdNotMatch":"两次输入的交易密码不一致",
"changepwd.changingPassword":"正在修改密码...",
"changepwd.changeloginpwdSuccess":"修改登录密码成功",
"changepwd.changetransactionpwdSuccess":"修改交易密码成功",
"changepwd.changeFail":"密码修改失败",
"changepwd.requestError":"请求错误",
//实名认证
"realauth.realauthtitle":"实名认证",
"realauth.realname":"真实姓名",
"realauth.realnameplaceholder":"请输入真实姓名",
"realauth.IDcardnum":"身份证号码",
"realauth.IDcardnumplaceholder":"请输入身份证号码",
"realauth.IDcardfront":"身份证正面",
"realauth.uploadIDcardfront":"点击上传身份证正面",
"realauth.uploadIDcardfronttips":"需包含国徽面",
"realauth.IDcardback":"身份证反面",
"realauth.uploadIDcardback":"点击上传身份证反面",
"realauth.uploadIDcardbacktips":"需包含个人信息面",
"realauth.handleIDcardphoto":"手持身份证照片",
"realauth.uploadIDcardhandle":"点击上传手持身份证照片",
"realauth.uploadIDcardhandletips":"需清晰可见身份证信息",
"realauth.submitrealauth":"提交认证",
"realauth.submitting":"提交中...",
"realauth.realauthtips":"认证须知:",
"realauth.firsttips":"1. 请确保上传的身份证信息清晰可见",
"realauth.secondtips":"2. 身份证需在有效期内",
"realauth.thirdtips":"3. 信息仅用于实名认证,我们将严格保密",
"realauth.errors.handheldPhoto":"请上传手持身份证照片",
"realauth.errors.idCardBack":"请上传身份证反面",
"realauth.errors.idCardFront":"请上传身份证正面",
"realauth.errors.invalidIdCard":"请输入正确的身份证号码",
"realauth.inputnumber":"请输入身份证号码",
"realauth.inputrealname":"请输入真实姓名",
"realauth.selectpicfailed":"选择图片失败",
""
}