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

775 lines
18 KiB
Vue

<template>
<view class="asset-page asset-theme power-page">
<asset-page-shell title="BMT闪兑" />
<view class="asset-scroll power-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">{{ 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('yue')"
mode="aspectFit"
></image>
<text class="info-row__label">我的余额</text>
</view>
<text class="info-row__value asset-number-font">{{ displayBalance }}</text>
</view>
<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="pageIcon('bmt')"
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">BMT/CNY</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/shandui.png"
mode="aspectFit"
></image>
<text class="form-card__title-text">BMT闪兑</text>
</view>
<view class="mode-tabs">
<view
v-for="item in modeList"
:key="item.value"
class="mode-tabs__item"
:class="{ 'is-active': form.mode === item.value }"
@click="form.mode = item.value"
>
{{ item.label }}
</view>
</view>
<view class="form-input">
<input
v-model="form.amount"
class="form-input__field asset-number-font"
type="number"
:placeholder="'请输入' + currentModeLabel + '数量'"
placeholder-class="form-input__placeholder"
@wheel.prevent="preventAmountWheelChange"
/>
<text class="form-input__suffix">{{ currentModeLabel }}</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>
<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="openConfirmDialog">确认闪兑</view>
<view class="action-link" @click="openLedger">闪兑记录</view>
</view>
</view>
<asset-confirm-popup
:visible="dialog.visible"
:title="dialog.title"
:message="dialog.message"
:description="dialog.description"
:confirm-text="dialog.confirmText"
:cancel-text="dialog.cancelText"
:show-cancel="dialog.showCancel"
:status="dialog.status"
@cancel="closeDialog"
@confirm="handleDialogConfirm"
/>
</view>
</template>
<script>
import AssetConfirmPopup from "../../components/asset-confirm-popup.vue";
import AssetPageShell from "../../components/asset-page-shell.vue";
import {
fetchBmtFlashExchangeDetail,
submitAssetBmtFlashExchange,
} from "../../api/assets";
export default {
components: {
AssetConfirmPopup,
AssetPageShell,
},
data() {
return {
modeList: [
{ label: "抵用券闪兑", value: "voucher" },
{ label: "消费券闪兑", value: "coupon" },
{ label: "余额闪兑", value: "balance" },
],
detail: {
ticker: {},
balances: {},
tips: [],
},
hasShown: false,
form: {
mode: "voucher",
amount: "",
},
dialog: {
visible: false,
title: "确认闪兑",
message: "",
description: "",
confirmText: "确认",
cancelText: "取消",
showCancel: true,
status: "default",
action: "",
},
submitting: false,
};
},
onLoad() {
this.loadPage(true);
},
onShow() {
if (this.hasShown) {
this.loadPage();
return;
}
this.hasShown = true;
},
computed: {
amountValue() {
return Number(this.form.amount || 0);
},
currentModeLabel() {
if (this.form.mode === "coupon") {
return "消费券";
}
if (this.form.mode === "balance") {
return "余额";
}
return "抵用券";
},
currentModeBalance() {
if (this.form.mode === "coupon") {
return Number(this.detail.balances.coupon || 0);
}
if (this.form.mode === "balance") {
return Number(this.detail.balances.brokerage_price || 0);
}
return Number(this.detail.balances.voucher || 0);
},
priceNumber() {
return Number(this.detail.ticker.close || this.detail.ticker.cnyPrice || 0);
},
estimateBmt() {
if (!this.amountValue || !this.priceNumber) {
return "0";
}
return this.formatAmount(this.amountValue / this.priceNumber, 2);
},
allEstimateBmt() {
if (!this.priceNumber) {
return "0";
}
const totalAmount =
Number(this.detail.balances.voucher || 0) +
Number(this.detail.balances.coupon || 0) +
Number(this.detail.balances.brokerage_price || 0);
return this.formatAmount(totalAmount / this.priceNumber, 2);
},
displayBmt() {
return this.formatAmount(this.detail.balances.bmt, 2);
},
displayVoucher() {
return this.formatAmount(this.detail.balances.voucher, 2);
},
displayBalance() {
return this.formatAmount(this.detail.balances.brokerage_price, 2);
},
displayCoupon() {
return this.formatAmount(this.detail.balances.coupon, 2);
},
displayPrice() {
return this.priceNumber ? this.priceNumber : "0.00";
},
normalizedTips() {
if (this.detail.tips && this.detail.tips.length) {
return this.detail.tips;
}
return [
"BMT闪兑 = 输入数量 ÷ BMT实时价格",
"输入闪兑数量小于10的倍数不可闪兑",
"闪兑成功后数量将直接结算到可用BMT。",
];
},
},
methods: {
pageIcon(type) {
const iconMap = {
yue: "https://imgs.agrimedia.cn/bm-bmt/qianbao-icon.png",
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-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, ",");
},
preventAmountWheelChange(event) {
if (event && typeof event.preventDefault === "function") {
event.preventDefault();
}
const target = event && event.target;
if (target && typeof target.blur === "function") {
target.blur();
}
},
async loadPage(showLoading) {
try {
this.detail = await fetchBmtFlashExchangeDetail(
showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null,
);
} catch (error) {
this.openResultDialog(
"error",
"闪兑失败!",
error.message || "页面加载失败",
);
}
},
openDialog(payload) {
this.dialog = {
...this.dialog,
...payload,
visible: true,
};
},
closeDialog() {
this.dialog.visible = false;
this.dialog.action = "";
},
openResultDialog(status, message, description) {
this.openDialog({
title: "确认闪兑",
message: message,
description: description || "",
confirmText: "确认",
cancelText: "取消",
showCancel: false,
status: status,
action: "close",
});
},
openConfirmDialog() {
if (!this.amountValue) {
this.openResultDialog("error", "闪兑失败!", "请输入闪兑数量");
return;
}
if (this.amountValue < 10) {
this.openResultDialog(
"error",
"闪兑失败!",
"输入闪兑数量小于10的倍数不可闪兑",
);
return;
}
if (this.amountValue > this.currentModeBalance) {
this.openResultDialog(
"error",
"闪兑失败!",
"当前可用" + this.currentModeLabel + "不足",
);
return;
}
this.openDialog({
title: "确认闪兑",
message:
"确认使用" +
this.formatAmount(this.amountValue, 0) +
this.currentModeLabel +
"闪兑BMT?",
description: "预估可得 " + this.estimateBmt + " BMT",
confirmText: "确认",
cancelText: "取消",
showCancel: true,
status: "default",
action: "submit",
});
},
async submit() {
if (this.submitting) {
return;
}
this.submitting = true;
try {
await submitAssetBmtFlashExchange(
{
mode: this.form.mode,
amount: this.amountValue,
},
{
showLoading: true,
loadingText: "闪兑中",
},
);
this.form.amount = "";
await this.loadPage();
this.openResultDialog("success", "闪兑成功!", "");
} catch (error) {
this.openResultDialog(
"error",
"闪兑失败!",
error.message || "系统繁忙,请稍后再试",
);
} finally {
this.submitting = false;
}
},
handleDialogConfirm() {
if (this.dialog.action === "submit") {
this.closeDialog();
this.submit();
return;
}
this.closeDialog();
},
openLedger() {
uni.navigateTo({
url: "/pages/assets/ledger?type=bmt-flash",
});
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.power-page {
min-height: 100vh;
background: #191e32;
}
.power-scroll {
padding: 8rpx 20rpx calc(env(safe-area-inset-bottom) + 36rpx);
}
.panel-card,
.hero-card {
border-radius: 12rpx;
background: #20263e;
box-shadow: 0 12rpx 24rpx rgba(8, 13, 30, 0.12);
}
.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: 48rpx;
font-weight: 800;
line-height: 1.1;
color: #ffffff;
}
.panel-card {
margin-top: 18rpx;
padding: 0 16rpx;
}
.info-card {
padding-top: 2rpx;
padding-bottom: 14rpx;
}
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 104rpx;
border-bottom: 1px solid rgba(136, 148, 193, 0.34);
}
.info-row--last {
border-bottom: 1px solid rgba(136, 148, 193, 0.34);
}
.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;
align-items: baseline;
justify-content: flex-end;
padding-top: 18rpx;
font-size: 28rpx;
color: #1ad296;
}
.info-card__estimate-value {
margin: 0 10rpx;
font-size: 32rpx;
font-weight: 800;
color: #1ad296;
}
.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;
}
.mode-tabs {
display: flex;
width: 600rpx;
margin: 26rpx auto 0;
padding: 4rpx;
border-radius: 999rpx;
background: rgba(102, 115, 160, 0.28);
box-shadow: inset 0 0 0 1px rgba(165, 177, 216, 0.16);
}
.mode-tabs__item {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 60rpx;
border-radius: 999rpx;
font-size: 28rpx;
font-weight: 600;
color: rgba(210, 219, 242, 0.72);
}
.mode-tabs__item.is-active {
background: #02abf1;
color: #ffffff;
}
.form-input {
display: flex;
align-items: center;
margin-top: 34rpx;
padding: 0 20rpx;
border-radius: 10rpx;
background: #191e32;
}
.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: 28rpx;
font-weight: 600;
color: #ffffff;
}
.preview-card {
margin-top: 18rpx;
padding: 14rpx 18rpx;
border-radius: 8rpx;
background: linear-gradient(180deg, #21455a 0%, #1f4358 100%);
}
.preview-card__row {
display: flex;
align-items: center;
justify-content: space-between;
}
.preview-card__left {
display: flex;
align-items: center;
}
.preview-card__label {
font-size: 26rpx;
color: rgba(229, 252, 255, 0.94);
}
.preview-card__value {
font-size: 28rpx;
font-weight: 800;
color: #1df0b8;
}
.tips-block {
margin-top: 24rpx;
}
.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-weight: 400;
font-size: 24rpx;
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: 90rpx;
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
border-radius: 46rpx;
background: linear-gradient(135deg, #20b6f5 0%, #1ca5e3 100%);
box-shadow: 0 16rpx 30rpx rgba(20, 119, 214, 0.16);
}
.action-link {
margin-top: 26rpx;
text-align: center;
font-weight: 500;
font-size: 28rpx;
color: #1fb4ff;
}
</style>