bm-bmt/pages/assets/withdraw.vue

674 lines
15 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.

<template>
<view class="asset-page asset-theme withdraw-page">
<asset-page-shell title="提取BMT" />
<view class="asset-scroll withdraw-scroll">
<view class="hero-card">
<view class="hero-card__content">
<view class="hero-card__coin-row">
<view class="hero-card__coin">
<image
class="hero-card__coin-image"
src="https://imgs.agrimedia.cn/bm-bmt/b-w.png"
mode="aspectFit"
></image>
</view>
<text class="hero-card__coin-text">BMT</text>
</view>
<text class="hero-card__label">本地钱包可提取BMT:</text>
<text class="hero-card__value asset-number-font">{{ displayWithdrawable }}</text>
<text class="hero-card__price asset-number-font"
>(BMT实时价格 {{ displayPrice }}CNY/BMT)</text
>
</view>
</view>
<view class="panel-card amount-card">
<view class="section-head">
<image
class="section-head__icon"
:src="pageIcon('bmt')"
mode="aspectFit"
></image>
<text class="section-head__title">BMT提取数量</text>
</view>
<view class="amount-input">
<input
v-model="form.amount"
class="amount-input__field asset-number-font"
type="number"
placeholder="请输入提取数量"
placeholder-class="amount-input__placeholder"
/>
<text class="amount-input__action" @click="fillAll">全部提取</text>
</view>
<view class="fee-card">
<text class="fee-card__label asset-number-font">提取手续费1%</text>
<text class="fee-card__value asset-number-font">{{ feeText }}</text>
</view>
</view>
<view class="panel-card wallet-card">
<view class="wallet-card__head">
<view class="section-head">
<view class="wallet-card__title-icon">
<view class="wallet-card__title-tab"></view>
<view class="wallet-card__title-body"></view>
</view>
<text class="section-head__title">钱包地址</text>
</view>
<text class="wallet-card__link" @click="openWallet"
>修改钱包地址</text
>
</view>
<view v-if="detail.defaultWallet" class="wallet-box">
<view class="wallet-box__head">
<text class="wallet-box__name">{{
detail.defaultWallet.name
}}</text>
<view class="wallet-box__actions">
<text class="wallet-box__action" @click="toggleWalletVisible">{{
walletVisible ? "隐藏" : "显示"
}}</text>
<text class="wallet-box__action" @click="copyWallet">复制</text>
</view>
</view>
<text class="wallet-box__address asset-number-font">{{ walletDisplayAddress }}</text>
</view>
<view v-else class="wallet-box wallet-box--empty" @click="openWallet">
<text class="wallet-box__plus"></text>
<text class="wallet-box__empty-text">添加钱包地址</text>
</view>
<text class="wallet-card__desc">
请仔细核对钱包地址,一经转出将无法追回。
</text>
</view>
<view class="action-wrap">
<view class="action-button" @click="openConfirm">确定</view>
<view class="action-link" @click="openLedger">提取记录</view>
</view>
</view>
<asset-confirm-popup
:visible="confirmVisible"
title="确认提取"
:message="'确认提取 ' + amountDisplay + ' BMT 到以下钱包吗?'"
confirm-text="确认"
cancel-text="取消"
@cancel="confirmVisible = false"
@confirm="submit"
>
<view class="popup-detail">
<view class="popup-detail__row">
<text class="meta-pair__label">钱包地址</text>
<text class="meta-pair__value popup-address asset-number-font">{{
detail.defaultWallet ? detail.defaultWallet.address : "未设置"
}}</text>
</view>
<view class="popup-detail__row">
<text class="meta-pair__label">手续费</text>
<text class="meta-pair__value asset-number-font">{{ feeText }}</text>
</view>
<view class="popup-detail__row">
<text class="meta-pair__label">预计到账</text>
<text class="meta-pair__value asset-number-font">{{ actualText }}</text>
</view>
</view>
</asset-confirm-popup>
</view>
</template>
<script>
import AssetConfirmPopup from "../../components/asset-confirm-popup.vue";
import AssetPageShell from "../../components/asset-page-shell.vue";
import { fetchWithdrawDetail, submitAssetWithdraw } from "../../api/assets";
export default {
components: {
AssetConfirmPopup,
AssetPageShell,
},
data() {
return {
detail: {
ticker: {},
withdrawableBmt: "0",
defaultWallet: null,
},
hasShown: false,
form: {
amount: "",
},
confirmVisible: false,
walletVisible: false,
submitting: false,
};
},
onLoad() {
this.loadPage(true);
},
onShow() {
if (this.hasShown) {
this.loadPage();
return;
}
this.hasShown = true;
},
computed: {
amountValue() {
return Number(this.form.amount || 0);
},
amountDisplay() {
return this.formatAmount(this.amountValue, 2);
},
feeText() {
return this.amountValue
? this.formatAmount(this.amountValue * 0.01, 2) + " BMT"
: "0.00 BMT";
},
actualText() {
return this.amountValue
? this.formatAmount(this.amountValue * 0.99, 2) + " BMT"
: "0.00 BMT";
},
displayWithdrawable() {
return this.formatAmount(this.detail.withdrawableBmt, 2);
},
displayPrice() {
const ticker =
this.detail && this.detail.ticker && typeof this.detail.ticker === "object"
? this.detail.ticker
: {};
const price = Number(ticker.close || ticker.cnyPrice || 0);
if (!Number.isFinite(price) || price <= 0) {
return "0.000";
}
return price.toFixed(3);
},
walletDisplayAddress() {
if (!this.detail.defaultWallet || !this.detail.defaultWallet.address) {
return "";
}
const address = this.detail.defaultWallet.address;
if (this.walletVisible || address.length <= 16) {
return address;
}
return address.slice(0, 8) + "..." + address.slice(-8);
},
},
methods: {
pageIcon(type) {
const iconMap = {
power: "https://imgs.agrimedia.cn/bm-bmt/s.png",
bmt: "https://imgs.agrimedia.cn/bm-bmt/b.png",
transfer: "https://imgs.agrimedia.cn/bm-bmt/z.png",
withdraw: "https://imgs.agrimedia.cn/bm-bmt/t.png",
voucher: "https://imgs.agrimedia.cn/bm-bmt/quan-icon.png",
points: "https://imgs.agrimedia.cn/bm-bmt/j.png",
coupon: "https://imgs.agrimedia.cn/bm-bmt/xiaofei-icon.png",
};
return iconMap[type] || "";
},
formatAmount(value, digits) {
const number = Number(value || 0);
if (!Number.isFinite(number)) {
return digits > 0 ? Number(0).toFixed(digits) : "0";
}
if (digits > 0) {
const fixed = number.toFixed(digits);
const parts = fixed.split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
return parts.join(".");
}
return Math.floor(number)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
},
async loadPage(showLoading) {
try {
this.detail = await fetchWithdrawDetail(
showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null,
);
this.walletVisible = false;
} catch (error) {
uni.showToast({
title: error.message || "页面加载失败",
icon: "none",
});
}
},
fillAll() {
this.form.amount = this.detail.withdrawableBmt || "0";
},
toggleWalletVisible() {
this.walletVisible = !this.walletVisible;
},
copyWallet() {
if (!this.detail.defaultWallet || !this.detail.defaultWallet.address) {
return;
}
uni.setClipboardData({
data: this.detail.defaultWallet.address,
showToast: false,
success: () => {
uni.showToast({
title: "钱包地址已复制",
icon: "none",
});
},
});
},
openConfirm() {
if (!this.detail.defaultWallet) {
uni.showToast({
title: "请先添加钱包地址",
icon: "none",
});
return;
}
if (!this.amountValue) {
uni.showToast({
title: "请输入提取数量",
icon: "none",
});
return;
}
if (this.amountValue > Number(this.detail.withdrawableBmt || 0)) {
uni.showToast({
title: "可提取BMT不足",
icon: "none",
});
return;
}
this.confirmVisible = true;
},
async submit() {
if (this.submitting) {
return;
}
if (!this.detail.defaultWallet) {
uni.showToast({
title: "请先添加钱包地址",
icon: "none",
});
return;
}
if (!this.amountValue) {
uni.showToast({
title: "请输入提取数量",
icon: "none",
});
return;
}
this.submitting = true;
try {
await submitAssetWithdraw({
amount: this.amountValue,
address: this.detail.defaultWallet.address,
}, {
showLoading: true,
loadingText: "提交中",
});
this.confirmVisible = false;
this.form.amount = "";
uni.showToast({
title: "提取申请成功",
icon: "none",
});
this.loadPage();
} catch (error) {
uni.showToast({
title: error.message || "提取失败",
icon: "none",
});
} finally {
this.submitting = false;
}
},
openWallet() {
uni.navigateTo({
url: "/pages/assets/wallet",
});
},
openLedger() {
uni.navigateTo({
url: "/pages/assets/ledger?type=withdraw",
});
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.withdraw-page {
min-height: 100vh;
background: #191e32;
}
.withdraw-scroll {
padding: 8rpx 14rpx calc(env(safe-area-inset-bottom) + 36rpx);
}
.panel-card,
.hero-card {
border-radius: 12rpx;
background: #242944;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.12);
}
.hero-card {
position: relative;
min-height: 240rpx;
padding: 24rpx 28rpx;
overflow: hidden;
background: #242944 url("https://imgs.agrimedia.cn/bm-bmt/qianbao-bg.png")
no-repeat top / 100% 240rpx;
background-size: cover;
}
.hero-card__content {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
}
.hero-card__coin-row {
display: flex;
align-items: center;
}
.hero-card__coin {
display: flex;
align-items: center;
justify-content: center;
width: 56rpx;
height: 56rpx;
border-radius: 50%;
background: linear-gradient(135deg, #18c5ff 0%, #1496ff 100%);
}
.hero-card__coin-image {
width: 30rpx;
height: 30rpx;
}
.hero-card__coin-text {
margin-left: 16rpx;
font-size: 32rpx;
font-weight: 500;
color: #ffffff;
}
.hero-card__label {
margin-top: 34rpx;
font-size: 28rpx;
color: #9ba7ce;
}
.hero-card__value {
margin-top: 8rpx;
font-size: 42rpx;
font-weight: 800;
line-height: 1.1;
color: #ffffff;
}
.hero-card__price {
margin-top: 14rpx;
font-size: 28rpx;
color: #29dca3;
}
.panel-card {
margin-top: 18rpx;
padding: 22rpx 20rpx 24rpx;
}
.section-head {
display: flex;
align-items: center;
}
.section-head__icon {
width: 28rpx;
height: 28rpx;
flex-shrink: 0;
}
.section-head__title {
margin-left: 14rpx;
font-size: 28rpx;
font-weight: 700;
color: #ffffff;
}
.amount-input {
display: flex;
align-items: center;
margin-top: 22rpx;
padding: 0 20rpx;
border-radius: 10rpx;
background: rgba(25, 30, 50, 1);
}
.amount-input__field {
flex: 1;
height: 84rpx;
font-size: 36rpx;
font-weight: 700;
color: #ffffff;
}
.amount-input__placeholder {
color: rgba(170, 179, 204, 0.66);
font-size: 28rpx;
}
.amount-input__action {
margin-left: 20rpx;
font-size: 28rpx;
font-weight: 500;
color: #20b6f5;
}
.fee-card {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 18rpx;
padding: 16rpx 20rpx;
border-radius: 8rpx;
background: rgba(96, 67, 88, 0.44);
}
.fee-card__label {
font-size: 26rpx;
color: #ff9367;
}
.fee-card__value {
font-size: 24rpx;
font-weight: 700;
color: #ff9367;
}
.wallet-card__head {
display: flex;
align-items: center;
justify-content: space-between;
}
.wallet-card__title-icon {
position: relative;
width: 34rpx;
height: 28rpx;
margin-right: 2rpx;
flex-shrink: 0;
}
.wallet-card__title-tab {
position: absolute;
left: 5rpx;
top: -4rpx;
width: 16rpx;
height: 10rpx;
border-radius: 6rpx 6rpx 0 0;
background: #b48eff;
}
.wallet-card__title-body {
position: absolute;
left: 0;
bottom: 0;
width: 34rpx;
height: 22rpx;
border-radius: 6rpx;
background: linear-gradient(135deg, #b48eff 0%, #8c6ef0 100%);
}
.wallet-card__link {
font-size: 26rpx;
font-weight: 600;
color: #20b6f5;
}
.wallet-box {
margin-top: 18rpx;
padding: 18rpx 20rpx;
border-radius: 8rpx;
background: #353e5e;
}
.wallet-box__head {
display: flex;
align-items: center;
justify-content: space-between;
}
.wallet-box__name {
font-size: 26rpx;
color: rgba(232, 239, 255, 0.88);
}
.wallet-box__actions {
display: flex;
align-items: center;
}
.wallet-box__action {
margin-left: 24rpx;
font-size: 26rpx;
font-weight: 400;
color: #20b6f5;
}
.wallet-box__address {
margin-top: 18rpx;
font-size: 26rpx;
line-height: 1.6;
color: #ffffff;
word-break: break-all;
}
.wallet-box--empty {
display: flex;
align-items: center;
justify-content: center;
min-height: 120rpx;
background: #353e5e;
}
.wallet-box__plus {
margin-right: 14rpx;
font-size: 46rpx;
line-height: 1;
color: #20b6f5;
}
.wallet-box__empty-text {
font-size: 26rpx;
font-weight: 600;
color: #20b6f5;
}
.wallet-card__desc {
display: block;
margin-top: 22rpx;
font-size: 26rpx;
line-height: 1.65;
color: rgba(170, 179, 204, 0.9);
}
.action-wrap {
padding: 86rpx 20rpx 0;
}
.action-button {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 90rpx;
font-weight: 500;
font-size: 30rpx;
color: #ffffff;
border-radius: 46rpx;
background: linear-gradient(135deg, #20b6f5 0%, #1ca5e3 100%);
box-shadow: 0 16rpx 30rpx rgba(20, 119, 214, 0.16);
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
}
.action-link {
margin-top: 28rpx;
text-align: center;
font-weight: 500;
font-size: 28rpx;
color: #1fb4ff;
}
.popup-detail__row {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-top: 14rpx;
}
.popup-address {
max-width: 320rpx;
text-align: right;
word-break: break-all;
}
</style>