538 lines
12 KiB
Vue
538 lines
12 KiB
Vue
<template>
|
||
<view class="asset-page asset-theme bmtr-page">
|
||
<asset-page-shell title="BMTR" />
|
||
|
||
<view class="bmtr-scroll">
|
||
<view class="bmtr-hero">
|
||
<view class="bmtr-hero__main">
|
||
<view class="bmtr-hero__head">
|
||
<view class="bmtr-hero__badge">
|
||
<text class="bmtr-hero__badge-text">R</text>
|
||
</view>
|
||
<text class="bmtr-hero__title">BMTR</text>
|
||
</view>
|
||
<text class="bmtr-hero__label">可用 BMTR:</text>
|
||
<text class="bmtr-hero__value asset-number-font">{{
|
||
summary.available
|
||
}}</text>
|
||
<!-- <text class="bmtr-hero__total asset-number-font">累计:{{ summary.total }}</text> -->
|
||
<!-- <text class="bmtr-hero__total asset-number-font">(仅米小嘉用户使用)</text> -->
|
||
</view>
|
||
<!-- 设计图右侧的钱包插图,替换 heroImage 为实际图片地址即可显示 -->
|
||
<image v-if="heroImage" class="bmtr-hero__image" :src="heroImage" mode="aspectFit"></image>
|
||
</view>
|
||
|
||
<view class="bmtr-address glass-panel">
|
||
<view class="bmtr-address__head">
|
||
<image class="bmtr-address__icon" src="https://imgs.agrimedia.cn/bm-bmt/qianbao.png" mode="aspectFit"></image>
|
||
<text class="bmtr-address__title">白马提回地址</text>
|
||
</view>
|
||
<view class="bmtr-address__field">
|
||
<view class="bmtr-address__content">
|
||
<text class="bmtr-address__label" v-if="displayAddress">从交易所提回BMTR的地址</text>
|
||
<text class="bmtr-address__value asset-number-font">{{ displayAddress || "请绑定手机号" }}</text>
|
||
</view>
|
||
<view class="bmtr-address__actions" v-if="displayAddress">
|
||
<text class="bmtr-address__action" @click="toggleAddress">{{
|
||
isAddressVisible ? "隐藏" : "显示"
|
||
}}</text>
|
||
<text class="bmtr-address__action" @click="copyAddress">复制</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<text class="bmtr-section-title">BMTR记录</text>
|
||
|
||
<view v-if="records.length" class="bmtr-records glass-panel">
|
||
<view v-for="item in records" :key="item.id" class="bmtr-record">
|
||
<view class="bmtr-record__main">
|
||
<text class="bmtr-record__title">{{ item.title }}</text>
|
||
<text class="bmtr-record__time">{{ item.time }}</text>
|
||
</view>
|
||
<text class="bmtr-record__amount asset-number-font" :class="item.amount >= 0
|
||
? 'bmtr-record__amount--in'
|
||
: 'bmtr-record__amount--out'
|
||
">{{ formatAmount(item.amount) }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else class="bmtr-records-empty glass-panel">
|
||
<text class="bmtr-records-empty__text">暂无记录</text>
|
||
</view>
|
||
|
||
<view v-if="showLoadMoreState" class="list-load-more">
|
||
<text class="list-load-more__text asset-number-font">{{ loadMoreText }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import AssetPageShell from "../../components/asset-page-shell.vue";
|
||
import { fetchBmtrDetail, fetchBmtrRecords } from "../../api/assets";
|
||
|
||
export default {
|
||
components: {
|
||
AssetPageShell,
|
||
},
|
||
data() {
|
||
return {
|
||
heroImage: "",
|
||
summary: {
|
||
available: "0.0000",
|
||
total: "0.00",
|
||
},
|
||
address: "",
|
||
isAddressVisible: false,
|
||
records: [],
|
||
page: 1,
|
||
pageSize: 10,
|
||
hasMore: false,
|
||
loadingMore: false,
|
||
hasShown: false,
|
||
};
|
||
},
|
||
computed: {
|
||
displayAddress() {
|
||
if (this.isAddressVisible) {
|
||
return this.address;
|
||
}
|
||
|
||
return this.maskAddress(this.address);
|
||
|
||
},
|
||
showLoadMoreState() {
|
||
return this.records.length > 0;
|
||
},
|
||
loadMoreText() {
|
||
if (this.loadingMore) {
|
||
return "加载中...";
|
||
}
|
||
|
||
if (this.hasMore) {
|
||
return "上拉加载更多";
|
||
}
|
||
|
||
return "没有更多了";
|
||
},
|
||
},
|
||
onLoad() {
|
||
this.resetPagingState();
|
||
this.loadDetail(true);
|
||
this.loadRecords(false, 1);
|
||
},
|
||
onShow() {
|
||
if (this.hasShown) {
|
||
this.resetPagingState();
|
||
this.loadDetail(false);
|
||
this.loadRecords(false, 1);
|
||
return;
|
||
}
|
||
|
||
this.hasShown = true;
|
||
},
|
||
onReachBottom() {
|
||
this.loadMore();
|
||
},
|
||
methods: {
|
||
resetPagingState() {
|
||
this.page = 1;
|
||
this.hasMore = false;
|
||
this.loadingMore = false;
|
||
},
|
||
updatePaging(result, requestedPage, receivedLength) {
|
||
const pagination =
|
||
result && result.pagination && typeof result.pagination === "object"
|
||
? result.pagination
|
||
: null;
|
||
this.page =
|
||
Number(pagination && pagination.page) || Number(requestedPage) || this.page || 1;
|
||
|
||
if (pagination && typeof pagination.hasMore === "boolean") {
|
||
this.hasMore = pagination.hasMore;
|
||
return;
|
||
}
|
||
|
||
this.hasMore = receivedLength >= this.pageSize;
|
||
},
|
||
mergeRecords(currentRecords, nextRecords) {
|
||
const mergedMap = {};
|
||
const mergedList = [];
|
||
const appendItem = (item) => {
|
||
const key = String(item && item.id ? item.id : "").trim();
|
||
|
||
if (!key || mergedMap[key]) {
|
||
return;
|
||
}
|
||
|
||
mergedMap[key] = true;
|
||
mergedList.push(item);
|
||
};
|
||
|
||
(Array.isArray(currentRecords) ? currentRecords : []).forEach(appendItem);
|
||
(Array.isArray(nextRecords) ? nextRecords : []).forEach(appendItem);
|
||
return mergedList;
|
||
},
|
||
async loadDetail(showLoading) {
|
||
try {
|
||
const detail = await fetchBmtrDetail(
|
||
showLoading
|
||
? {
|
||
showLoading: true,
|
||
loadingText: "加载中",
|
||
}
|
||
: null,
|
||
);
|
||
|
||
this.summary = Object.assign({}, this.summary, {
|
||
available: detail.available,
|
||
});
|
||
this.address = detail.address;
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: error.message || "页面加载失败",
|
||
icon: "none",
|
||
});
|
||
}
|
||
},
|
||
async loadRecords(showLoading, targetPage) {
|
||
const page = Number(targetPage || 1);
|
||
const isLoadMore = page > 1;
|
||
|
||
if (isLoadMore && this.loadingMore) {
|
||
return;
|
||
}
|
||
|
||
if (isLoadMore) {
|
||
this.loadingMore = true;
|
||
}
|
||
|
||
try {
|
||
const result = await fetchBmtrRecords(
|
||
{
|
||
page: page,
|
||
pageSize: this.pageSize,
|
||
},
|
||
!isLoadMore && showLoading
|
||
? {
|
||
showLoading: true,
|
||
loadingText: "加载中",
|
||
}
|
||
: null,
|
||
);
|
||
const incomingRecords = Array.isArray(result.records)
|
||
? result.records
|
||
: [];
|
||
const previousCount = this.records.length;
|
||
const mergedRecords = isLoadMore
|
||
? this.mergeRecords(this.records, incomingRecords)
|
||
: incomingRecords;
|
||
this.records = mergedRecords;
|
||
|
||
this.updatePaging(result, page, incomingRecords.length);
|
||
|
||
if (isLoadMore && mergedRecords.length <= previousCount) {
|
||
this.hasMore = false;
|
||
}
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: error.message || (isLoadMore ? "加载更多失败" : "页面加载失败"),
|
||
icon: "none",
|
||
});
|
||
} finally {
|
||
if (isLoadMore) {
|
||
this.loadingMore = false;
|
||
}
|
||
}
|
||
},
|
||
loadMore() {
|
||
if (this.loadingMore || !this.hasMore) {
|
||
return;
|
||
}
|
||
|
||
this.loadRecords(false, this.page + 1);
|
||
},
|
||
maskAddress(address) {
|
||
const safeAddress = String(address || "");
|
||
|
||
if (!safeAddress) {
|
||
return "";
|
||
}
|
||
|
||
if (safeAddress.length <= 4) {
|
||
return "****";
|
||
}
|
||
|
||
const headLength = Math.min(4, Math.ceil(safeAddress.length / 3));
|
||
const tailLength = Math.min(4, Math.floor(safeAddress.length / 3));
|
||
|
||
return (
|
||
safeAddress.slice(0, headLength) +
|
||
"****" +
|
||
safeAddress.slice(-tailLength)
|
||
);
|
||
},
|
||
toggleAddress() {
|
||
this.isAddressVisible = !this.isAddressVisible;
|
||
},
|
||
copyAddress() {
|
||
uni.setClipboardData({
|
||
data: this.address,
|
||
success() {
|
||
uni.showToast({
|
||
title: "已复制",
|
||
icon: "none",
|
||
});
|
||
},
|
||
});
|
||
},
|
||
formatAmount(amount) {
|
||
const number = Number(amount || 0);
|
||
return (number >= 0 ? "+" : "-") + Math.abs(number);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import "../../styles/tokens.scss";
|
||
@import "../../styles/common.scss";
|
||
|
||
.bmtr-page {
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.bmtr-scroll {
|
||
padding: 12rpx 24rpx calc(env(safe-area-inset-bottom) + 36rpx);
|
||
}
|
||
|
||
.bmtr-hero {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 32rpx;
|
||
border-radius: 24rpx;
|
||
border: 1px solid rgba(143, 167, 207, 0.16);
|
||
background:
|
||
url("https://imgs.agrimedia.cn/webimg/202607221601148466081.png") center /
|
||
cover no-repeat,
|
||
radial-gradient(circle at 88% 30%,
|
||
rgba(76, 201, 255, 0.16),
|
||
transparent 55%),
|
||
linear-gradient(135deg, #1d2b52 0%, #14203f 100%);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bmtr-hero__main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-width: 0;
|
||
}
|
||
|
||
.bmtr-hero__head {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.bmtr-hero__badge {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
margin-right: 16rpx;
|
||
border-radius: 50%;
|
||
background: #ff5a5a;
|
||
}
|
||
|
||
.bmtr-hero__badge-text {
|
||
font-size: 36rpx;
|
||
font-weight: 800;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.bmtr-hero__title {
|
||
font-size: 36rpx;
|
||
font-weight: 700;
|
||
color: $asset-text-main;
|
||
}
|
||
|
||
.bmtr-hero__label {
|
||
margin-top: 24rpx;
|
||
font-size: 26rpx;
|
||
color: $asset-text-subtle;
|
||
}
|
||
|
||
.bmtr-hero__value {
|
||
margin-top: 8rpx;
|
||
font-size: 56rpx;
|
||
font-weight: 800;
|
||
line-height: 1.1;
|
||
color: $asset-text-main;
|
||
}
|
||
|
||
.bmtr-hero__total {
|
||
margin-top: 12rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: rgba(255, 97, 97, 1);
|
||
}
|
||
|
||
.bmtr-hero__image {
|
||
width: 260rpx;
|
||
height: 200rpx;
|
||
flex: 0 0 auto;
|
||
margin-left: 24rpx;
|
||
}
|
||
|
||
.bmtr-address {
|
||
margin-top: 24rpx;
|
||
padding: 28rpx;
|
||
}
|
||
|
||
.bmtr-address__head {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.bmtr-address__icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
margin-right: 12rpx;
|
||
}
|
||
|
||
.bmtr-address__title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: $asset-text-main;
|
||
}
|
||
|
||
.bmtr-address__field {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 24rpx;
|
||
padding: 24rpx;
|
||
border-radius: 14rpx;
|
||
background: rgba(18, 25, 48, 0.72);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
}
|
||
|
||
.bmtr-address__content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.bmtr-address__label {
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.62);
|
||
}
|
||
|
||
.bmtr-address__value {
|
||
margin-top: 12rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
word-break: break-all;
|
||
color: $asset-text-main;
|
||
}
|
||
|
||
.bmtr-address__actions {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 0 0 auto;
|
||
margin-left: 24rpx;
|
||
}
|
||
|
||
.bmtr-address__action {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
color: #38d0bd;
|
||
}
|
||
|
||
.bmtr-address__action+.bmtr-address__action {
|
||
margin-left: 28rpx;
|
||
}
|
||
|
||
.bmtr-section-title {
|
||
display: block;
|
||
margin: 36rpx 0 20rpx;
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
color: $asset-text-main;
|
||
}
|
||
|
||
.bmtr-records {
|
||
padding: 0 28rpx;
|
||
}
|
||
|
||
.bmtr-record {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 28rpx 0;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||
}
|
||
|
||
.bmtr-record:last-child {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.bmtr-record__main {
|
||
display: flex;
|
||
flex-direction: column;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.bmtr-record__title {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: $asset-text-main;
|
||
}
|
||
|
||
.bmtr-record__time {
|
||
margin-top: 10rpx;
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.45);
|
||
}
|
||
|
||
.bmtr-record__amount {
|
||
flex: 0 0 auto;
|
||
margin-left: 24rpx;
|
||
font-size: 36rpx;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.bmtr-record__amount--in {
|
||
color: #ff9440;
|
||
}
|
||
|
||
.bmtr-record__amount--out {
|
||
color: #35d9a0;
|
||
}
|
||
|
||
.bmtr-records-empty {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 70rpx 24rpx;
|
||
}
|
||
|
||
.bmtr-records-empty__text {
|
||
font-size: 26rpx;
|
||
color: rgba(158, 170, 204, 0.86);
|
||
}
|
||
|
||
.list-load-more {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 12rpx 0 22rpx;
|
||
}
|
||
|
||
.list-load-more__text {
|
||
font-size: 22rpx;
|
||
color: rgba(173, 182, 211, 0.9);
|
||
}
|
||
</style>
|