bm-bmt/pages/assets/bmt-exchange.vue

699 lines
16 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 bmt-page">
<asset-page-shell title="BMT兑换" />
<view class="asset-scroll bmt-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="pageIcon('bmt')"
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">{{ displayBmt }}</text>
</view>
</view>
<view class="panel-card info-card">
<view class="info-row">
<view class="info-row__left">
<image
class="info-row__icon"
:src="pageIcon('voucher')"
mode="aspectFit"
></image>
<text class="info-row__label">我的算力</text>
</view>
<text class="info-row__value asset-number-font">{{ displayVoucher }}</text>
</view>
<view class="info-row">
<view class="info-row__left">
<image
class="info-row__icon"
:src="pageIcon('coupon')"
mode="aspectFit"
></image>
<text class="info-row__label">可用积分</text>
</view>
<text class="info-row__value asset-number-font">{{ displayCoupon }}</text>
</view>
<view class="info-row info-row--last">
<view class="info-row__left">
<image
class="info-row__icon"
src="https://imgs.agrimedia.cn/bm-bmt/b.png"
mode="aspectFit"
></image>
<text class="info-row__label">BMT实时价格</text>
</view>
<view class="info-row__price">
<text class="info-row__value asset-number-font">{{ displayPrice }}</text>
<text class="info-row__unit">CNY/BMT</text>
</view>
</view>
<view class="info-card__estimate">
<text>全部兑换预估可得</text>
<text class="info-card__estimate-value asset-number-font">{{ allEstimateBmt }}</text>
<text>BMT</text>
</view>
</view>
<view class="panel-card form-card">
<view class="form-card__title">
<image
class="form-card__title-icon"
src="https://imgs.agrimedia.cn/bm-bmt/b.png"
mode="aspectFit"
></image>
<text class="form-card__title-text">BMT兑换</text>
</view>
<view class="form-input">
<input
v-model="form.points"
class="form-input__field asset-number-font"
type="number"
placeholder="请输入积分数量"
placeholder-class="form-input__placeholder"
/>
<text class="form-input__suffix">可用积分</text>
</view>
<view class="preview-card">
<view class="preview-card__row">
<view class="preview-card__left">
<text class="preview-card__label">预估兑换BMT</text>
</view>
<text class="preview-card__value asset-number-font">{{ estimateBmt }}</text>
</view>
<view class="preview-card__row">
<view class="preview-card__left">
<text class="preview-card__label" style="color: #fff"
>消耗算力</text
>
</view>
<text class="preview-card__value preview-card__value--light asset-number-font">{{
powerCost
}}</text>
</view>
</view>
<view class="tips-block">
<text class="tips-block__title">兑换说明:</text>
<view
v-for="(tip, index) in normalizedTips"
:key="tip"
class="tips-block__item"
>
<text class="tips-block__index asset-number-font">{{ index + 1 }}.</text>
<text class="tips-block__text asset-number-font">{{ tip }}</text>
</view>
</view>
</view>
<view class="action-wrap">
<view class="action-button" @click="confirmVisible = true"
>确认兑换</view
>
<view class="action-link" @click="openLedger">兑换记录</view>
</view>
</view>
<asset-confirm-popup
:visible="confirmVisible"
title="兑换提示"
:message="
'确认使用 ' + formatAmount(pointsValue, 0) + ' 积分兑换 BMT 吗?'
"
@cancel="confirmVisible = false"
@confirm="submit"
>
<view class="popup-line">
<text class="meta-pair__label">预估兑换BMT</text>
<text class="meta-pair__value asset-number-font">{{ estimateBmt }}</text>
</view>
<view class="popup-line">
<text class="meta-pair__label">消耗可用积分:</text>
<text class="meta-pair__value asset-number-font">{{ formatAmount(pointsValue, 0) }}</text>
</view>
<view class="popup-line">
<text class="meta-pair__label">消耗算力:</text>
<text class="meta-pair__value asset-number-font">{{ powerCost }}</text>
</view>
</asset-confirm-popup>
</view>
</template>
<script>
import AssetConfirmPopup from "../../components/asset-confirm-popup.vue";
import AssetPageShell from "../../components/asset-page-shell.vue";
import {
fetchBmtExchangeDetail,
submitAssetBmtExchange,
} from "../../api/assets";
export default {
components: {
AssetConfirmPopup,
AssetPageShell,
},
data() {
return {
detail: {
ticker: {},
balances: {},
tips: [],
},
hasShown: false,
form: {
points: "",
},
confirmVisible: false,
submitting: false,
};
},
onLoad() {
this.loadPage(true);
},
onShow() {
if (this.hasShown) {
this.loadPage();
return;
}
this.hasShown = true;
},
computed: {
pointsValue() {
return Number(this.form.points || 0);
},
powerRateNumber() {
return Number(this.detail.powerRate || 0);
},
estimateBmt() {
if (!this.pointsValue) {
return "0";
}
return this.formatAmount(this.pointsValue, 0);
},
powerCost() {
if (!this.pointsValue || !this.powerRateNumber) {
return "0.00";
}
return (this.pointsValue * this.powerRateNumber).toFixed(2);
},
allEstimateBmt() {
const pointsLimit = Number(this.detail.balances.points || 0);
const powerLimit = Number(this.detail.balances.power || 0);
if (!this.powerRateNumber) {
return "0";
}
const maxByPower = powerLimit / this.powerRateNumber;
const available = Math.max(0, Math.min(pointsLimit, maxByPower || 0));
return this.formatAmount(available, 2);
},
priceNumber() {
return Number(
this.detail.ticker.close || this.detail.ticker.cnyPrice || 0,
);
},
displayBmt() {
return this.detail.balances.bmt;
},
displayVoucher() {
return this.detail.balances.power;
},
displayCoupon() {
return this.detail.balances.points;
},
displayPrice() {
return this.priceNumber ? this.priceNumber.toFixed(2) : "0.00";
},
normalizedTips() {
if (this.detail.tips && this.detail.tips.length) {
return this.detail.tips;
}
return [
"1.BMT=积分-BMT实时价格",
"2.只能兑换100的整数倍小于100积分不可兑换",
"3.凌晨0点-凌晨1点积分系统维护不可兑换",
"4.可用积分不足可以将释放中的积分转换成可用积分0。",
];
},
},
methods: {
pageIcon(type) {
const iconMap = {
power: "https://imgs.agrimedia.cn/bm-bmt/s.png",
bmt: "https://imgs.agrimedia.cn/bm-bmt/b-w.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-g%20%281%29.png",
points: "https://imgs.agrimedia.cn/bm-bmt/j.png",
coupon: "https://imgs.agrimedia.cn/bm-bmt/xiaofei-icon-o.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 fetchBmtExchangeDetail(
showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null,
);
} catch (error) {
uni.showToast({
title: error.message || "页面加载失败",
icon: "none",
});
}
},
async submit() {
if (this.submitting) {
return;
}
if (!this.pointsValue) {
uni.showToast({
title: "请输入积分数量",
icon: "none",
});
return;
}
if (this.pointsValue < 100) {
uni.showToast({
title: "小于100积分不可兑换",
icon: "none",
});
return;
}
if (this.pointsValue % 100 !== 0) {
uni.showToast({
title: "只能兑换100的整数倍",
icon: "none",
});
return;
}
if (this.pointsValue > Number(this.detail.balances.points || 0)) {
uni.showToast({
title: "可用积分不足",
icon: "none",
});
return;
}
if (Number(this.powerCost) > Number(this.detail.balances.power || 0)) {
uni.showToast({
title: "可用算力不足",
icon: "none",
});
return;
}
this.submitting = true;
try {
await submitAssetBmtExchange({
amount: this.pointsValue,
}, {
showLoading: true,
loadingText: "兑换中",
});
this.confirmVisible = false;
this.form.points = "";
uni.showToast({
title: "兑换成功",
icon: "none",
});
this.loadPage();
} catch (error) {
uni.showToast({
title: error.message || "兑换失败",
icon: "none",
});
} finally {
this.submitting = false;
}
},
openLedger() {
uni.navigateTo({
url: "/pages/assets/ledger?type=bmt",
});
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.bmt-page {
min-height: 100vh;
background: #191e32;
}
.bmt-scroll {
padding: 8rpx 20rpx;
}
.panel-card,
.hero-card {
border-radius: 12rpx;
background: #20263e;
}
.hero-card {
position: relative;
min-height: 240rpx;
padding: 24rpx 28rpx;
overflow: hidden;
background: #20263e url("https://imgs.agrimedia.cn/bm-bmt/bmt2-header.png")
no-repeat center top / 100% 240rpx;
}
.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: 32rpx;
font-weight: 800;
color: #ffffff;
line-height: 1.1;
}
.panel-card {
margin-top: 18rpx;
padding: 0 26rpx;
}
.info-card {
padding-top: 2rpx;
padding-bottom: 14rpx;
}
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 96rpx;
border-bottom: 1rpx solid #4e5a82;
}
.info-row--last {
border-bottom: 1rpx solid #4e5a82;
}
.info-row__left {
display: flex;
align-items: center;
min-width: 0;
}
.info-row__icon {
width: 34rpx;
height: 34rpx;
flex-shrink: 0;
}
.info-row__label {
margin-left: 20rpx;
font-size: 28rpx;
font-weight: 400;
color: #eef2ff;
}
.info-row__value {
font-size: 32rpx;
font-weight: 700;
color: #ffffff;
}
.info-row__price {
display: flex;
align-items: baseline;
}
.info-row__unit {
margin-left: 10rpx;
font-size: 22rpx;
color: rgba(184, 193, 218, 0.88);
}
.info-card__estimate {
display: flex;
justify-content: flex-end;
align-items: baseline;
padding-top: 18rpx;
font-size: 28rpx;
color: #1ad296;
}
.info-card__estimate-value {
margin: 0 10rpx;
font-size: 32rpx;
font-weight: 800;
color: #2fe2a4;
}
.form-card {
padding-top: 18rpx;
padding-bottom: 20rpx;
}
.form-card__title {
display: flex;
align-items: center;
}
.form-card__title-icon {
width: 34rpx;
height: 34rpx;
flex-shrink: 0;
}
.form-card__title-text {
margin-left: 14rpx;
font-size: 28rpx;
font-weight: 700;
color: #ffffff;
}
.form-input {
display: flex;
align-items: center;
margin-top: 20rpx;
padding: 0 20rpx;
border-radius: 10rpx;
background: #191e32;
}
.form-input__icon {
width: 34rpx;
height: 34rpx;
margin-right: 18rpx;
flex-shrink: 0;
}
.form-input__field {
flex: 1;
height: 98rpx;
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
}
.form-input__placeholder {
color: rgba(177, 187, 214, 0.42);
}
.form-input__suffix {
margin-left: 16rpx;
font-size: 22rpx;
font-weight: 600;
color: #ffffff;
}
.preview-card {
margin-top: 18rpx;
padding: 12rpx 26rpx;
border-radius: 8rpx;
background: rgba(26, 210, 150, 0.1);
}
.preview-card__row {
display: flex;
align-items: center;
justify-content: space-between;
}
.preview-card__row + .preview-card__row {
margin-top: 8rpx;
}
.preview-card__left {
display: flex;
align-items: center;
}
.preview-card__icon {
width: 30rpx;
height: 30rpx;
margin-right: 10rpx;
flex-shrink: 0;
}
.preview-card__label {
font-size: 26rpx;
color: rgba(26, 210, 150, 1);
}
.preview-card__value {
font-size: 26rpx;
font-weight: 800;
color: #1ad296;
}
.preview-card__value--light {
color: #ffffff;
}
.tips-block {
margin-top: 24rpx;
padding: 0rpx 10rpx;
}
.tips-block__title {
display: block;
font-size: 24rpx;
color: rgba(190, 197, 219, 0.92);
}
.tips-block__item {
display: flex;
margin-top: 10rpx;
}
.tips-block__index,
.tips-block__text {
font-size: 24rpx;
color: #929dbf;
line-height: 44rpx;
color: rgba(166, 175, 204, 0.92);
}
.tips-block__index {
margin-right: 6rpx;
}
.tips-block__text {
flex: 1;
}
.action-wrap {
padding: 36rpx 22rpx 0;
}
.action-button {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 88rpx;
border-radius: 999rpx;
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: 26rpx;
text-align: center;
font-weight: 500;
font-size: 28rpx;
color: #1fb4ff;
}
.popup-line {
margin-top: 14rpx;
}
</style>