Files
bdrp-app/src/components/RealNameAuthDialog.vue
2026-04-23 14:57:35 +08:00

409 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { computed, onUnmounted, ref } from 'vue'
import { useDialogStore } from '@/stores/dialogStore'
const router = useRouter()
const dialogStore = useDialogStore()
const agentStore = useAgentStore()
const userStore = useUserStore()
// 表单数据
const realName = ref('')
const idCard = ref('')
const phoneNumber = ref('')
const verificationCode = ref('')
const isAgreed = ref(false)
// 倒计时相关
const isCountingDown = ref(false)
const countdown = ref(60)
let timer = null
function showToast(options) {
const message = typeof options === 'string' ? options : (options?.message || options?.title || '')
if (!message)
return
uni.showToast({
title: message,
icon: options?.type === 'success' ? 'success' : 'none',
})
}
// 表单验证
const isPhoneNumberValid = computed(() => {
return /^1[3-9]\d{9}$/.test(phoneNumber.value)
})
const isIdCardValid = computed(() => {
return /^(?:\d{15}|\d{17}[\dX])$/i.test(idCard.value)
})
const isRealNameValid = computed(() => {
return /^[\u4E00-\u9FA5]{2,}$/.test(realName.value)
})
const canSubmit = computed(() => {
return (
isPhoneNumberValid.value
&& isIdCardValid.value
&& isRealNameValid.value
&& verificationCode.value.length === 6
&& isAgreed.value
)
})
// 发送验证码
async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value)
return
if (!isPhoneNumberValid.value) {
showToast({ message: '请输入有效的手机号' })
return
}
const { data, error } = await useApiFetch('auth/sendSms')
.post({ mobile: phoneNumber.value, actionType: 'realName', captchaVerifyParam: '' })
.json()
if (!error.value && data.value?.code === 200) {
showToast({ message: '获取成功' })
startCountdown()
}
else {
showToast(data.value?.msg || '发送失败')
}
}
function startCountdown() {
isCountingDown.value = true
countdown.value = 60
timer = setInterval(() => {
if (countdown.value > 0) {
countdown.value--
}
else {
clearInterval(timer)
isCountingDown.value = false
}
}, 1000)
}
// 提交实名认证
async function handleSubmit() {
if (!isRealNameValid.value) {
showToast({ message: '请输入有效的姓名' })
return
}
if (!isIdCardValid.value) {
showToast({ message: '请输入有效的身份证号' })
return
}
if (!isPhoneNumberValid.value) {
showToast({ message: '请输入有效的手机号' })
return
}
if (verificationCode.value.length !== 6) {
showToast({ message: '请输入有效的验证码' })
return
}
if (!isAgreed.value) {
showToast({ message: '请先同意用户协议' })
return
}
const { data, error } = await useApiFetch('/agent/real_name')
.post({
name: realName.value,
id_card: idCard.value,
mobile: phoneNumber.value,
code: verificationCode.value,
})
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
showToast({ message: '认证成功' })
agentStore.isRealName = true
await agentStore.fetchAgentStatus()
await userStore.fetchUserInfo()
closeDialog()
}
else {
showToast(data.value.msg)
}
}
}
function closeDialog() {
dialogStore.closeRealNameAuth()
realName.value = ''
idCard.value = ''
phoneNumber.value = ''
verificationCode.value = ''
isAgreed.value = false
if (timer) {
clearInterval(timer)
timer = null
}
isCountingDown.value = false
}
function toUserAgreement() {
closeDialog()
router.push(`/userAgreement`)
}
function toPrivacyPolicy() {
closeDialog()
router.push(`/privacyPolicy`)
}
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
})
</script>
<template>
<view v-if="dialogStore.showRealNameAuth" class="real-name-auth-dialog-box">
<wd-popup
v-model="dialogStore.showRealNameAuth"
round
position="bottom"
:style="{ maxHeight: '90vh' }"
@close="closeDialog"
>
<view
class="real-name-auth-dialog"
style="background: linear-gradient(135deg, var(--color-primary-light), rgba(255,255,255,0.9));"
>
<view class="title-bar">
<view class="text-base font-bold sm:text-lg" style="color: var(--color-text-primary);">
实名认证
</view>
<wd-icon name="cross" class="close-icon" style="color: var(--color-text-secondary);" @click="closeDialog" />
</view>
<view class="dialog-content">
<view class="dialog-inner px-4 pb-4 pt-2">
<view
class="auth-notice mb-4 rounded-xl p-3 sm:p-4"
style="background-color: var(--color-primary-light); border: 1px solid rgba(162, 37, 37, 0.2);"
>
<view class="text-xs space-y-1.5 sm:text-sm sm:space-y-2" style="color: var(--color-text-primary);">
<text class="font-medium" style="color: var(--color-primary);">
实名认证说明
</text>
<text>1. 实名认证是提现的必要条件</text>
<text>2. 提现金额将转入您实名认证的银行卡账户</text>
<text>3. 请确保填写的信息真实有效否则将影响提现功能的使用</text>
<text>4. 认证信息提交后将无法修改请仔细核对</text>
</view>
</view>
<view class="real-name-form">
<view class="form-item">
<text class="form-label">
姓名
</text>
<wd-input
v-model="realName"
class="field-wd-input"
type="text"
placeholder="请输入真实姓名"
no-border
clearable
/>
</view>
<view class="form-item">
<text class="form-label">
身份证
</text>
<wd-input
v-model="idCard"
class="field-wd-input"
type="text"
placeholder="请输入身份证号"
maxlength="18"
no-border
clearable
/>
</view>
<view class="form-item">
<text class="form-label">
手机号
</text>
<wd-input
v-model="phoneNumber"
class="field-wd-input"
type="number"
placeholder="请输入手机号"
maxlength="11"
no-border
clearable
/>
</view>
<view class="form-item">
<text class="form-label">
验证码
</text>
<view class="verification-input-wrapper">
<wd-input
v-model="verificationCode"
class="verification-wd-input"
placeholder="请输入验证码"
maxlength="6"
no-border
clearable
>
<template #suffix>
<wd-button
size="small"
type="primary"
plain
:disabled="isCountingDown || !isPhoneNumberValid"
@click="sendVerificationCode"
>
{{ isCountingDown ? `${countdown}s` : '获取验证码' }}
</wd-button>
</template>
</wd-input>
</view>
</view>
<view class="agreement-wrapper">
<wd-checkbox v-model="isAgreed" shape="square" size="18px" />
<text class="agreement-text">
我已阅读并同意
<text class="agreement-link" @click="toUserAgreement">
用户协议
</text>
<text class="agreement-link" @click="toPrivacyPolicy">
隐私政策
</text>
并确认以上信息真实有效将用于提现等资金操作
</text>
</view>
<wd-button
class="submit-wd-btn"
block
type="primary"
:disabled="!canSubmit"
@click="handleSubmit"
>
确认认证
</wd-button>
</view>
</view>
</view>
</view>
</wd-popup>
</view>
</template>
<style scoped>
.real-name-auth-dialog {
max-height: 90vh;
display: flex;
flex-direction: column;
}
.dialog-content {
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
.title-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid var(--color-border-primary);
flex-shrink: 0;
}
.close-icon {
font-size: 18px;
cursor: pointer;
}
@media (min-width: 640px) {
.close-icon {
font-size: 20px;
}
}
/* 与登录页 login.vue 表单卡片一致 */
.real-name-form {
border-radius: 16px;
background-color: rgba(255, 255, 255, 0.96);
box-shadow: 0 8px 28px rgba(63, 63, 63, 0.08);
padding: 1.25rem 1rem 1.5rem;
backdrop-filter: blur(4px);
}
.form-item {
margin-bottom: 1rem;
display: flex;
align-items: center;
border-radius: 12px;
background-color: #fff;
padding: 0 0.75rem;
min-height: 48px;
}
.form-label {
font-size: 0.9375rem;
color: #111827;
margin-right: 0.75rem;
font-weight: 500;
min-width: 3.25rem;
flex-shrink: 0;
}
.field-wd-input {
flex: 1;
}
.verification-input-wrapper {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
.verification-wd-input {
width: 100%;
}
.agreement-wrapper {
display: flex;
align-items: flex-start;
margin-top: 1rem;
margin-bottom: 1rem;
}
.agreement-text {
font-size: 0.75rem;
color: #6b7280;
line-height: 1.5;
margin-left: 0.5rem;
flex: 1;
}
.agreement-link {
color: #2563eb;
cursor: pointer;
}
.submit-wd-btn {
margin-top: 0.25rem;
}
.real-name-auth-dialog-box {
position: relative;
}
</style>