web-base-pc/src/components/walletBindBank.vue

531 lines
17 KiB
Vue
Raw Normal View History

2025-03-23 09:23:38 +08:00
<template>
<el-dialog
title="绑定银行卡"
:visible.sync="dialogVisible"
width="40%"
2025-03-23 09:23:38 +08:00
:close-on-click-modal="false"
:before-close="handleCloseDialog"
2025-03-23 09:23:38 +08:00
>
<div class="bind-bank-form-container">
2025-03-23 09:23:38 +08:00
<el-form
ref="bankForm"
2025-03-23 09:23:38 +08:00
:rules="rules"
:model="form"
label-position="right"
label-width="100px"
>
<el-form-item label="银行卡号:" prop="cardNumber">
2025-03-23 09:23:38 +08:00
<el-input
v-model="form.cardNumber"
placeholder="请输入银行卡号"
2025-03-23 09:23:38 +08:00
></el-input>
</el-form-item>
<el-form-item label="银行选择:" prop="bankId">
<el-select v-model="form.bankId" placeholder="请选择银行名称">
2025-03-23 09:23:38 +08:00
<el-option
v-for="(item, index) in bankCardChioceList"
:key="index"
:label="item.bankName"
:value="item.pkId"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="开户支行:" prop="subBankName">
2025-03-23 09:23:38 +08:00
<el-input
v-model="form.subBankName"
placeholder="请输入开户支行"
2025-03-23 09:23:38 +08:00
></el-input>
</el-form-item>
<el-form-item label="姓名:" prop="accountName">
2025-03-23 09:23:38 +08:00
<el-input
v-model="form.accountName"
placeholder="请输入姓名"
2025-03-23 09:23:38 +08:00
></el-input>
</el-form-item>
<el-form-item label="证件类型:" prop="idType">
<el-select v-model="form.idType" placeholder="请选择证件类型">
2025-03-23 09:23:38 +08:00
<el-option
v-for="(item, index) in cardTypeList"
:key="index"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="证件号码:" prop="idCard">
2025-03-23 09:23:38 +08:00
<el-input
v-model="form.idCard"
placeholder="请输入证件号码"
2025-03-23 09:23:38 +08:00
></el-input>
</el-form-item>
<el-form-item v-if="isMainlandChinaUser" label="手机号:" prop="phone">
2025-03-23 09:23:38 +08:00
<el-input
v-model="form.phone"
placeholder="银行卡预留手机号"
2025-03-23 09:23:38 +08:00
></el-input>
</el-form-item>
2025-03-23 09:23:38 +08:00
<el-form-item
v-if="isMainlandChinaUser && needsVerificationCode"
label="验证码:"
2025-03-23 09:23:38 +08:00
prop="verificationCode"
>
<el-input
v-model="form.verificationCode"
placeholder="请输入验证码"
2025-03-23 09:23:38 +08:00
></el-input>
<el-button
style="margin-left: 10px"
:class="{ 'is-disabled': isSendingCode }"
:disabled="isSendingCode"
type="primary"
@click="sendVerificationCode"
>
{{ verificationCodeButtonText }}
</el-button>
2025-03-23 09:23:38 +08:00
</el-form-item>
</el-form>
</div>
<div class="dialog-footer">
<!-- <div class="btn cancel-btn" @click="handleCloseDialog">取消</div> -->
<el-button @click="handleCloseDialog">取消</el-button>
<el-button type="primary" @click="submitBankForm('bankForm')"
>确定
</el-button>
2025-03-23 09:23:38 +08:00
</div>
</el-dialog>
</template>
<script>
import * as walletApi from "@/api/wallet.js";
2025-03-23 09:23:38 +08:00
import { mapGetters } from "vuex";
export default {
data() {
// 自定义验证码校验逻辑
const validateVerificationCode = (rule, value, callback) => {
if (this.isMainlandChinaUser && this.needsVerificationCode && !value) {
callback(new Error("请输入验证码"));
} else {
callback();
}
};
// 自定义手机号校验逻辑
const validatePhoneNumber = (rule, value, callback) => {
if (this.isMainlandChinaUser && !value) {
callback(new Error("请输入正确的手机号"));
} else if (this.isMainlandChinaUser && value && value.length < 11) {
callback(new Error("请输入正确的手机号"));
} else {
callback();
}
};
2025-03-23 09:23:38 +08:00
return {
cardTypeList: [], // 证件类型列表
dialogVisible: false, // 控制对话框显示
2025-03-23 09:23:38 +08:00
form: {
cardNumber: "", // 银行卡号
accountName: "", // 姓名
idCard: "", // 证件号码
phone: "", // 手机号
bankId: "", // 银行ID (原 pkBank)
verificationCode: "", // 验证码
subBankName: "", // 开户支行
idType: "", // 证件类型
2025-03-23 09:23:38 +08:00
},
isSendingCode: false, // 是否正在发送验证码
countdownSeconds: 60, // 倒计时秒数
verificationCodeButtonText: "获取验证码", // 验证码按钮文字
countdownTimer: null, // 倒计时定时器
2025-03-23 09:23:38 +08:00
rules: {
cardNumber: [
{ required: true, message: "请输入银行卡号", trigger: "blur" },
2025-03-23 09:23:38 +08:00
],
verificationCode: [
{ required: true, message: "请输入验证码", trigger: "blur" },
{ validator: validateVerificationCode, trigger: "blur" },
2025-03-23 09:23:38 +08:00
],
idType: [
{ required: true, message: "请选择证件类型", trigger: "change" },
2025-03-23 09:23:38 +08:00
],
accountName: [
{ required: true, message: "请输入姓名", trigger: "blur" },
2025-03-23 09:23:38 +08:00
],
subBankName: [
{ required: true, message: "请输入开户支行", trigger: "blur" },
2025-03-23 09:23:38 +08:00
],
// bankNo 验证似乎是重复的,已移除
// name 验证似乎是重复的,已移除
2025-03-23 09:23:38 +08:00
idCard: [
{ required: true, message: "请输入证件号码", trigger: "blur" },
2025-03-23 09:23:38 +08:00
],
// smsCode 似乎未使用,已移除
2025-03-23 09:23:38 +08:00
phone: [
{
required: true,
message: "请输入银行卡预留手机号",
2025-03-23 09:23:38 +08:00
trigger: "blur",
},
// { min: 11, message: '请输入正确的手机号', trigger: "blur" }, // 合并到 validator
{ validator: validatePhoneNumber, trigger: "blur" },
2025-03-23 09:23:38 +08:00
],
bankId: [
// 原 pkBank
{ required: true, message: "请选择银行卡", trigger: "change" },
2025-03-23 09:23:38 +08:00
],
},
isBankCardVerified: false, // 银行卡是否已验证通过 (原 ifpass)
bankCardChioceList: [], // 可选银行列表
// pkCountry: "", // 由 isMainlandChinaUser 计算属性替代
needsVerificationCode: true, // 是否需要验证码 (原 cancode)
2025-03-23 09:23:38 +08:00
};
},
props: {
// isAdd 更名为 visible更符合对话框的常用属性名
visible: {
2025-03-23 09:23:38 +08:00
type: Boolean,
default: false,
},
},
computed: {
...mapGetters(["userInfo"]),
// 计算用户是否为中国大陆用户
isMainlandChinaUser() {
return this.userInfo?.pkCountry === 1;
},
2025-03-23 09:23:38 +08:00
},
watch: {
// 监听 prop 变化来控制对话框显示
visible(newVal) {
this.dialogVisible = newVal;
if (newVal) {
// 对话框打开时重置状态(如果需要)
this.resetFormState();
}
},
// 监听对话框内部状态变化,并通知父组件
dialogVisible(newVal) {
if (!newVal) {
this.$emit("update:visible", false); // 使用 .sync 修饰符更新父组件状态
this.$emit("closeBind", this.isBankCardVerified ? 1 : 0); // 保留原有逻辑,根据验证状态发送不同值
this.resetFormAndValidation(); // 关闭时重置表单和状态
}
2025-03-23 09:23:38 +08:00
},
},
created() {
this.fetchBankCardChoices();
this.checkVerificationRequirement();
// this.pkCountry = this.userInfo.pkCountry; // 不再需要单独存储,使用计算属性
},
beforeDestroy() {
// 组件销毁前清除定时器
if (this.countdownTimer) {
clearInterval(this.countdownTimer);
}
2025-03-23 09:23:38 +08:00
},
methods: {
// 检查是否需要验证码
checkVerificationRequirement() {
walletApi
.checkIfWhite()
.then((res) => {
// 后端接口 flag 为 'Y' 表示在白名单,不需要验证码
this.needsVerificationCode = !(res.code == 200 && res.flag === "Y");
})
.catch((_err) => {
// 处理获取白名单状态失败的情况,可以设置默认值或提示用户
console.error("获取白名单状态失败:", _err);
this.needsVerificationCode = true; // 默认为需要验证码
});
2025-03-23 09:23:38 +08:00
},
// 获取银行卡选项和证件类型
fetchBankCardChoices() {
walletApi
.getBankCardChoiceList()
.then((res) => {
this.bankCardChioceList = res.data || [];
})
.catch((_err) => {
console.error("获取银行列表失败:", _err);
});
walletApi
.getCardType()
.then((res) => {
this.cardTypeList = res.data || [];
})
.catch((_err) => {
console.error("获取证件类型失败:", _err);
});
2025-03-23 09:23:38 +08:00
},
// 关闭对话框处理
handleCloseDialog() {
this.dialogVisible = false;
// 关闭时已在 watch 中处理了 resetFields 和 emit 事件
2025-03-23 09:23:38 +08:00
},
// 重置表单和相关状态
resetFormAndValidation() {
this.$refs.bankForm?.resetFields();
this.isBankCardVerified = false;
this.clearCountdown(); // 清除倒计时状态
},
// 清除倒计时状态
clearCountdown() {
if (this.countdownTimer) {
clearInterval(this.countdownTimer);
this.countdownTimer = null;
2025-03-23 09:23:38 +08:00
}
this.isSendingCode = false;
this.verificationCodeButtonText = "获取验证码";
this.countdownSeconds = 60;
},
// 重置表单外的状态(如果需要)
resetFormState() {
this.isBankCardVerified = false;
this.clearCountdown();
},
// 实际执行绑卡操作
performBindBank() {
// 对于非中国大陆用户,或不需要验证码的情况,直接标记为已验证
if (!this.isMainlandChinaUser || !this.needsVerificationCode) {
this.isBankCardVerified = true;
}
if (!this.isBankCardVerified) {
2025-03-23 09:23:38 +08:00
this.$message({
message: "请先完成银行卡验证", // 或 '银行卡验证不一致'
2025-03-23 09:23:38 +08:00
type: "warning",
});
return;
}
// let that = this; // 不再需要
walletApi
.bindWalletBankAdd(this.form)
.then(() => {
this.$message({
message: "银行卡绑定成功",
type: "success",
});
// 成功后关闭对话框,并触发成功回调
setTimeout(() => {
// this.resetFormAndValidation(); // 已移至 watch 中处理关闭
// this.$emit("closeBind", 1); // 已移至 watch 中处理关闭
this.dialogVisible = false; // 关闭对话框
}, 1500);
})
.catch((_err) => {
// 添加错误处理
console.error("绑定银行卡失败:", _err);
this.$message({
message: "银行卡绑定失败,请稍后重试",
type: "error",
});
2025-03-23 09:23:38 +08:00
});
},
// 提交表单(验证 + 绑卡)
submitBankForm(formName) {
this.$refs[formName].validate((valid) => {
2025-03-23 09:23:38 +08:00
if (valid) {
// 如果是中国大陆用户且需要验证码
if (this.isMainlandChinaUser && this.needsVerificationCode) {
walletApi
.verifyBankCard(this.form)
.then(() => {
// 验证成功,标记状态并执行绑卡
this.isBankCardVerified = true;
this.performBindBank();
})
.catch((_err) => {
// 使用 _err 表示未使用
// 验证失败
this.isBankCardVerified = false;
// 提示后端返回的错误信息,或通用错误信息
this.$message({
message: _err?.msg || "银行卡验证失败", // 优先使用后端消息
type: "warning",
2025-03-23 09:23:38 +08:00
});
// this.countdownSeconds = 0; // 不应在此重置倒计时,应在 clearCountdown 中处理
this.clearCountdown(); // 验证失败时也应该能重新获取验证码
});
2025-03-23 09:23:38 +08:00
} else {
// 非中国大陆用户或不需要验证码,直接尝试绑卡
// isBankCardVerified 在 performBindBank 开始时会设置为 true
this.performBindBank();
2025-03-23 09:23:38 +08:00
}
} else {
console.log("表单验证失败!");
2025-03-23 09:23:38 +08:00
return false;
}
});
},
// 发送验证码并启动倒计时
sendVerificationCode() {
2025-03-23 09:23:38 +08:00
if (!this.form.phone) {
this.$message({
message: "请先输入手机号",
2025-03-23 09:23:38 +08:00
type: "warning",
});
return;
}
if (this.isSendingCode) return; // 防止重复点击
this.isSendingCode = true;
this.verificationCodeButtonText = `${this.countdownSeconds}s后重新发送`;
this.countdownTimer = setInterval(() => {
this.countdownSeconds--;
if (this.countdownSeconds <= 0) {
this.clearCountdown(); // 使用统一的清理函数
} else {
this.verificationCodeButtonText = `${this.countdownSeconds}s后重新发送`;
}
}, 1000);
// 调用发送验证码接口
walletApi
.getVerification({ phone: this.form.phone })
.then(() => {
2025-03-23 09:23:38 +08:00
this.$message({
message: "验证码已发送",
2025-03-23 09:23:38 +08:00
type: "success",
});
})
.catch((_err) => {
// 添加错误处理
console.error("发送验证码失败:", _err);
// this.$message({
// message: "验证码发送失败,请稍后重试",
// type: "error",
// });
this.clearCountdown(); // 发送失败时重置按钮状态
2025-03-23 09:23:38 +08:00
});
},
},
};
</script>
<style lang="scss" scoped>
// 统一管理表单容器样式
.bind-bank-form-container {
padding: 20px 30px; // 调整内边距
// 使用深度选择器影响 Element UI 内部元素
2025-03-23 09:23:38 +08:00
::v-deep .el-form-item {
margin-bottom: 22px; // 统一表单项间距
padding-bottom: 8px; // 增加底部内边距
display: flex; // 确保 label 和 content 在一行
align-items: center; // 垂直居中对齐
.el-form-item__label {
padding-right: 12px; // 标签右侧留空
color: #333; // 标签颜色
}
.el-form-item__content {
flex: 1; // 内容区域占据剩余空间
margin-left: 0 !important; // 覆盖 Element UI 默认的 margin
display: flex; // 使 input 和按钮等可以在一行显示
align-items: center; // 垂直居中对齐内部元素
}
.el-input,
.el-select {
flex: 1; // 输入框/选择框占据主要空间
width: auto; // 覆盖固定宽度
}
// .el-input__inner, .el-select .el-input__inner {
// border: none; // 移除内部输入框边框
// padding-left: 0; // 移除左侧内边距
// &:focus {
// box-shadow: none; // 移除聚焦时的阴影
// }
// }
// // 移除选择框外层边框(如果需要完全无边框)
// .el-select .el-input.is-focus .el-input__inner {
// border: none !important;
// }
// .el-select .el-input__inner:focus {
// border-color: transparent !important; // Element UI 可能有特定样式,尝试覆盖
// }
}
// 最后一个表单项移除下边框
::v-deep .el-form-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
2025-03-23 09:23:38 +08:00
}
}
// 验证码按钮样式
.verification-code-button {
background-color: #d5251d; // 主题色
color: #ffffff;
border-radius: 4px; // 圆角调整
padding: 6px 12px; // 内边距调整
font-size: 12px; // 字体大小调整
2025-03-23 09:23:38 +08:00
cursor: pointer;
white-space: nowrap; // 防止文字换行
margin-left: 10px; // 与输入框的间距
transition: background-color 0.3s, opacity 0.3s; // 过渡效果
&:hover {
background-color: #c02018; // Hover 颜色加深
2025-03-23 09:23:38 +08:00
}
// 禁用状态样式
&.is-disabled {
opacity: 0.6;
cursor: not-allowed;
background-color: #fab6b6; // 禁用时颜色变浅
&:hover {
background-color: #fab6b6; // 禁用时 Hover 颜色不变
}
2025-03-23 09:23:38 +08:00
}
}
// 对话框底部按钮区域样式
.dialog-footer {
2025-03-23 09:23:38 +08:00
display: flex;
justify-content: space-around; // 按钮均匀分布
padding: 20px 30px 30px; // 底部区域内边距
border-top: 1px solid #f0f0f0; // 添加顶部边框分隔
2025-03-23 09:23:38 +08:00
.btn {
width: 160px; // 按钮宽度调整
height: 45px; // 按钮高度调整
border-radius: 6px;
display: flex; // 使用 flex 居中文字
align-items: center;
justify-content: center;
2025-03-23 09:23:38 +08:00
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s; // 添加过渡效果
2025-03-23 09:23:38 +08:00
}
.confirm-btn {
background-color: #d5251d; // 主题色
color: #ffffff;
&:hover {
background-color: #c02018;
}
}
.cancel-btn {
background-color: #f5f5f5; // 取消按钮背景色
color: #666; // 取消按钮文字颜色
border: 1px solid #e0e0e0; // 添加边框
&:hover {
background-color: #eeeeee;
}
2025-03-23 09:23:38 +08:00
}
}
// 对话框标题加粗Element UI 默认可能已处理)
2025-03-23 09:23:38 +08:00
::v-deep .el-dialog__title {
font-weight: 500;
}
</style>