624 lines
15 KiB
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>
|