|
|
@@ -7,135 +7,147 @@
|
|
|
</template>
|
|
|
|
|
|
<view class="page-body">
|
|
|
- <scroll-view scroll-y class="main-scroll">
|
|
|
- <view class="top-up-page-container">
|
|
|
- <view class="recharge-container">
|
|
|
- <view class="qr-code-section">
|
|
|
- <view class="qr-code-box">
|
|
|
- <view class="qr-code-bg">
|
|
|
- <u-image
|
|
|
- v-if="qrCodeUrl"
|
|
|
- :src="qrCodeUrl"
|
|
|
- width="180rpx"
|
|
|
- height="180rpx"
|
|
|
- mode="aspectFit"
|
|
|
- class="qr-code-img"
|
|
|
- ref="qrCodeImage"
|
|
|
- ></u-image>
|
|
|
- <view v-else class="empty-qr-code">
|
|
|
- <u-icon name="photo" size="48rpx" color="#9ca3af"></u-icon>
|
|
|
- <text class="empty-text">{{ $t('暂无二维码') }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
+ <scroll-view scroll-y class="main-scroll" style="height: 100%;">
|
|
|
+
|
|
|
+ <template v-if="!payResult">
|
|
|
+ <view class="section">
|
|
|
+ <view class="section-title">{{ $t('选择支付方式') }}</view>
|
|
|
+ <view class="channel-list">
|
|
|
+ <view
|
|
|
+ v-for="(item, index) in channelList"
|
|
|
+ :key="index"
|
|
|
+ class="channel-item"
|
|
|
+ :class="{ active: currentChannel?.value === item.value }"
|
|
|
+ @click="selectChannel(item)"
|
|
|
+ >
|
|
|
+ {{ item.label }}
|
|
|
</view>
|
|
|
</view>
|
|
|
+ </view>
|
|
|
|
|
|
- <view class="selection-section">
|
|
|
- <text class="section-title">{{ $t('选择币种') }}</text>
|
|
|
- <view class="currency-options">
|
|
|
- <view
|
|
|
- v-for="currency in allCurrencies"
|
|
|
- :key="currency.value"
|
|
|
- class="currency-card"
|
|
|
- :class="{ 'selected': selectedCurrency === currency.value }"
|
|
|
- @click="() => {
|
|
|
- selectedCurrency = currency.value
|
|
|
- getBlockChainFee()
|
|
|
- }"
|
|
|
- >
|
|
|
- <view class="currency-info">
|
|
|
- <text class="currency-name">{{ currency.label }}</text>
|
|
|
- </view>
|
|
|
- <u-icon
|
|
|
- v-if="selectedCurrency === currency.value"
|
|
|
- name="checkmark"
|
|
|
- class="check-icon"
|
|
|
- color="#f8b932"
|
|
|
- size="24rpx"
|
|
|
- ></u-icon>
|
|
|
- </view>
|
|
|
+ <view class="section" v-if="currentChannel && currentChannel.isUsdt">
|
|
|
+ <template v-if="receivingType !== '1'">
|
|
|
+ <view class="section-title">{{ $t('充值金额') }}</view>
|
|
|
+
|
|
|
+ <view class="amount-input-box" style="margin-bottom: 20rpx;">
|
|
|
+ <text class="currency">$</text>
|
|
|
+ <input
|
|
|
+ type="digit"
|
|
|
+ v-model="amount"
|
|
|
+ class="amount-input"
|
|
|
+ :placeholder="$t('请输入充值数量')"
|
|
|
+ />
|
|
|
</view>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <view class="rate-tip" v-if="usdtRate">
|
|
|
+ {{ $t('当前汇率 (USDT/RMB)') }}: {{ usdtRate }}
|
|
|
</view>
|
|
|
|
|
|
- <view class="selection-section">
|
|
|
- <text class="section-title">{{ $t('选择区块链网络') }}</text>
|
|
|
- <view class="network-options">
|
|
|
+ <view class="network-section">
|
|
|
+ <view class="network-title">{{ $t('网络类型') }}</view>
|
|
|
+ <view class="network-list">
|
|
|
+ <view
|
|
|
+ v-if="trc20Address"
|
|
|
+ class="network-item"
|
|
|
+ :class="{ active: selectedNetwork === 'trc20' }"
|
|
|
+ @click="selectNetwork('trc20')"
|
|
|
+ >
|
|
|
+ TRC20
|
|
|
+ </view>
|
|
|
<view
|
|
|
- v-for="(network, n) in networkOptions"
|
|
|
- :key="network.value"
|
|
|
- class="network-card"
|
|
|
- :class="{ 'selected': selectedNetwork === network.value }"
|
|
|
- @click="() => {
|
|
|
- selectedNetwork = network.value
|
|
|
- rechargeAddress = addressOptions[n]
|
|
|
- }"
|
|
|
+ v-if="erc20Address"
|
|
|
+ class="network-item"
|
|
|
+ :class="{ active: selectedNetwork === 'erc20' }"
|
|
|
+ @click="selectNetwork('erc20')"
|
|
|
>
|
|
|
- <text class="network-name">{{ network.label }}</text>
|
|
|
- <u-icon
|
|
|
- v-if="selectedNetwork === network.value"
|
|
|
- name="checkmark"
|
|
|
- class="check-icon"
|
|
|
- color="#f8b932"
|
|
|
- size="20rpx"
|
|
|
- ></u-icon>
|
|
|
+ ERC20
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <view class="address-section">
|
|
|
- <text class="section-title">{{ $t('充值地址') }}</text>
|
|
|
- <view class="address-container">
|
|
|
- <u-input
|
|
|
- v-model="rechargeAddress"
|
|
|
- :placeholder="$t('充值地址')"
|
|
|
- disabled
|
|
|
- class="address-input input-border"
|
|
|
- ></u-input>
|
|
|
+ <template v-if="usdtQrcode">
|
|
|
+ <view class="qrcode-box">
|
|
|
+ <image :src="usdtQrcode" mode="aspectFit" class="qrcode-img"></image>
|
|
|
</view>
|
|
|
- <text class="address-tip">{{ $t('请确认充值地址正确无误,错误地址将导致资产丢失且无法找回') }}</text>
|
|
|
+ <view class="address-box" v-if="usdtAddress">
|
|
|
+ <text class="address-text">{{ usdtAddress }}</text>
|
|
|
+ <view class="copy-btn" @click="copyAddress">{{ $t('复制') }}</view>
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <view class="network-section" style="margin-top: 40rpx;" v-if="receivingType !== '1'">
|
|
|
+ <view class="network-title">{{ $t('上传支付凭证') }}</view>
|
|
|
+ <CUploadImage ref="CUploadImageRef" @change="changeImg" />
|
|
|
</view>
|
|
|
+ </view>
|
|
|
|
|
|
- <view class="amount-section">
|
|
|
- <text class="section-title">{{ $t('充值数量') }}</text>
|
|
|
- <u-input
|
|
|
- v-model="amount"
|
|
|
- :placeholder="$t('请输入充值数量')"
|
|
|
- class="amount-input input-border"
|
|
|
- type="digit"
|
|
|
- @blur="amountTouched = true"
|
|
|
- ></u-input>
|
|
|
- <text class="error-tip" v-if="!amount && amountTouched">{{ $t('请输入充值数量') }}</text>
|
|
|
+ <view class="section" v-if="currentChannel && !currentChannel.isUsdt">
|
|
|
+ <view class="section-title">{{ $t('充值金额') }}</view>
|
|
|
+
|
|
|
+ <view v-if="currentChannel.config.fixed" class="amount-list">
|
|
|
+ <view
|
|
|
+ v-for="(val, index) in currentChannel.config.fixed"
|
|
|
+ :key="index"
|
|
|
+ class="amount-item"
|
|
|
+ :class="{ active: amount === val }"
|
|
|
+ @click="selectFixedAmount(val)"
|
|
|
+ >
|
|
|
+ ¥{{ val }}
|
|
|
+ </view>
|
|
|
</view>
|
|
|
|
|
|
- <view class="estimated-section">
|
|
|
- <text class="section-title">{{ $t('预计到账') }}</text>
|
|
|
- <u-input
|
|
|
- v-model="estimatedAmount"
|
|
|
- disabled
|
|
|
- class="estimated-input input-border"
|
|
|
- ></u-input>
|
|
|
- <text class="exchange-rate">{{ $t('当前汇率') }}: 1 {{ selectedCurrency }} = {{ (1 / exchangeRate).toFixed(2) }} {{ $currency }}</text>
|
|
|
+ <view v-else class="amount-input-box">
|
|
|
+ <text class="currency">¥</text>
|
|
|
+ <input
|
|
|
+ type="digit"
|
|
|
+ v-model="amount"
|
|
|
+ class="amount-input"
|
|
|
+ :placeholder="$t('限额') + ` ${currentChannel.config.min} - ${currentChannel.config.max}`"
|
|
|
+ />
|
|
|
</view>
|
|
|
|
|
|
- <view class="upload-section">
|
|
|
- <text class="section-title">{{ $t('上传支付凭证') }}</text>
|
|
|
- <view class="upload-container">
|
|
|
- <CUploadImage
|
|
|
- @change="changeImg"
|
|
|
- :maxCount="1"
|
|
|
- :imgCenter="false"
|
|
|
- ref="CUploadImageRef"
|
|
|
- />
|
|
|
- <text v-if="uploadFileUrl" class="upload-tip">{{ $t('已上传支付凭证') }}</text>
|
|
|
+ <view class="network-section" v-if="currentChannel.value === 'rgcz'">
|
|
|
+ <view class="network-title">{{ $t('支付方式') }}</view>
|
|
|
+ <view class="network-list">
|
|
|
+ <view
|
|
|
+ v-for="method in rgczMethods"
|
|
|
+ :key="method.value"
|
|
|
+ class="network-item"
|
|
|
+ :class="{ active: selectedRgczMethod === method.value }"
|
|
|
+ @click="selectedRgczMethod = method.value"
|
|
|
+ >
|
|
|
+ {{ method.label }}
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+
|
|
|
+ </view>
|
|
|
+ </template>
|
|
|
|
|
|
- <view class="bottom-placeholder"></view>
|
|
|
+ <view class="section result-section" v-if="payResult">
|
|
|
+ <view class="qrcode-box" style="margin-top: 0; margin-bottom: 40rpx;">
|
|
|
+ <image :src="payResult.image" mode="aspectFit" class="qrcode-img"></image>
|
|
|
+ </view>
|
|
|
+ <view class="result-text-box">
|
|
|
+ <view class="result-line">{{ currentChannel.label }}扫码充值确认</view>
|
|
|
+ <view class="result-line">请使用浏览器扫码或者复制支付地址到浏览器打开</view>
|
|
|
+ <view class="result-line" style="display: flex; align-items: center; justify-content: space-between;">
|
|
|
+ <view style="flex: 1;">
|
|
|
+ 支付地址:<text class="link-text" @click="openPayUrl(payResult.url)">{{ payResult.url }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="copy-btn" style="margin-left: 20rpx; flex-shrink: 0;" @click="copyPayUrl(payResult.url)">{{ $t('复制') }}</view>
|
|
|
+ </view>
|
|
|
+ <view class="result-line">支付金额:{{ amount }} RMB</view>
|
|
|
+ <view class="result-line">请按实际支付金额进行付款,否则影响到账</view>
|
|
|
+ <view class="result-line">支付完成后请耐心等待,支付到账会第一时间通知您!</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
+
|
|
|
+ <view class="bottom-placeholder"></view>
|
|
|
</scroll-view>
|
|
|
|
|
|
- <view class="fixed-submit-section">
|
|
|
+ <view class="fixed-submit-section" v-if="!payResult && !(currentChannel?.isUsdt && receivingType === '1')">
|
|
|
<u-button
|
|
|
type="primary"
|
|
|
@click="submitRecharge"
|
|
|
@@ -151,127 +163,297 @@
|
|
|
import { ref, watch, reactive } from "vue";
|
|
|
import { useI18n } from "vue-i18n";
|
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|
|
-import { getExchangeRate, getRechargeAddressAndWithdrawCurrencyList, recharge } from "@/request/api";
|
|
|
+import { recharge, getConfigIndex } from "@/request/api";
|
|
|
import CUploadImage from "@/components/CUploadImage/index.vue";
|
|
|
+import { apiUrl } from "@/request";
|
|
|
|
|
|
-const { t } = useI18n()
|
|
|
+const { t, locale } = useI18n()
|
|
|
const router = useRouter()
|
|
|
const CUploadImageRef = ref()
|
|
|
|
|
|
-const allCurrencies = ref([]);
|
|
|
-const selectedCurrency = ref('USDT');
|
|
|
-const networkOptions = ref([]);
|
|
|
-const addressOptions = ref([]);
|
|
|
+const channelList = ref([]);
|
|
|
+const currentChannel = ref(null);
|
|
|
+const amount = ref('');
|
|
|
+const uploadFileUrl = ref(''); // 补充定义了图片上传变量
|
|
|
+const payResult = ref(null); // 保存第三方充值结果
|
|
|
+
|
|
|
+const usdtRate = ref('');
|
|
|
+const trc20Address = ref('');
|
|
|
+const erc20Address = ref('');
|
|
|
+const showThirdPayment = ref('0');
|
|
|
const selectedNetwork = ref('');
|
|
|
-const rechargeAddress = ref('');
|
|
|
-const amount = ref('');
|
|
|
-const amountTouched = ref(false);
|
|
|
-const estimatedAmount = ref('0');
|
|
|
-const qrCodeUrl = ref('');
|
|
|
-const exchangeRate = ref(0);
|
|
|
-const uploadFileUrl = ref('');
|
|
|
+const usdtQrcode = ref('');
|
|
|
+const usdtAddress = ref(''); // 新增:保存充值地址
|
|
|
+const receivingType = ref(''); // 新增:保存接收类型
|
|
|
+
|
|
|
+const rgczMethods = [
|
|
|
+ { label: '微信', value: 1 },
|
|
|
+ { label: '支付宝', value: 2 },
|
|
|
+ { label: '银行卡', value: 3 }
|
|
|
+];
|
|
|
+const selectedRgczMethod = ref('');
|
|
|
|
|
|
const changeImg = (e) => {
|
|
|
uploadFileUrl.value = Array.isArray(e) ? e[0] : e;
|
|
|
};
|
|
|
|
|
|
-const updateCurrencyInfo = (currencyValue) => {
|
|
|
- const currency = allCurrencies.value.find(item => item.value === currencyValue);
|
|
|
- if (currency) {
|
|
|
- qrCodeUrl.value = currency.qrCodeUrl;
|
|
|
- networkOptions.value = currency.networks;
|
|
|
- addressOptions.value = currency.address;
|
|
|
- selectedNetwork.value = currency.networks.length > 0 ? currency.networks[0].value : '';
|
|
|
- rechargeAddress.value = currency.address[0] || '';
|
|
|
+const selectChannel = (item) => {
|
|
|
+ currentChannel.value = item;
|
|
|
+ amount.value = '';
|
|
|
+ selectedNetwork.value = '';
|
|
|
+ usdtQrcode.value = '';
|
|
|
+ usdtAddress.value = ''; // 切换通道时清空地址
|
|
|
+ uploadFileUrl.value = ''; // 切换通道时清空凭证
|
|
|
+ payResult.value = null; // 切换通道时清空结果
|
|
|
+ selectedRgczMethod.value = ''; // 切换通道时清空人工充值方式
|
|
|
+
|
|
|
+ // USDT默认选择网络类型
|
|
|
+ if (item.isUsdt) {
|
|
|
+ if (trc20Address.value) {
|
|
|
+ selectNetwork('trc20');
|
|
|
+ } else if (erc20Address.value) {
|
|
|
+ selectNetwork('erc20');
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-watch(selectedCurrency, (newVal) => {
|
|
|
- updateCurrencyInfo(newVal);
|
|
|
- amount.value = '';
|
|
|
- amountTouched.value = false;
|
|
|
- estimatedAmount.value = '0';
|
|
|
-});
|
|
|
+const selectFixedAmount = (val) => {
|
|
|
+ amount.value = val;
|
|
|
+};
|
|
|
|
|
|
-watch(amount, (newAmount) => {
|
|
|
- if (newAmount === '') {
|
|
|
- estimatedAmount.value = '0';
|
|
|
- return;
|
|
|
- }
|
|
|
- amountTouched.value = true;
|
|
|
- // estimatedAmount.value = (Number(newAmount) * Number(exchangeRate.value)).toFixed(6);
|
|
|
- estimatedAmount.value = (Number(newAmount) / Number(exchangeRate.value)).toFixed(2);
|
|
|
-});
|
|
|
+const selectNetwork = (network) => {
|
|
|
+ selectedNetwork.value = network;
|
|
|
+ getScanQrcode();
|
|
|
+};
|
|
|
+
|
|
|
+const copyAddress = () => {
|
|
|
+ if (!usdtAddress.value) return;
|
|
|
+ uni.setClipboardData({
|
|
|
+ data: usdtAddress.value,
|
|
|
+ success: () => {
|
|
|
+ uni.showToast({ title: t('复制成功'), icon: 'none' });
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 新增:复制第三方支付地址
|
|
|
+const copyPayUrl = (url) => {
|
|
|
+ if (!url) return;
|
|
|
+ uni.setClipboardData({
|
|
|
+ data: url,
|
|
|
+ success: () => {
|
|
|
+ uni.showToast({ title: t('复制成功'), icon: 'none' });
|
|
|
+ }
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// 新增:判断环境打开支付地址
|
|
|
+const openPayUrl = (url) => {
|
|
|
+ if (!url) return;
|
|
|
+ // #ifdef H5
|
|
|
+ window.open(url, '_blank');
|
|
|
+ // #endif
|
|
|
+ // #ifndef H5
|
|
|
+ router.push({ name: 'web-view', query: { src: url }});
|
|
|
+ // #endif
|
|
|
+};
|
|
|
|
|
|
const submitRecharge = async () => {
|
|
|
- if (!amount.value || Number(amount.value) <= 0) {
|
|
|
- amountTouched.value = true;
|
|
|
- uni.showToast({ title: t('请输入充值数量'), icon: 'none' });
|
|
|
+ if (!currentChannel.value) return uni.showToast({ title: t('请选择支付方式'), icon: 'none' });
|
|
|
+
|
|
|
+ // ================= USDT充值提交 =================
|
|
|
+ if (currentChannel.value.isUsdt) {
|
|
|
+ if (!amount.value) return uni.showToast({ title: t('请输入充值数量'), icon: 'none' });
|
|
|
+ if (!selectedNetwork.value) return uni.showToast({ title: t('请选择网络类型'), icon: 'none' });
|
|
|
+ if (!uploadFileUrl.value) return uni.showToast({ title: t('请上传支付凭证'), icon: 'none' });
|
|
|
+
|
|
|
+ uni.showLoading({ title: t('提交中...') });
|
|
|
+ uni.request({
|
|
|
+ url: apiUrl + 'wallet/recharge',
|
|
|
+ method: 'POST',
|
|
|
+ data: {
|
|
|
+ amount: amount.value,
|
|
|
+ image: uploadFileUrl.value,
|
|
|
+ net: selectedNetwork.value,
|
|
|
+ toAddress: selectedNetwork.value === 'trc20' ? trc20Address.value : erc20Address.value
|
|
|
+ },
|
|
|
+ header: {
|
|
|
+ Authorization: uni.getStorageSync('token'),
|
|
|
+ lang: locale.value
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ uni.showToast({ title:res.data.msg || t('提交成功'), icon: 'success' });
|
|
|
+ // 清空表单
|
|
|
+ amount.value = '';
|
|
|
+ selectedNetwork.value = '';
|
|
|
+ uploadFileUrl.value = '';
|
|
|
+ usdtQrcode.value = '';
|
|
|
+ usdtAddress.value = '';
|
|
|
+ CUploadImageRef.value.clear(); // 调用子组件方法清除图片
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: res.data.msg || t('提交失败'), icon: 'none' });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ uni.showToast({ title: t('网络错误'), icon: 'none' });
|
|
|
+ }
|
|
|
+ });
|
|
|
return;
|
|
|
}
|
|
|
- if (!uploadFileUrl.value) {
|
|
|
- uni.showToast({ title: t('请上传支付凭证'), icon: 'none' });
|
|
|
+
|
|
|
+ // ================= 人工充值提交 =================
|
|
|
+ if (currentChannel.value.value === 'rgcz') {
|
|
|
+ if (!amount.value) return uni.showToast({ title: t('请输入或选择充值金额'), icon: 'none' });
|
|
|
+ if (!selectedRgczMethod.value) return uni.showToast({ title: t('请选择支付方式'), icon: 'none' });
|
|
|
+
|
|
|
+ uni.showLoading({ title: t('提交中...') });
|
|
|
+ uni.request({
|
|
|
+ url: apiUrl + 'wallet/paymentOrder',
|
|
|
+ method: 'POST',
|
|
|
+ data: {
|
|
|
+ payment_type: selectedRgczMethod.value,
|
|
|
+ amount: amount.value
|
|
|
+ },
|
|
|
+ header: {
|
|
|
+ Authorization: uni.getStorageSync('token'),
|
|
|
+ lang: locale.value
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ uni.showToast({ title: res.data.msg || t('提交成功'), icon: 'success' });
|
|
|
+ amount.value = '';
|
|
|
+ selectedRgczMethod.value = '';
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: res.data.msg || t('提交失败'), icon: 'none' });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ uni.showToast({ title: t('网络错误'), icon: 'none' });
|
|
|
+ }
|
|
|
+ });
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const params = {
|
|
|
- currency: selectedCurrency.value,
|
|
|
- network_type: selectedNetwork.value,
|
|
|
- address: rechargeAddress.value,
|
|
|
- amount: amount.value,
|
|
|
- payment_receipt: uploadFileUrl.value
|
|
|
- };
|
|
|
-
|
|
|
- await recharge(params);
|
|
|
-
|
|
|
- amount.value = '';
|
|
|
- estimatedAmount.value = '0'
|
|
|
- uploadFileUrl.value = '';
|
|
|
- if (CUploadImageRef.value) CUploadImageRef.value.handleDelete(0);
|
|
|
-};
|
|
|
-
|
|
|
-const getBlockChainFee = async () => {
|
|
|
- const res = await getExchangeRate({ symbol: selectedCurrency.value });
|
|
|
- exchangeRate.value = res.data.price || 0;
|
|
|
+ // ================= 第三方充值提交 =================
|
|
|
+ if (!amount.value) return uni.showToast({ title: t('请输入或选择充值金额'), icon: 'none' });
|
|
|
+
|
|
|
+ uni.showLoading({ title: t('提交中...') });
|
|
|
+ uni.request({
|
|
|
+ url: apiUrl + 'wallet/createPay',
|
|
|
+ method: 'POST',
|
|
|
+ data: {
|
|
|
+ payment_type: currentChannel.value.config.type,
|
|
|
+ amount: amount.value
|
|
|
+ },
|
|
|
+ header: {
|
|
|
+ Authorization: uni.getStorageSync('token'),
|
|
|
+ lang: locale.value
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ if (res.data.code === 0) {
|
|
|
+ uni.showToast({ title: res.data.msg || t('提交成功'), icon: 'success' });
|
|
|
+ payResult.value = res.data.data; // 赋值展示结果页
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: res.data.msg || t('提交失败'), icon: 'none' });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ uni.showToast({ title: t('网络错误'), icon: 'none' });
|
|
|
+ }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
const getData = async () => {
|
|
|
- const res = await getRechargeAddressAndWithdrawCurrencyList({ type: 1 });
|
|
|
- const mergedData = res.data.list.reduce((acc, item) => {
|
|
|
- const existing = acc.find(entry => entry.currency === item.currency);
|
|
|
- if (existing) {
|
|
|
- existing.network_type.push(item.network_type);
|
|
|
- existing.address.push(item.address);
|
|
|
- } else {
|
|
|
- acc.push({
|
|
|
- currency: item.currency,
|
|
|
- network_type: [item.network_type],
|
|
|
- address: [item.address],
|
|
|
- qrCodeUrl: item.qrcode,
|
|
|
+ try {
|
|
|
+ const configData = await getConfigIndex();
|
|
|
+ if (configData && configData.data) {
|
|
|
+ console.log(configData, 266);
|
|
|
+
|
|
|
+ configData.data.forEach(item => {
|
|
|
+ if (item.field === 'exchange_rate_rmb') usdtRate.value = item.val;
|
|
|
+ if (item.field === 'receiving_address') trc20Address.value = item.val;
|
|
|
+ if (item.field === 'receiving_address_erc20') erc20Address.value = item.val;
|
|
|
+ if (item.field === 'three_payment_switch') showThirdPayment.value = item.val;
|
|
|
+ if (item.field === 'receiving_type') receivingType.value = item.val; // 获取隐藏配置
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ channelList.value = [];
|
|
|
+
|
|
|
+ const res = await getChannel();
|
|
|
+ if (res && res.data && res.data.list) {
|
|
|
+ // 遍历后端返回的通道,如果是 USDT 则标记 isUsdt,以兼容已有的页面和提交逻辑
|
|
|
+ channelList.value = res.data.list.map(item => {
|
|
|
+ if (item.value === 'usdt' || item.type === 'usdt' || item.isUsdt) {
|
|
|
+ item.isUsdt = true;
|
|
|
+ }
|
|
|
+ return item;
|
|
|
});
|
|
|
}
|
|
|
- return acc;
|
|
|
- }, []);
|
|
|
-
|
|
|
- allCurrencies.value = mergedData.map(item => ({
|
|
|
- label: item.currency,
|
|
|
- value: item.currency,
|
|
|
- address: item.address,
|
|
|
- qrCodeUrl: item.qrCodeUrl,
|
|
|
- networks: item.network_type.map(trim => ({
|
|
|
- label: trim,
|
|
|
- value: trim,
|
|
|
- })),
|
|
|
- }));
|
|
|
-
|
|
|
- if (allCurrencies.value.length > 0) {
|
|
|
- selectedCurrency.value = allCurrencies.value[0].value;
|
|
|
- await getBlockChainFee();
|
|
|
- updateCurrencyInfo(allCurrencies.value[0].value);
|
|
|
+
|
|
|
+ if (channelList.value.length > 0) {
|
|
|
+ selectChannel(channelList.value[0]); // 初始化调用以触发默认网络选择
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error(error);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+function getChannel() {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ uni.request({
|
|
|
+ url: apiUrl + 'wallet/channel',
|
|
|
+ method: 'GET',
|
|
|
+ success: (res) => resolve(res.data),
|
|
|
+ fail: (err) => reject(err),
|
|
|
+ header: {
|
|
|
+ Authorization: uni.getStorageSync('token'),
|
|
|
+ lang: locale.value
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function getScanQrcode() {
|
|
|
+ uni.showLoading({ title: t('获取中...') });
|
|
|
+ uni.request({
|
|
|
+ url: apiUrl + 'wallet/scan',
|
|
|
+ method: 'GET',
|
|
|
+ data: {
|
|
|
+ type: selectedNetwork.value
|
|
|
+ },
|
|
|
+ header: {
|
|
|
+ Authorization: uni.getStorageSync('token'),
|
|
|
+ lang: locale.value
|
|
|
+ },
|
|
|
+ success: (res) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ if (res.data && res.data.code === 0 && res.data.data) {
|
|
|
+ usdtQrcode.value = res.data.data.qrcode;
|
|
|
+ usdtAddress.value = res.data.data.address; // 保存地址
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: res.data.msg || t('获取二维码失败'), icon: 'none' });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fail: (err) => {
|
|
|
+ uni.hideLoading();
|
|
|
+ uni.showToast({ title: err.data?.msg || t('网络错误'), icon: 'none' });
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
onLoad(() => {
|
|
|
+ if(uni.getStorageSync('youke_token')) {
|
|
|
+ uni.navigateBack()
|
|
|
+ return
|
|
|
+ }
|
|
|
getData();
|
|
|
});
|
|
|
</script>
|
|
|
@@ -282,175 +464,213 @@ onLoad(() => {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
position: relative;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
-.main-scroll {
|
|
|
- flex: 1;
|
|
|
- overflow: hidden;
|
|
|
-}
|
|
|
-
|
|
|
-.top-up-page-container {
|
|
|
+.section {
|
|
|
padding: 30rpx;
|
|
|
- box-sizing: border-box;
|
|
|
+ background-color: #fff;
|
|
|
+ margin-top: 20rpx;
|
|
|
}
|
|
|
|
|
|
-.recharge-container {
|
|
|
- width: 100%;
|
|
|
+.section-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-bottom: 30rpx;
|
|
|
+ color: #333;
|
|
|
}
|
|
|
|
|
|
-.qr-code-section {
|
|
|
- margin-bottom: 40rpx;
|
|
|
+.channel-list {
|
|
|
display: flex;
|
|
|
- justify-content: center;
|
|
|
- .qr-code-bg {
|
|
|
- background-color: #fff;
|
|
|
- padding: 20rpx;
|
|
|
- border-radius: 12rpx;
|
|
|
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
|
|
- }
|
|
|
- .empty-qr-code {
|
|
|
- width: 180rpx;
|
|
|
- height: 180rpx;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- border: 1px dashed #e5e7eb;
|
|
|
- background: #f9fafb;
|
|
|
- .empty-text { font-size: 24rpx; margin-top: 8rpx; color: #9ca3af; }
|
|
|
- }
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 20rpx;
|
|
|
}
|
|
|
|
|
|
-.selection-section {
|
|
|
- margin-bottom: 32rpx;
|
|
|
- .section-title {
|
|
|
- font-size: 30rpx;
|
|
|
- font-weight: bold;
|
|
|
- color: #1f2329;
|
|
|
- margin-bottom: 20rpx;
|
|
|
- display: block;
|
|
|
+.channel-item {
|
|
|
+ padding: 16rpx 24rpx;
|
|
|
+ border: 2rpx solid #eee;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ transition: all 0.2s;
|
|
|
+
|
|
|
+ &.active {
|
|
|
+ border-color: #f8b932;
|
|
|
+ background-color: rgba(248, 185, 50, 0.1);
|
|
|
+ color: #f8b932;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.currency-options {
|
|
|
+.amount-list {
|
|
|
display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
gap: 20rpx;
|
|
|
- overflow-x: auto;
|
|
|
- padding-bottom: 10rpx;
|
|
|
- /* 隐藏滚动条 */
|
|
|
- &::-webkit-scrollbar {
|
|
|
- display: none;
|
|
|
- width: 0 !important;
|
|
|
- height: 0 !important;
|
|
|
- -webkit-appearance: none;
|
|
|
- background: transparent;
|
|
|
- }
|
|
|
- -ms-overflow-style: none;
|
|
|
- scrollbar-width: none;
|
|
|
}
|
|
|
|
|
|
-.currency-card {
|
|
|
- flex: 0 0 auto;
|
|
|
- width: 220rpx;
|
|
|
- padding: 26rpx 16rpx;
|
|
|
- min-height: 100rpx;
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
- border-radius: 16rpx;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- justify-content: center;
|
|
|
- align-items: center;
|
|
|
- position: relative;
|
|
|
- background-color: #fff;
|
|
|
+.amount-item {
|
|
|
+ width: calc(33.33% - 14rpx);
|
|
|
+ text-align: center;
|
|
|
+ padding: 24rpx 0;
|
|
|
+ border: 2rpx solid #eee;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #333;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ box-sizing: border-box;
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
- &:active {
|
|
|
- transform: scale(0.96);
|
|
|
- }
|
|
|
-
|
|
|
- &.selected {
|
|
|
+ &.active {
|
|
|
border-color: #f8b932;
|
|
|
- background-color: rgba(248, 185, 50, 0.04);
|
|
|
- .currency-name { color: #f8b932; }
|
|
|
+ background-color: rgba(248, 185, 50, 0.1);
|
|
|
+ color: #f8b932;
|
|
|
+ font-weight: bold;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- .currency-name {
|
|
|
+.amount-input-box {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ border-bottom: 2rpx solid #eee;
|
|
|
+ padding: 10rpx 0 20rpx 0;
|
|
|
+
|
|
|
+ .currency {
|
|
|
+ font-size: 48rpx;
|
|
|
font-weight: bold;
|
|
|
- font-size: 32rpx;
|
|
|
+ margin-right: 16rpx;
|
|
|
color: #333;
|
|
|
}
|
|
|
-
|
|
|
- .check-icon {
|
|
|
- position: absolute;
|
|
|
- top: 12rpx;
|
|
|
- right: 12rpx;
|
|
|
+
|
|
|
+ .amount-input {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 40rpx;
|
|
|
+ height: 70rpx;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.network-options {
|
|
|
+.rate-tip {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #f56c6c;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.network-section {
|
|
|
+ margin-top: 30rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.network-title {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #666;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.network-list {
|
|
|
display: flex;
|
|
|
flex-wrap: wrap;
|
|
|
- gap: 16rpx;
|
|
|
+ gap: 20rpx;
|
|
|
}
|
|
|
|
|
|
-.network-card {
|
|
|
- padding: 12rpx 28rpx;
|
|
|
- border: 1px solid #e5e7eb;
|
|
|
- border-radius: 40rpx;
|
|
|
- background: #fff;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 8rpx;
|
|
|
- &.selected {
|
|
|
+.network-item {
|
|
|
+ padding: 16rpx 40rpx;
|
|
|
+ border: 2rpx solid #eee;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ transition: all 0.2s;
|
|
|
+
|
|
|
+ &.active {
|
|
|
border-color: #f8b932;
|
|
|
- background-color: rgba(248, 185, 50, 0.04);
|
|
|
+ background-color: rgba(248, 185, 50, 0.1);
|
|
|
color: #f8b932;
|
|
|
}
|
|
|
- .network-name { font-size: 26rpx; }
|
|
|
}
|
|
|
|
|
|
-.address-section {
|
|
|
- margin-bottom: 32rpx;
|
|
|
- .address-tip {
|
|
|
- margin-top: 12rpx;
|
|
|
- font-size: 22rpx;
|
|
|
- color: #ef4444;
|
|
|
- line-height: 1.4;
|
|
|
+.qrcode-box {
|
|
|
+ margin-top: 40rpx;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .qrcode-img {
|
|
|
+ width: 400rpx;
|
|
|
+ height: 400rpx;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ padding: 20rpx;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-.amount-section, .estimated-section {
|
|
|
- margin-bottom: 32rpx;
|
|
|
- .error-tip { color: #ef4444; font-size: 22rpx; margin-top: 8rpx; }
|
|
|
- .exchange-rate { font-size: 24rpx; color: #94a3b8; margin-top: 8rpx; }
|
|
|
+/* 充值地址展示样式 */
|
|
|
+.address-box {
|
|
|
+ margin-top: 30rpx;
|
|
|
+ padding: 20rpx;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ border: 2rpx solid #eee;
|
|
|
+}
|
|
|
+
|
|
|
+.address-text {
|
|
|
+ flex: 1;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #333;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ word-break: break-all;
|
|
|
+}
|
|
|
+
|
|
|
+.copy-btn {
|
|
|
+ padding: 10rpx 30rpx;
|
|
|
+ background-color: #f8b932;
|
|
|
+ color: #000;
|
|
|
+ font-size: 24rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ font-weight: bold;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+/* 结果页展示样式 */
|
|
|
+.result-section {
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.result-text-box {
|
|
|
+ text-align: left;
|
|
|
+ background-color: #f9f9f9;
|
|
|
+ padding: 30rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
}
|
|
|
|
|
|
-.upload-section {
|
|
|
- margin-bottom: 40rpx;
|
|
|
+.result-line {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+ line-height: 1.8;
|
|
|
+ margin-bottom: 10rpx;
|
|
|
+ word-break: break-all;
|
|
|
}
|
|
|
|
|
|
-.input-border {
|
|
|
- border-bottom: 1px solid #e5e7eb;
|
|
|
- background: transparent;
|
|
|
- padding: 10rpx 0;
|
|
|
+.link-text {
|
|
|
+ color: #007aff;
|
|
|
+ text-decoration: underline;
|
|
|
}
|
|
|
|
|
|
-/* 固定底部按钮样式 */
|
|
|
.fixed-submit-section {
|
|
|
position: fixed;
|
|
|
bottom: 0;
|
|
|
left: 0;
|
|
|
width: 100%;
|
|
|
padding: 20rpx 30rpx;
|
|
|
- padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); // 适配全面屏底部
|
|
|
+ padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
|
|
box-sizing: border-box;
|
|
|
- // box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
|
|
|
z-index: 99;
|
|
|
+ background-color: #fff;
|
|
|
}
|
|
|
|
|
|
-/* 占位符,高度略大于底部按钮区域高度 */
|
|
|
.bottom-placeholder {
|
|
|
height: 300rpx;
|
|
|
width: 100%;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|