对接U存款通道

This commit is contained in:
Songsl 2026-06-14 16:43:02 +08:00
parent 6b49a037e0
commit 02c0e84c07
3 changed files with 227 additions and 6 deletions

View File

@ -47,3 +47,20 @@ export const createBuyEx = (data: {
}) => {
return request.post('/delphiPay/createBuyEx', data, { showMessage: false })
}
// ERC支付
export const ERCPay = (data: {
channel_id: number
amount: string
}) => {
return request.post('/upay/ERCPay', data, { showMessage: false })
}
// TRC支付
export const TRCPay = (data: {
channel_id: number
amount: string
}) => {
return request.post('/upay/TRCPay', data, { showMessage: false })
}

View File

@ -17,8 +17,8 @@
<!-- 存款弹窗 -->
<RechangeDialog class="rechange-pay-dialog" :visible="dialogVisible" title="存款" width="600px" :show-footer="false"
@cancel="handleCancel" @close="handleClose">
<!-- type === 8 时显示新样式选择金额和支付方式 -->
<template v-if="rechangeForm.type === 8">
<!-- channelCode === 'DELPHIPAY' 时显示新样式选择金额和支付方式 -->
<template v-if="rechangeForm.channelCode === 'DELPHIPAY'">
<!-- 步骤1选择金额和支付方式 -->
<template v-if="payStep === 1">
<!-- 渠道信息 -->
@ -114,7 +114,56 @@
</template>
</template>
<!-- type !== 8 时显示老样式银行卡信息 + 上传凭证 -->
<template v-else-if="rechangeForm.channelCode === 'TRC' || rechangeForm.channelCode === 'ERC'">
<div class="crypto-section">
<div class="crypto-left">
<div class="crypto-title">请向以下地址转账</div>
<div class="qrcode-box">
<img :src="cryptoQrcode" alt="请生成钱包地址二维码" class="crypto-qrcode" />
<!-- <div v-else class="qrcode-loading">
<el-icon class="is-loading" size="48">
<Loading />
</el-icon>
</div> -->
</div>
<div class="crypto-tip">扫描二维码获取钱包地址</div>
</div>
<div class="crypto-right">
<div class="wallet-title">钱包地址</div>
<div class="wallet-address-row">
<el-input v-model="rechangeForm.address" readonly size="large" class="wallet-address-input">
<template #append>
<el-button @click="handleCopyAddress" class="copy-btn">
<el-icon><DocumentCopy /></el-icon>
<span>复制</span>
</el-button>
</template>
</el-input>
</div>
<div class="wallet-type">
<span class="type-label">钱包类型:</span>
<span class="type-value">{{ rechangeForm.channelCode === 'TRC' ? 'USDT-TRC20' : 'USDT-ERC20' }}</span>
</div>
</div>
</div>
<!-- 充值金额 -->
<el-form-item label="充值金额" size="large" prop="depositAmount">
<el-input v-model="rechangeForm.depositAmount" placeholder="请输入充值金额生成二维码和钱包地址" size="large" @input="handleAmountInput">
<template #suffix>
<span class="currency-suffix">USDT</span>
</template>
</el-input>
</el-form-item>
<!-- 按钮区 -->
<div class="dialog-actions">
<el-button type="primary" size="large" :loading="submitLoading" @click="handleGenerateQrcode">
生成二维码
</el-button>
</div>
</template>
<!-- 时显示老样式银行卡信息 + 上传凭证 -->
<template v-else>
<el-form ref="rechangeFormRef" :model="rechangeForm" :rules="rechangeFormRules" label-position="top">
<!-- 渠道信息 -->
@ -204,7 +253,7 @@
</template>
<script setup lang="ts">
import { Check, Loading, Plus } from '@element-plus/icons-vue'
import { Check, Loading, Plus, DocumentCopy } from '@element-plus/icons-vue'
defineOptions({
name: 'Rechange'
@ -217,7 +266,7 @@ import RechangeTable from '@/components/common/Table.vue'
import RechangeDialog from '@/components/common/Dialog.vue'
import { usePageLoading } from '@/composables/useLoading'
import { rechangeTableColumns } from './options'
import { getRechargeChannelList, getApplyCustomerRef, getKycDetail, createBuyEx, rechargeApi } from '@/api/modules/capital/rechangeChanl'
import { getRechargeChannelList, getApplyCustomerRef, getKycDetail, createBuyEx, rechargeApi, ERCPay, TRCPay } from '@/api/modules/capital/rechangeChanl'
import { uploadImageApi } from '@/api/modules/upload'
import QRCode from 'qrcode'
import alipayIcon from '@/assets/images/zfb.png'
@ -285,6 +334,9 @@ const qrcodeImageUrls = ref<string[]>([])
//
const paymentUrls = ref<string[]>([])
//
const cryptoQrcode = ref('')
//
const generateQRCode = async (url: string) => {
await nextTick()
@ -431,6 +483,9 @@ const handleOpenDialog = async (row: any) => {
beneficiaryBankAccount: row.beneficiary_bank_account || '',
address: (row as any).address || '',
}
if (rechangeForm.value.channelCode === 'TRC' || rechangeForm.value.channelCode === 'ERC') {
rechangeForm.value.address = ''
}
//
payStep.value = 1
selectedMethod.value = ''
@ -441,6 +496,33 @@ const handleOpenDialog = async (row: any) => {
rechangeFormRef.value?.clearValidate()
}
//
const generateCryptoQrcode = async (address: string) => {
if (!address) return
try {
const dataUrl = await QRCode.toDataURL(address, {
width: 200,
margin: 4,
})
cryptoQrcode.value = dataUrl
} catch (error) {
console.error('生成二维码失败:', error)
}
}
//
const handleCopyAddress = async () => {
const address = rechangeForm.value.address
if (!address) return
try {
await navigator.clipboard.writeText(address)
ElMessage.success('复制成功')
} catch (error) {
console.error('复制失败:', error)
ElMessage.error('复制失败')
}
}
//
const handleQuickAmount = (amount: number) => {
rechangeForm.value.depositAmount = amount.toString()
@ -545,6 +627,20 @@ const handleUpload = async (options: any) => {
}
}
//
const handleGenerateQrcode = async () => {
let pay = ERCPay
if(rechangeForm.value.channelCode === 'TRC'){
pay = TRCPay
}
const res: any = await pay({
channel_id: rechangeForm.value.id,
amount: rechangeForm.value.depositAmount,
})
rechangeForm.value.address = res.wallet_url || ''
await generateCryptoQrcode(res.wallet_url || '')
}
// type !== 8 使
const handleOldSubmit = async () => {
try {
@ -577,6 +673,7 @@ const handleCancel = () => {
payStep.value = 1
selectedMethod.value = ''
voucherUrl.value = ''
cryptoQrcode.value = ''
}
//
@ -943,6 +1040,113 @@ onMounted(() => {
}
}
//
.crypto-section {
display: flex;
gap: 32px;
background: #f5f7fa;
border-radius: 12px;
padding: 24px;
margin-bottom: 20px;
.crypto-left {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
.crypto-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.qrcode-box {
width: 200px;
height: 200px;
background: #fff;
border-radius: 12px;
padding: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
justify-content: center;
}
.crypto-qrcode {
width: 100%;
height: 100%;
object-fit: contain;
}
.qrcode-loading {
color: #409eff;
}
.crypto-tip {
margin-top: 12px;
font-size: 13px;
color: #909399;
}
}
.crypto-right {
flex: 1;
display: flex;
flex-direction: column;
.wallet-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 16px;
}
.wallet-address-row {
margin-bottom: 16px;
.wallet-address-input {
:deep(.el-input__wrapper) {
border-radius: 8px 0 0 8px;
}
:deep(.el-input__inner) {
font-family: monospace;
font-size: 14px;
}
}
.copy-btn {
padding: 0 16px;
border-left: none;
border-radius: 0 8px 8px 0;
}
}
.wallet-type {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: #fff;
border-radius: 8px;
border-left: 3px solid #409eff;
.type-label {
font-size: 14px;
color: #909399;
}
.type-value {
font-size: 14px;
color: #409eff;
font-weight: 500;
}
}
}
}
//
.voucher-uploader {
width: 100%;

View File

@ -13,7 +13,7 @@ export const rules = (menuForm: any, iconOptions: any[]): FormRules => {
// 3. 路由路径:必填 + 格式校验(以/开头,仅包含字母/数字/下划线/斜杠)
path: [
{ required: true, message: '请输入前端路由路径', trigger: 'blur' },
{ pattern: /^\/[a-zA-Z0-9_\/]+$/, message: '前端路由路径必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' }
{ pattern: /^\/[a-zA-Z0-9_/]+$/, message: '前端路由路径必须以/开头,仅包含字母、数字、下划线和斜杠', trigger: 'blur' }
],
// 4. 后端路由:可选,但填了就必须是指定范围(匹配你提供的后端路由列表)
api: [