726 lines
17 KiB
Vue
726 lines
17 KiB
Vue
<template>
|
||
<view class="asset-page voucher-page" @click="closeTips">
|
||
<asset-page-shell title="抵用券" />
|
||
|
||
<view class="asset-scroll voucher-scroll">
|
||
<view class="voucher-header">
|
||
<view class="voucher-header__bg">
|
||
<image
|
||
class="voucher-header__bg-image"
|
||
src="https://imgs.agrimedia.cn/shop/d-bg.png"
|
||
mode="aspectFill"
|
||
></image>
|
||
<view class="voucher-header__content">
|
||
<view class="voucher-header__current">
|
||
<text class="voucher-header__current-label">当前抵用券:</text>
|
||
<text class="voucher-header__current-value asset-number-font">{{
|
||
summary.balance
|
||
}}</text>
|
||
</view>
|
||
|
||
<view class="voucher-header__totals">
|
||
<view class="voucher-header__total-item">
|
||
<text class="voucher-header__total-value asset-number-font">{{
|
||
summary.totalIn
|
||
}}</text>
|
||
<view
|
||
class="voucher-header__total-text"
|
||
@click.stop="toggleTip('showTip1')"
|
||
>
|
||
<text>累计收入</text>
|
||
<image
|
||
class="voucher-header__help-icon"
|
||
src="https://imgs.agrimedia.cn/shop/%E6%84%9F%E5%8F%B9%E5%8F%B7.svg"
|
||
mode="widthFix"
|
||
></image>
|
||
</view>
|
||
|
||
<view
|
||
v-if="showTip1"
|
||
class="voucher-tooltip voucher-tooltip--left"
|
||
@click.stop
|
||
>
|
||
<view class="voucher-tooltip__row">
|
||
<text class="voucher-tooltip__text">
|
||
收入不包含消费、退款扣回等扣除抵用券的场景
|
||
</text>
|
||
<view
|
||
class="voucher-tooltip__close"
|
||
@click.stop="showTip1 = false"
|
||
>
|
||
<image
|
||
src="https://imgs.agrimedia.cn/shop/line-close.png"
|
||
mode="aspectFit"
|
||
></image>
|
||
</view>
|
||
</view>
|
||
<view class="voucher-tooltip__arrow voucher-tooltip__arrow--left"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="voucher-header__total-item">
|
||
<text class="voucher-header__total-value asset-number-font">{{
|
||
summary.totalOut
|
||
}}</text>
|
||
<view
|
||
class="voucher-header__total-text"
|
||
@click.stop="toggleTip('showTip2')"
|
||
>
|
||
<text>累计支出</text>
|
||
<image
|
||
class="voucher-header__help-icon"
|
||
src="https://imgs.agrimedia.cn/shop/%E6%84%9F%E5%8F%B9%E5%8F%B7.svg"
|
||
mode="widthFix"
|
||
></image>
|
||
</view>
|
||
|
||
<view
|
||
v-if="showTip2"
|
||
class="voucher-tooltip voucher-tooltip--right"
|
||
@click.stop
|
||
>
|
||
<view class="voucher-tooltip__row">
|
||
<text class="voucher-tooltip__text">
|
||
支出包含消费、退款扣回等扣除抵用券的场景
|
||
</text>
|
||
<view
|
||
class="voucher-tooltip__close"
|
||
@click.stop="showTip2 = false"
|
||
>
|
||
<image
|
||
src="https://imgs.agrimedia.cn/shop/line-close.png"
|
||
mode="aspectFit"
|
||
></image>
|
||
</view>
|
||
</view>
|
||
<view class="voucher-tooltip__arrow"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="voucher-wrapper">
|
||
<view class="voucher-nav">
|
||
<view class="voucher-tabs">
|
||
<view
|
||
v-for="item in tabOptions"
|
||
:key="item.key"
|
||
class="voucher-tabs__item"
|
||
:class="{ 'is-active': activeTab === item.key }"
|
||
@click.stop="changeTab(item.key)"
|
||
>
|
||
<text class="voucher-tabs__text">{{ item.label }}</text>
|
||
<image
|
||
v-if="activeTab === item.key"
|
||
class="voucher-tabs__line"
|
||
src="https://imgs.agrimedia.cn/shop/active.png"
|
||
mode="widthFix"
|
||
></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="voucher-list" :class="{ 'voucher-list--filled': sections.length }">
|
||
<view
|
||
v-for="section in sections"
|
||
:key="section.key"
|
||
class="voucher-card"
|
||
>
|
||
<view class="voucher-card__head">
|
||
<view class="voucher-card__month">
|
||
<text class="voucher-card__month-text asset-number-font">{{
|
||
section.label
|
||
}}</text>
|
||
</view>
|
||
<view class="voucher-card__totals">
|
||
<text class="voucher-card__total-text">
|
||
收入:
|
||
<text class="voucher-card__total-income asset-number-font">{{
|
||
section.income
|
||
}}</text>
|
||
</text>
|
||
<text class="voucher-card__total-text">
|
||
支出:
|
||
<text class="voucher-card__total-expense asset-number-font">
|
||
-{{ section.expense }}
|
||
</text>
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
v-for="item in section.items"
|
||
:key="item.id"
|
||
class="voucher-row"
|
||
>
|
||
<view class="voucher-row__left">
|
||
<view class="voucher-row__icon">
|
||
<image
|
||
src="https://imgs.agrimedia.cn/shop/sifang-icon.png"
|
||
mode="aspectFit"
|
||
></image>
|
||
</view>
|
||
<view class="voucher-row__body">
|
||
<text class="voucher-row__title">{{ item.title }}</text>
|
||
<text class="voucher-row__time asset-number-font">{{
|
||
item.time
|
||
}}</text>
|
||
</view>
|
||
</view>
|
||
<text
|
||
class="voucher-row__amount asset-number-font"
|
||
:class="
|
||
item.amountTone === 'income'
|
||
? 'voucher-row__amount--income'
|
||
: 'voucher-row__amount--expense'
|
||
"
|
||
>
|
||
{{ item.amount }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="sections.length" class="voucher-loading">
|
||
<text class="voucher-loading__text asset-number-font">{{
|
||
loadMoreText
|
||
}}</text>
|
||
</view>
|
||
|
||
<view v-else class="voucher-empty">
|
||
<text class="voucher-empty__title">暂无记录哦~</text>
|
||
<text class="voucher-empty__desc">
|
||
当前分类下还没有抵用券流水数据。
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import AssetPageShell from "../../components/asset-page-shell.vue";
|
||
import { fetchVoucherCenterPage } from "../../api/assets";
|
||
|
||
export default {
|
||
components: {
|
||
AssetPageShell,
|
||
},
|
||
data() {
|
||
return {
|
||
summary: {
|
||
balance: "0",
|
||
totalIn: "0",
|
||
totalOut: "0",
|
||
},
|
||
tabOptions: [
|
||
{ key: "all", label: "全部" },
|
||
{ key: "normal", label: "常规补贴" },
|
||
{ key: "order", label: "单单补贴" },
|
||
],
|
||
activeTab: "all",
|
||
sections: [],
|
||
page: 1,
|
||
pageSize: 10,
|
||
hasMore: false,
|
||
loadingMore: false,
|
||
showTip1: false,
|
||
showTip2: false,
|
||
};
|
||
},
|
||
computed: {
|
||
loadMoreText() {
|
||
if (this.loadingMore) {
|
||
return "加载中...";
|
||
}
|
||
|
||
if (this.hasMore) {
|
||
return "加载更多";
|
||
}
|
||
|
||
return "暂无更多";
|
||
},
|
||
},
|
||
onLoad() {
|
||
this.loadPage(true, 1);
|
||
},
|
||
onReachBottom() {
|
||
this.loadMore();
|
||
},
|
||
methods: {
|
||
closeTips() {
|
||
this.showTip1 = false;
|
||
this.showTip2 = false;
|
||
},
|
||
toggleTip(key) {
|
||
if (this[key]) {
|
||
this[key] = false;
|
||
return;
|
||
}
|
||
|
||
this.showTip1 = false;
|
||
this.showTip2 = false;
|
||
this[key] = true;
|
||
},
|
||
mergeSections(currentSections, nextSections) {
|
||
const sectionMap = {};
|
||
const mergedSections = [];
|
||
const appendSection = (section) => {
|
||
if (!section || !section.key) {
|
||
return;
|
||
}
|
||
|
||
if (!sectionMap[section.key]) {
|
||
sectionMap[section.key] = {
|
||
key: section.key,
|
||
label: section.label,
|
||
income: section.income,
|
||
expense: section.expense,
|
||
items: [],
|
||
};
|
||
mergedSections.push(sectionMap[section.key]);
|
||
}
|
||
|
||
const targetSection = sectionMap[section.key];
|
||
targetSection.income = section.income || targetSection.income;
|
||
targetSection.expense = section.expense || targetSection.expense;
|
||
|
||
const itemMap = {};
|
||
targetSection.items.forEach((item) => {
|
||
itemMap[item.id] = true;
|
||
});
|
||
|
||
(section.items || []).forEach((item) => {
|
||
if (!item || itemMap[item.id]) {
|
||
return;
|
||
}
|
||
itemMap[item.id] = true;
|
||
targetSection.items.push(item);
|
||
});
|
||
};
|
||
|
||
(currentSections || []).forEach(appendSection);
|
||
(nextSections || []).forEach(appendSection);
|
||
|
||
return mergedSections;
|
||
},
|
||
changeTab(key) {
|
||
if (this.activeTab === key) {
|
||
return;
|
||
}
|
||
|
||
this.activeTab = key;
|
||
this.closeTips();
|
||
this.loadPage(true, 1);
|
||
},
|
||
async loadPage(showLoading, targetPage) {
|
||
const page = Number(targetPage || 1);
|
||
const isLoadMore = page > 1;
|
||
|
||
if (isLoadMore && (this.loadingMore || !this.hasMore)) {
|
||
return;
|
||
}
|
||
|
||
if (isLoadMore) {
|
||
this.loadingMore = true;
|
||
}
|
||
|
||
try {
|
||
const requestOptions =
|
||
!isLoadMore && showLoading
|
||
? {
|
||
showLoading: true,
|
||
loadingText: "加载中",
|
||
}
|
||
: null;
|
||
const result = await fetchVoucherCenterPage(
|
||
this.activeTab,
|
||
{
|
||
page: page,
|
||
pageSize: this.pageSize,
|
||
},
|
||
requestOptions,
|
||
);
|
||
const nextSections = Array.isArray(result.sections) ? result.sections : [];
|
||
|
||
this.summary = Object.assign({}, this.summary, result.summary || {});
|
||
this.sections = isLoadMore
|
||
? this.mergeSections(this.sections, nextSections)
|
||
: nextSections;
|
||
|
||
const pagination = result.pagination || {};
|
||
this.page = Number(pagination.page || page || 1);
|
||
this.hasMore = Boolean(pagination.hasMore);
|
||
} catch (error) {
|
||
uni.showToast({
|
||
title: error.message || (isLoadMore ? "加载更多失败" : "页面加载失败"),
|
||
icon: "none",
|
||
});
|
||
} finally {
|
||
if (isLoadMore) {
|
||
this.loadingMore = false;
|
||
}
|
||
}
|
||
},
|
||
loadMore() {
|
||
if (this.loadingMore || !this.hasMore) {
|
||
return;
|
||
}
|
||
|
||
this.loadPage(false, this.page + 1);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import "../../styles/tokens.scss";
|
||
@import "../../styles/common.scss";
|
||
|
||
.voucher-page {
|
||
min-height: 100vh;
|
||
background: #f5f5f5;
|
||
}
|
||
|
||
.voucher-scroll {
|
||
padding: 16rpx 12rpx calc(env(safe-area-inset-bottom) + 36rpx);
|
||
}
|
||
|
||
.voucher-header {
|
||
margin-top: 6rpx;
|
||
}
|
||
|
||
.voucher-header__bg {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 206rpx;
|
||
overflow: visible;
|
||
}
|
||
|
||
.voucher-header__bg-image {
|
||
width: 100%;
|
||
height: 206rpx;
|
||
border-radius: 24rpx;
|
||
}
|
||
|
||
.voucher-header__content {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
}
|
||
|
||
.voucher-header__current {
|
||
position: absolute;
|
||
bottom: 130rpx;
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: center;
|
||
width: 618rpx;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.voucher-header__current-label {
|
||
font-size: 36rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.voucher-header__current-value {
|
||
margin-left: 8rpx;
|
||
font-size: 48rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.voucher-header__totals {
|
||
position: absolute;
|
||
top: 100rpx;
|
||
left: 0;
|
||
right: 0;
|
||
display: flex;
|
||
justify-content: space-around;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.voucher-header__total-item {
|
||
position: relative;
|
||
text-align: center;
|
||
}
|
||
|
||
.voucher-header__total-value {
|
||
display: block;
|
||
font-size: 42rpx;
|
||
font-weight: 600;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.voucher-header__total-text {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 6rpx;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
.voucher-header__help-icon {
|
||
width: 30rpx;
|
||
margin-left: 10rpx;
|
||
}
|
||
|
||
.voucher-tooltip {
|
||
position: absolute;
|
||
top: calc(100% + 14rpx);
|
||
width: 360rpx;
|
||
max-width: calc(100vw - 48rpx);
|
||
padding: 16rpx 18rpx;
|
||
border-radius: 12rpx;
|
||
background: #333333;
|
||
color: #ffffff;
|
||
z-index: 30;
|
||
text-align: left;
|
||
box-shadow: 0 12rpx 24rpx rgba(0, 0, 0, 0.18);
|
||
}
|
||
|
||
.voucher-tooltip--left {
|
||
left: 0;
|
||
right: auto;
|
||
}
|
||
|
||
.voucher-tooltip--right {
|
||
right: 0;
|
||
left: auto;
|
||
}
|
||
|
||
.voucher-tooltip__row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.voucher-tooltip__text {
|
||
flex: 1;
|
||
min-width: 0;
|
||
font-size: 24rpx;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.voucher-tooltip__close {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
margin-left: 14rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.voucher-tooltip__close image {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
}
|
||
|
||
.voucher-tooltip__arrow {
|
||
position: absolute;
|
||
top: -14rpx;
|
||
right: 60rpx;
|
||
width: 0;
|
||
height: 0;
|
||
border-left: 12rpx solid transparent;
|
||
border-right: 12rpx solid transparent;
|
||
border-bottom: 16rpx solid #333333;
|
||
}
|
||
|
||
.voucher-tooltip__arrow--left {
|
||
left: 80rpx;
|
||
right: auto;
|
||
}
|
||
|
||
.voucher-wrapper {
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.voucher-nav {
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
|
||
.voucher-tabs {
|
||
display: flex;
|
||
width: 100%;
|
||
padding: 0 8rpx;
|
||
}
|
||
|
||
.voucher-tabs__item {
|
||
position: relative;
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 0;
|
||
padding: 8rpx 0 18rpx;
|
||
}
|
||
|
||
.voucher-tabs__text {
|
||
font-size: 30rpx;
|
||
color: #000;
|
||
}
|
||
|
||
.voucher-tabs__item.is-active .voucher-tabs__text {
|
||
color: #ec0208;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.voucher-tabs__line {
|
||
position: absolute;
|
||
bottom: 0;
|
||
width: 60rpx;
|
||
height: 16rpx;
|
||
}
|
||
|
||
.voucher-list {
|
||
width: 100%;
|
||
margin-top: 20rpx;
|
||
border-radius: 20rpx;
|
||
}
|
||
|
||
.voucher-card {
|
||
width: 100%;
|
||
margin-bottom: 20rpx;
|
||
padding: 34rpx 28rpx 20rpx;
|
||
border-radius: 22rpx;
|
||
background: #ffffff;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.voucher-card__head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-between;
|
||
margin-bottom: 20rpx;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.voucher-card__month-text {
|
||
font-size: 32rpx;
|
||
line-height: 44rpx;
|
||
color: #000000;
|
||
}
|
||
|
||
.voucher-card__totals {
|
||
flex-shrink: 0;
|
||
font-size: 28rpx;
|
||
line-height: 40rpx;
|
||
color: #000000;
|
||
}
|
||
|
||
.voucher-card__total-text + .voucher-card__total-text {
|
||
margin-top: 6rpx;
|
||
}
|
||
|
||
.voucher-card__total-income {
|
||
margin-left: 8rpx;
|
||
color: #fc2838;
|
||
}
|
||
|
||
.voucher-card__total-expense {
|
||
margin-left: 8rpx;
|
||
color: #40ae36;
|
||
}
|
||
|
||
.voucher-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
min-height: 115rpx;
|
||
padding: 10rpx 0;
|
||
color: #999999;
|
||
}
|
||
|
||
.voucher-row + .voucher-row {
|
||
border-top: 1px solid rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.voucher-row__left {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.voucher-row__icon {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
margin-right: 30rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.voucher-row__icon image {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
}
|
||
|
||
.voucher-row__body {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.voucher-row__title {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #000000;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.voucher-row__time {
|
||
display: block;
|
||
margin-top: 12rpx;
|
||
font-size: 24rpx;
|
||
color: #999999;
|
||
}
|
||
|
||
.voucher-row__amount {
|
||
font-size: 36rpx;
|
||
font-weight: 400;
|
||
line-height: 1;
|
||
}
|
||
|
||
.voucher-row__amount--income {
|
||
color: #e93323;
|
||
}
|
||
|
||
.voucher-row__amount--expense {
|
||
color: #16ac57;
|
||
}
|
||
|
||
.voucher-loading {
|
||
display: flex;
|
||
justify-content: center;
|
||
padding: 10rpx 0 0;
|
||
}
|
||
|
||
.voucher-loading__text {
|
||
font-size: 24rpx;
|
||
color: rgba(255, 255, 255, 0.72);
|
||
}
|
||
|
||
.voucher-empty {
|
||
padding: 72rpx 28rpx 120rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.voucher-empty__title {
|
||
display: block;
|
||
font-size: 30rpx;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.voucher-empty__desc {
|
||
display: block;
|
||
margin-top: 16rpx;
|
||
font-size: 24rpx;
|
||
line-height: 1.7;
|
||
color: rgba(255, 255, 255, 0.68);
|
||
}
|
||
</style>
|