This commit is contained in:
qiuyuan 2026-01-12 09:55:41 +08:00
parent b788b75175
commit ab2ab20b72
2 changed files with 623 additions and 484 deletions

View File

@ -35,3 +35,12 @@ export const DelInvoiceTitleInfo = (id:any) => request.delete(`/v1/invoice_title
// 删除镜像 // 删除镜像
export const delImageItem = (params:any) => request.delete(`/v1/image/delete_image/${params.image_id}`) export const delImageItem = (params:any) => request.delete(`/v1/image/delete_image/${params.image_id}`)
// 银行卡管理-添加银行卡
export const addBankCard = (params:any) => request.put('/v1/bank_card/add_bank_card',params)
// 银行卡管理-获取绑定信息
export const getBankCardInfo = (params:any) => request.get('/v1/bank_card/bank_card_info',{params})
// 银行卡管理 - 解绑银行卡
export const delBankCard = (params:any) => request.post('/v1/bank_card/delete_bank_card',params)

View File

@ -14,17 +14,17 @@
<div class="bank-icon"> <div class="bank-icon">
<img src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg" alt="银联" /> <img src="https://gw.alipayobjects.com/zos/rmsportal/XuVpGqBFxXplzvLjJBZB.svg" alt="银联" />
</div> </div>
<div class="card-number">{{ formatCardNumber(boundCard.cardNumber) }}</div> <div class="card-number">{{ formatCardNumber(boundCard.bank_card_no) }}</div>
</div> </div>
<div class="card-details"> <div class="card-details">
<div class="detail-item"> <div class="detail-item">
<span class="label">持卡人姓名</span> <span class="label">持卡人姓名</span>
<span class="value">{{ boundCard.realName }}</span> <span class="value">{{ boundCard.name }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="label">开户行</span> <span class="label">开户行</span>
<span class="value">{{ boundCard.bankName }}</span> <span class="value">{{ boundCard.bank_name || '--' }}</span>
</div> </div>
<div class="detail-item"> <div class="detail-item">
<span class="label">手机号</span> <span class="label">手机号</span>
@ -49,48 +49,36 @@
</div> </div>
<!-- 添加银行卡对话框 --> <!-- 添加银行卡对话框 -->
<a-modal <a-modal v-model:visible="addModalVisible" title="添加银行卡" @ok="handleAddCard" @cancel="handleAddCancel"
v-model:visible="addModalVisible" :confirm-loading="addLoading" :width="500" :ok-text="'绑定'" :cancel-text="'取消'">
title="添加银行卡" <a-form ref="addFormRef" :model="addFormState" :rules="addFormRules" layout="vertical">
@ok="handleAddCard"
@cancel="handleAddCancel"
:confirm-loading="addLoading"
:width="500"
:ok-text="'绑定'"
:cancel-text="'取消'"
>
<a-form
ref="addFormRef"
:model="addFormState"
:rules="addFormRules"
layout="vertical"
>
<a-form-item label="真实姓名" name="realName"> <a-form-item label="真实姓名" name="realName">
<a-input <a-input v-model:value="addFormState.realName" placeholder="请输入持卡人真实姓名" size="large" />
v-model:value="addFormState.realName" </a-form-item>
placeholder="请输入持卡人真实姓名"
size="large" <a-form-item label="手机号" name="phone">
/> <div class="sms-code-input">
<a-input v-model:value="addFormState.phone" placeholder="请输入银行预留手机号" size="large"
:maxlength="11" />
<a-button type="primary" :disabled="addCountdown > 0" @click="sendAddSmsCode"
class="sms-button">
{{ addCountdown > 0 ? `${addCountdown}秒后重新获取` : '获取验证码' }}
</a-button>
</div>
</a-form-item>
<a-form-item label="短信验证码" name="smsCode">
<a-input v-model:value="addFormState.smsCode" placeholder="请输入短信验证码" size="large" :maxlength="6" />
</a-form-item> </a-form-item>
<a-form-item label="银行卡号" name="cardNumber"> <a-form-item label="银行卡号" name="cardNumber">
<a-input <a-input v-model:value="addFormState.cardNumber" placeholder="请输入银行卡号" size="large" :maxlength="19"
v-model:value="addFormState.cardNumber" @input="formatCardInput" />
placeholder="请输入银行卡号"
size="large"
:maxlength="19"
@input="formatCardInput"
/>
</a-form-item> </a-form-item>
<a-form-item label="开户行" name="bankName"> <a-form-item label="开户行" name="bankName">
<a-select <a-select v-model:value="addFormState.bankName" placeholder="请选择开户行" size="large"
v-model:value="addFormState.bankName" :options="bankOptions" show-search />
placeholder="请选择开户行"
size="large"
:options="bankOptions"
show-search
/>
</a-form-item> </a-form-item>
<a-form-item name="agreement"> <a-form-item name="agreement">
@ -103,62 +91,39 @@
</a-modal> </a-modal>
<!-- 解绑银行卡对话框 --> <!-- 解绑银行卡对话框 -->
<a-modal <!-- 修复移除 @ok 事件中的参数传递改为在方法内部处理 -->
v-model:visible="unbindModalVisible" <a-modal v-model:visible="unbindModalVisible" title="解绑银行卡" @ok="handleUnbindCard"
title="解绑银行卡" @cancel="handleUnbindCancel" :confirm-loading="unbindLoading" :width="500" :ok-text="'确认解绑'"
@ok="handleUnbindCard" :cancel-text="'取消'">
@cancel="handleUnbindCancel" <div v-if="boundCard" class="unbind-info">
:confirm-loading="unbindLoading"
:width="500"
:ok-text="'确认解绑'"
:cancel-text="'取消'"
>
<div class="unbind-info">
<div class="info-item"> <div class="info-item">
<span class="label">持卡人姓名</span> <span class="label">持卡人姓名</span>
<span class="value">{{ boundCard.realName }}</span> <span class="value">{{ boundCard.name }}</span>
</div> </div>
<div class="info-item"> <div class="info-item">
<span class="label">银行卡号</span> <span class="label">银行卡号</span>
<span class="value">{{ formatCardNumber(boundCard.cardNumber) }}</span> <span class="value">{{ formatCardNumber(boundCard.bank_card_no) }}</span>
</div> </div>
<div class="info-item"> <div class="info-item">
<span class="label">开户行</span> <span class="label">开户行</span>
<span class="value">{{ boundCard.bankName }}</span> <span class="value">{{ boundCard.bank_name || '--' }}</span>
</div> </div>
<a-divider /> <a-divider />
</div> </div>
<a-form <a-form ref="unbindFormRef" :model="unbindFormState" :rules="unbindFormRules" layout="vertical">
ref="unbindFormRef"
:model="unbindFormState"
:rules="unbindFormRules"
layout="vertical"
>
<a-form-item label="手机号码" name="phone"> <a-form-item label="手机号码" name="phone">
<a-input <a-input v-model:value="unbindFormState.phone" placeholder="请输入银行预留手机号" size="large"
v-model:value="unbindFormState.phone" :maxlength="11" />
placeholder="请输入银行预留手机号"
size="large"
:maxlength="11"
/>
</a-form-item> </a-form-item>
<a-form-item label="短信验证码" name="smsCode"> <a-form-item label="短信验证码" name="smsCode">
<div class="sms-code-input"> <div class="sms-code-input">
<a-input <a-input v-model:value="unbindFormState.smsCode" placeholder="请输入短信验证码" size="large"
v-model:value="unbindFormState.smsCode" :maxlength="6" />
placeholder="请输入短信验证码" <a-button type="primary" :disabled="unbindCountdown > 0" @click="sendUnbindSmsCode"
size="large" class="sms-button">
:maxlength="6" {{ unbindCountdown > 0 ? `${unbindCountdown}秒后重新获取` : '获取验证码' }}
/>
<a-button
type="primary"
:disabled="countdown > 0"
@click="sendSmsCode"
class="sms-button"
>
{{ countdown > 0 ? `${countdown}秒后重新获取` : '获取验证码' }}
</a-button> </a-button>
</div> </div>
</a-form-item> </a-form-item>
@ -174,17 +139,22 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { ref, reactive, computed, onMounted } from 'vue' import { ref, reactive, computed, onMounted, nextTick } from 'vue'
import { PlusOutlined } from '@ant-design/icons-vue' import { PlusOutlined } from '@ant-design/icons-vue'
import { message, Modal } from 'ant-design-vue' import { message, Modal } from 'ant-design-vue'
import { addBankCard, getBankCardInfo, delBankCard } from '@/apis/admin'
onMounted(() => {
getBoundCardInfo();
})
// - // -
const boundCard = ref(null) const boundCard = ref<any>(null)
// - false // - false
const hasBoundCard = computed(() => { const hasBoundCard = computed(() => {
return boundCard.value !== null && boundCard.value !== undefined return boundCard.value !== null && boundCard.value !== undefined && boundCard.value.bank_card_no
}) })
// //
@ -212,17 +182,30 @@ const addLoading = ref(false)
const addFormRef = ref() const addFormRef = ref()
const addFormState = reactive({ const addFormState = reactive({
realName: '', realName: '',
phone: '',
smsCode: '',
cardNumber: '', cardNumber: '',
bankName: '', bankName: '',
agreement: false agreement: false
}) })
//
const addCountdown = ref(0)
// //
const addFormRules = { const addFormRules = {
realName: [ realName: [
{ required: true, message: '请输入真实姓名', trigger: 'blur' }, { required: true, message: '请输入真实姓名', trigger: 'blur' },
{ pattern: /^[\u4e00-\u9fa5]{2,10}$/, message: '请输入2-10位中文字符', trigger: 'blur' } { pattern: /^[\u4e00-\u9fa5]{2,10}$/, message: '请输入2-10位中文字符', trigger: 'blur' }
], ],
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
],
smsCode: [
{ required: true, message: '请输入短信验证码', trigger: 'blur' },
{ pattern: /^\d{6}$/, message: '验证码为6位数字', trigger: 'blur' }
],
cardNumber: [ cardNumber: [
{ required: true, message: '请输入银行卡号', trigger: 'blur' }, { required: true, message: '请输入银行卡号', trigger: 'blur' },
{ {
@ -253,8 +236,8 @@ const unbindFormState = reactive({
agreement: false agreement: false
}) })
// //
const countdown = ref(0) const unbindCountdown = ref(0)
// //
const unbindFormRules = { const unbindFormRules = {
@ -292,10 +275,13 @@ const showAddModal = () => {
// //
Object.assign(addFormState, { Object.assign(addFormState, {
realName: '', realName: '',
phone: '',
smsCode: '',
cardNumber: '', cardNumber: '',
bankName: '', bankName: '',
agreement: false agreement: false
}) })
addCountdown.value = 0
} }
// //
@ -307,57 +293,195 @@ const showUnbindModal = () => {
smsCode: '', smsCode: '',
agreement: false agreement: false
}) })
unbindCountdown.value = 0
//
if (boundCard.value && boundCard.value.phone) {
//
//
//
unbindFormState.phone = ''
}
} }
// //
const handleAddCard = () => { const handleAddCard = async () => {
addFormRef.value.validate().then(() => { try {
//
await addFormRef.value.validate()
addLoading.value = true addLoading.value = true
// API
setTimeout(() => {
// //
const cleanCardNumber = addFormState.cardNumber.replace(/\s+/g, '') const cleanCardNumber = addFormState.cardNumber.replace(/\s+/g, '')
// // API
const requestData = {
bank_card_no: cleanCardNumber, //
bank_name: addFormState.bankName, //
code: addFormState.smsCode, //
name: addFormState.realName, //
phone: addFormState.phone //
}
console.log('发送银行卡添加请求:', requestData)
// API
const res = await addBankCard(requestData)
console.log('添加银行卡结果:', res)
//
boundCard.value = { boundCard.value = {
realName: addFormState.realName, ...requestData,
cardNumber: cleanCardNumber, phone: addFormState.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') //
bankName: addFormState.bankName,
phone: '138****' + cleanCardNumber.substr(cleanCardNumber.length - 4) //
} }
addModalVisible.value = false addModalVisible.value = false
addLoading.value = false addLoading.value = false
message.success('银行卡绑定成功') message.success('银行卡绑定成功')
}, 1000) getBoundCardInfo();
}).catch(error => { } catch (error: any) {
console.log('验证失败:', error) console.log('银行卡绑定失败:', error)
}) addLoading.value = false
//
if (error.response?.data?.message) {
message.error(error.response.data.message)
} else if (error.message) {
message.error(error.message)
} else {
message.error('银行卡绑定失败,请重试')
}
}
}
//
const getBoundCardInfo = async () => {
try {
const res = await getBankCardInfo()
console.log('获取绑定银行卡信息结果:', res)
//
if (res && res.bank_card_no) {
//
const phoneNumber = res.phone || ''
const formattedPhone = phoneNumber ?
phoneNumber.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2') :
''
boundCard.value = {
...res,
phone: formattedPhone,
// API
original_phone: res.phone || ''
}
console.log('已绑定银行卡信息:', boundCard.value)
} else {
//
boundCard.value = null
}
} catch (error) {
console.log('获取绑定银行卡信息失败:', error)
boundCard.value = null
}
} }
// //
const handleUnbindCard = () => { const handleUnbindCard = async () => {
unbindFormRef.value.validate().then(() => { try {
await unbindFormRef.value.validate()
unbindLoading.value = true unbindLoading.value = true
// API //
setTimeout(() => { if (!boundCard.value || !boundCard.value.id) {
// message.error('银行卡信息无效,无法解绑')
boundCard.value = null
unbindModalVisible.value = false
unbindLoading.value = false unbindLoading.value = false
return
}
message.success('银行卡解绑成功') //
}, 1000) const requestData = {
}).catch(error => { code: unbindFormState.smsCode, //
console.log('验证失败:', error) id: boundCard.value.id, // ID
phone: unbindFormState.phone //
}
console.log('发送银行卡解绑请求:', requestData)
// API
const res: any = await delBankCard(requestData)
console.log('解绑银行卡结果:', res)
if (res.code === 1) {
message.success('解绑成功')
//
unbindModalVisible.value = false
//
Object.assign(unbindFormState, {
phone: '',
smsCode: '',
agreement: false
}) })
//
await nextTick()
// null
await getBoundCardInfo();
} else {
message.error(res.msg || '解绑失败')
}
} catch (error: any) {
console.log('银行卡解绑失败:', error)
message.error('银行卡解绑失败,请重试')
} finally {
unbindLoading.value = false
}
} }
// //
const sendSmsCode = () => { const sendAddSmsCode = async () => {
if (!addFormState.phone) {
message.warning('请输入手机号码')
return
}
if (!/^1[3-9]\d{9}$/.test(addFormState.phone)) {
message.warning('手机号格式不正确')
return
}
try {
// TODO: API
// sendSmsCode API
// const res = await sendSmsCode({
// phone: addFormState.phone,
// type: 'BIND_CARD' //
// })
//
addCountdown.value = 60
const timer = setInterval(() => {
addCountdown.value -= 1
if (addCountdown.value <= 0) {
clearInterval(timer)
}
}, 1000)
// 123456
message.success('验证码已发送请注意查收演示验证码123456')
//
// addFormState.smsCode = '123456'
} catch (error) {
console.log('发送验证码失败:', error)
message.error('发送验证码失败,请重试')
}
}
//
const sendUnbindSmsCode = async () => {
if (!unbindFormState.phone) { if (!unbindFormState.phone) {
message.warning('请输入手机号码') message.warning('请输入手机号码')
return return
@ -368,32 +492,38 @@ const sendSmsCode = () => {
return return
} }
// try {
const expectedPhone = boundCard.value.phone.replace(/\*/g, '') //
const inputPhone = unbindFormState.phone //
const lastFourDigits = boundCard.value.cardNumber.substr(boundCard.value.cardNumber.length - 4) // API
if (inputPhone !== '138' + lastFourDigits) { // TODO: API
message.error('手机号与预留手机号不匹配') // const res = await sendSmsCode({
return // phone: unbindFormState.phone,
} // type: 'UNBIND_CARD'
// })
// //
countdown.value = 60 unbindCountdown.value = 60
const timer = setInterval(() => { const timer = setInterval(() => {
countdown.value -= 1 unbindCountdown.value -= 1
if (countdown.value <= 0) { if (unbindCountdown.value <= 0) {
clearInterval(timer) clearInterval(timer)
} }
}, 1000) }, 1000)
// 123456 //
message.success('验证码已发送请注意查收演示验证码123456') message.success('验证码已发送请注意查收演示验证码123456')
unbindFormState.smsCode = '123456' //
// unbindFormState.smsCode = '123456'
} catch (error) {
console.log('发送验证码失败:', error)
message.error('发送验证码失败,请重试')
}
} }
// //
const formatCardNumber = (cardNumber) => { const formatCardNumber = (cardNumber: string) => {
if (!cardNumber) return '' if (!cardNumber) return ''
const cleanNumber = cardNumber.replace(/\s+/g, '') const cleanNumber = cardNumber.replace(/\s+/g, '')
const visibleDigits = 4 const visibleDigits = 4
@ -507,9 +637,8 @@ const showUnbindAgreement = () => {
padding-top: 4px; padding-top: 4px;
} }
.add-card-section, .bound-card-section { .add-card-section,
/* display: flex; .bound-card-section {
justify-content: center; */
align-items: center; align-items: center;
min-height: 320px; min-height: 320px;
} }
@ -577,7 +706,7 @@ const showUnbindAgreement = () => {
right: -50%; right: -50%;
width: 200px; width: 200px;
height: 200px; height: 200px;
background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%); background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0) 70%);
border-radius: 50%; border-radius: 50%;
} }
@ -683,6 +812,7 @@ const showUnbindAgreement = () => {
.sms-button { .sms-button {
white-space: nowrap; white-space: nowrap;
min-width: 120px;
} }
:deep(.ant-modal-body) { :deep(.ant-modal-body) {