bm-bmt/pages/assets/ledger.vue

624 lines
15 KiB
Vue

<template>
<view class="asset-page asset-theme ledger-page">
<asset-page-shell :title="ledger.title || '记录'" />
<view class="asset-scroll" :class="{ 'ledger-scroll': isTransferLedger }">
<template v-if="isTransferLedger">
<view class="transfer-ledger-head">
<text class="transfer-ledger-head__eyebrow">{{
ledger.title || "记录"
}}</text>
<text class="transfer-ledger-head__desc">{{
ledger.subtitle || "资产流水记录"
}}</text>
</view>
<view v-if="transferRecords.length" class="transfer-ledger-list">
<view
v-for="item in transferRecords"
:key="item.id"
class="transfer-record-card"
>
<view
class="transfer-record-card__icon"
:class="
'transfer-record-card__icon--' +
(item.cardTone || item.tone || 'info')
"
>
<text class="transfer-record-card__icon-text">{{
item.actionSymbol || "·"
}}</text>
</view>
<view class="transfer-record-card__content">
<view class="transfer-record-card__row transfer-record-card__row--top">
<view class="transfer-record-card__main">
<text class="transfer-record-card__title">{{
item.title || "转赠记录"
}}</text>
<text
v-if="item.orderSn || item.subtitle"
class="transfer-record-card__order"
>
{{ item.orderSn ? "单号 " + item.orderSn : item.subtitle }}
</text>
</view>
<view class="transfer-record-card__amount-box">
<text class="transfer-record-card__asset">{{
item.assetLabel
}}</text>
<text
class="transfer-record-card__amount asset-number-font"
:class="
'transfer-record-card__amount--' +
(item.cardTone || item.tone || 'info')
"
>
{{ item.amount }}
</text>
</view>
</view>
<view
class="transfer-record-card__row transfer-record-card__row--middle"
>
<text class="transfer-record-card__balance asset-number-font">{{
item.balanceLabel || item.balance
}}</text>
<text class="transfer-record-card__fee asset-number-font">{{ item.feeText }}</text>
</view>
<view
class="transfer-record-card__row transfer-record-card__row--bottom"
>
<text class="transfer-record-card__time asset-number-font">{{ item.time }}</text>
<text
class="transfer-record-card__direction"
:class="
'transfer-record-card__direction--' +
(item.cardTone || item.tone || 'info')
"
>
{{ item.directionLabel }}
</text>
</view>
</view>
</view>
</view>
<view v-else class="transfer-empty">
<text class="transfer-empty__title">{{
"暂无" + (ledger.title || "记录")
}}</text>
<text class="transfer-empty__desc">
{{ ledger.subtitle || "当前暂无相关流水记录。" }}
</text>
</view>
</template>
<template v-else>
<view class="page-hero">
<view class="page-hero__mark">
<view class="page-hero__ring page-hero__ring--outer"></view>
<view class="page-hero__ring page-hero__ring--inner"></view>
<text class="page-hero__text">{{ pageMark }}</text>
</view>
<text class="page-hero__value">{{ ledger.title || "记录" }}</text>
<text v-if="ledger.subtitle" class="page-hero__desc asset-number-font">{{
ledger.subtitle
}}</text>
</view>
<view class="section glass-panel panel-block">
<text class="section-label">{{ ledger.title }}</text>
<text class="section-subtitle asset-number-font">{{ ledger.subtitle }}</text>
</view>
<view v-if="ledger.summary" class="section glass-panel panel-block">
<text class="section-label">{{ ledger.summary.label }}</text>
<text class="summary-hero asset-number-font">{{ ledger.summary.value }}</text>
</view>
<view
v-if="ledger.tabs && ledger.tabs.length"
class="section paper-panel panel-block"
>
<view class="tab-row tab-row--light">
<view
v-for="tab in ledger.tabs"
:key="tab.key"
class="tab-chip tab-chip--light"
:class="{ 'is-active': activeTab === tab.key }"
@click="activeTab = tab.key"
>
{{ tab.label }}
</view>
</view>
<view class="record-wrapper">
<asset-record-list :list="currentList" />
</view>
</view>
<view v-else class="section paper-panel panel-block">
<asset-record-list :list="ledger.records || []" />
</view>
</template>
<view v-if="showLoadMoreState" class="load-more-state">
<text class="load-more-state__text asset-number-font">{{ loadMoreText }}</text>
</view>
</view>
</view>
</template>
<script>
import AssetPageShell from "../../components/asset-page-shell.vue";
import AssetRecordList from "../../components/asset-record-list.vue";
import { fetchLedgerDetail } from "../../api/assets";
export default {
components: {
AssetPageShell,
AssetRecordList,
},
data() {
return {
type: "transfer",
ledger: {
title: "",
subtitle: "",
records: [],
tabs: [],
},
activeTab: "",
page: 1,
pageSize: 20,
hasMore: false,
loadingMore: false,
};
},
computed: {
isTransferLedger() {
return [
"transfer",
"points",
"power-flow",
"power",
"bmt",
"withdraw",
"voucher",
"coupon",
].indexOf(this.type) > -1;
},
currentList() {
if (!this.ledger.recordsByTab || !this.activeTab) {
return [];
}
return this.ledger.recordsByTab[this.activeTab] || [];
},
transferRecords() {
return Array.isArray(this.ledger.records) ? this.ledger.records : [];
},
showLoadMoreState() {
return this.isTransferLedger && this.transferRecords.length > 0;
},
loadMoreText() {
if (this.loadingMore) {
return "加载中...";
}
if (this.hasMore) {
return "上拉加载更多";
}
return "没有更多了";
},
pageMark() {
const markMap = {
transfer: "⇄",
power: "⚡",
bmt: "D",
withdraw: "B",
coupon: "券",
voucher: "抵",
points: "积",
};
return markMap[this.type] || "记";
},
},
onLoad(options) {
this.type = (options && options.type) || "transfer";
this.resetPagingState();
this.loadPage(true, 1);
},
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;
const nextPage =
Number(pagination && pagination.page) ||
Number(requestedPage) ||
this.page ||
1;
this.page = nextPage;
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() ||
[
item && item.orderSn,
item && item.time,
item && item.title,
item && item.amount,
]
.map((value) => String(value || ""))
.join("-");
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 loadPage(showLoading, targetPage) {
const page = Number(targetPage || 1);
const isLoadMore = page > 1;
if (isLoadMore && this.loadingMore) {
return;
}
if (isLoadMore) {
this.loadingMore = true;
}
try {
const requestOptions =
!isLoadMore && showLoading
? {
showLoading: true,
loadingText: "加载中",
}
: null;
const result = await fetchLedgerDetail(
this.type,
{
page: page,
pageSize: this.pageSize,
},
requestOptions,
);
const incomingRecords = Array.isArray(result.records) ? result.records : [];
const currentRecords = Array.isArray(this.ledger.records)
? this.ledger.records
: [];
const mergedRecords = isLoadMore
? this.mergeRecords(currentRecords, incomingRecords)
: incomingRecords;
this.ledger = Object.assign({}, this.ledger, result, {
records: mergedRecords,
});
if (!isLoadMore && result.tabs && result.tabs.length) {
this.activeTab = result.tabs[0].key || "";
}
this.updatePaging(result, page, incomingRecords.length);
if (isLoadMore && mergedRecords.length <= currentRecords.length) {
this.hasMore = false;
}
} catch (error) {
uni.showToast({
title: error.message || (isLoadMore ? "加载更多失败" : "记录加载失败"),
icon: "none",
});
} finally {
if (isLoadMore) {
this.loadingMore = false;
}
}
},
loadMore() {
if (!this.isTransferLedger || this.loadingMore || !this.hasMore) {
return;
}
this.loadPage(false, this.page + 1);
},
},
};
</script>
<style lang="scss" scoped>
@import "../../styles/tokens.scss";
@import "../../styles/common.scss";
.ledger-page {
min-height: 100vh;
background: #191e32;
}
.ledger-scroll {
padding: 12rpx 14rpx calc(env(safe-area-inset-bottom) + 36rpx);
}
.section {
margin-top: 16rpx;
}
.summary-hero {
display: block;
margin-top: 16rpx;
font-size: 56rpx;
font-weight: 700;
color: $asset-text-main;
}
.tab-row--light {
background: rgba(17, 27, 54, 0.54);
}
.tab-chip--light {
color: rgba(255, 255, 255, 0.64);
}
.record-wrapper {
margin-top: 18rpx;
}
.transfer-ledger-head {
padding: 10rpx 6rpx 14rpx;
}
.transfer-ledger-head__eyebrow {
display: block;
font-size: 34rpx;
font-weight: 700;
color: #ffffff;
}
.transfer-ledger-head__desc {
display: block;
margin-top: 10rpx;
font-size: 24rpx;
line-height: 1.6;
color: rgba(188, 197, 223, 0.82);
}
.transfer-ledger-list {
margin-top: 6rpx;
}
.transfer-record-card {
display: flex;
align-items: flex-start;
margin-top: 18rpx;
padding: 24rpx 20rpx;
border-radius: 16rpx;
background: #242944;
box-shadow: 0 14rpx 30rpx rgba(8, 13, 30, 0.14);
}
.transfer-record-card:first-child {
margin-top: 0;
}
.transfer-record-card__icon {
display: flex;
align-items: center;
justify-content: center;
width: 72rpx;
height: 72rpx;
margin-right: 18rpx;
border-radius: 50%;
flex-shrink: 0;
}
.transfer-record-card__icon--success {
background: rgba(41, 215, 164, 0.16);
color: #29d7a4;
}
.transfer-record-card__icon--danger {
background: rgba(255, 139, 93, 0.16);
color: #ff8b5d;
}
.transfer-record-card__icon--info {
background: rgba(32, 182, 245, 0.16);
color: #20b6f5;
}
.transfer-record-card__icon-text {
font-size: 38rpx;
font-weight: 700;
line-height: 1;
}
.transfer-record-card__content {
flex: 1;
min-width: 0;
}
.transfer-record-card__row {
display: flex;
justify-content: space-between;
}
.transfer-record-card__row--top {
align-items: flex-start;
}
.transfer-record-card__row--middle,
.transfer-record-card__row--bottom {
align-items: center;
margin-top: 18rpx;
}
.transfer-record-card__main {
flex: 1;
min-width: 0;
margin-right: 18rpx;
}
.transfer-record-card__title {
display: block;
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
}
.transfer-record-card__order {
display: block;
margin-top: 10rpx;
font-size: 22rpx;
line-height: 1.5;
color: rgba(167, 177, 207, 0.9);
word-break: break-all;
}
.transfer-record-card__amount-box {
display: flex;
align-items: baseline;
justify-content: flex-end;
flex-wrap: wrap;
min-width: 0;
}
.transfer-record-card__asset {
margin-right: 10rpx;
font-size: 24rpx;
color: rgba(191, 200, 224, 0.76);
}
.transfer-record-card__amount {
font-size: 34rpx;
font-weight: 700;
line-height: 1;
}
.transfer-record-card__amount--success {
color: #29d7a4;
}
.transfer-record-card__amount--danger {
color: #ff8b5d;
}
.transfer-record-card__amount--info {
color: #20b6f5;
}
.transfer-record-card__balance,
.transfer-record-card__time {
flex: 1;
min-width: 0;
font-size: 24rpx;
line-height: 1.5;
color: rgba(200, 208, 231, 0.82);
}
.transfer-record-card__time {
font-size: 22rpx;
color: rgba(159, 170, 202, 0.88);
}
.transfer-record-card__fee {
margin-left: 20rpx;
font-size: 24rpx;
line-height: 1.5;
text-align: right;
color: #5ad7a1;
}
.transfer-record-card__direction {
margin-left: 20rpx;
padding: 8rpx 18rpx;
border-radius: 999rpx;
font-size: 20rpx;
font-weight: 600;
}
.transfer-record-card__direction--success {
background: rgba(41, 215, 164, 0.14);
color: #29d7a4;
}
.transfer-record-card__direction--danger {
background: rgba(255, 139, 93, 0.14);
color: #ff8b5d;
}
.transfer-record-card__direction--info {
background: rgba(32, 182, 245, 0.14);
color: #20b6f5;
}
.transfer-empty {
margin-top: 20rpx;
padding: 48rpx 28rpx;
border-radius: 16rpx;
background: #242944;
text-align: center;
box-shadow: 0 14rpx 30rpx rgba(8, 13, 30, 0.14);
}
.transfer-empty__title {
display: block;
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
}
.transfer-empty__desc {
display: block;
margin-top: 16rpx;
font-size: 24rpx;
line-height: 1.6;
color: rgba(188, 197, 223, 0.78);
}
.load-more-state {
display: flex;
justify-content: center;
padding: 26rpx 0 8rpx;
}
.load-more-state__text {
font-size: 22rpx;
color: rgba(173, 182, 211, 0.9);
}
</style>