consumer-app/pages/profileSub/myCoupons.vue

394 lines
9.3 KiB
Vue
Raw Normal View History

2026-03-21 13:38:13 +00:00
<template>
<view class="my-coupons-page">
<view class="header-fixed-wrapper" :style="{ height: headerHeight + 'px' }">
<NavHeader title="我的优惠卷" />
</view>
<view class="main-wrap" :style="{ paddingTop: headerHeight + 'px' }">
<!-- Tab 切换 -->
<view class="tab-bar">
<view
class="tab-item"
:class="{ active: currentTab === '0' }"
@click="switchTab('0')"
>
<text class="tab-text">待使用</text>
</view>
<view
class="tab-item"
:class="{ active: currentTab === '1' }"
@click="switchTab('1')"
>
<text class="tab-text">已使用</text>
</view>
<view
class="tab-item"
:class="{ active: currentTab === '2' }"
@click="switchTab('2')"
>
<text class="tab-text">已过期</text>
</view>
<view
class="tab-item"
:class="{ active: currentTab === 'all' }"
@click="switchTab('all')"
>
<text class="tab-text">全部</text>
</view>
</view>
<!-- 列表内容 -->
<scroll-view
class="coupon-list"
scroll-y="true"
:refresher-enabled="true"
:refresher-triggered="refreshing"
@refresherrefresh="handleRefresh"
@scrolltolower="handleLoadMore"
:lower-threshold="100"
>
<!-- 空数据提示 -->
<view class="empty-state" v-if="!loading && coupons.length === 0">
<image
class="empty-icon"
src="/static/home/entry_icon.png"
mode="aspectFit"
></image>
<text class="empty-text">暂无优惠卷</text>
</view>
<view
class="coupon-item"
v-for="(item, index) in coupons"
:key="index"
:class="{ disabled: isCouponDisabled(item) }"
2026-03-21 14:15:37 +00:00
@click="goToDetail(item)"
2026-03-21 13:38:13 +00:00
>
<view class="coupon-left">
<!-- 折扣类 -->
<view class="coupon-price" v-if="item.type === 2">
<text class="amount">{{ (item.discountPercent / 10).toFixed(1).replace(/\.0$/, '') }}</text>
<text class="symbol" style="font-size: 24rpx; margin-left: 4rpx;"></text>
</view>
<!-- 金额类 -->
<view class="coupon-price" v-else>
<text class="symbol">¥</text>
<text class="amount">{{ formatAmount(item.discountAmount || item.discountPrice || item.price || 0) }}</text>
</view>
<view class="coupon-condition" v-if="item.usePrice">
{{ formatAmount(item.usePrice) }}可用
</view>
<view class="coupon-condition" v-else>
无门槛
</view>
</view>
<view class="coupon-right">
<view class="coupon-name">{{ item.name }}</view>
<view class="coupon-time" v-if="item.type === 2 && item.discountLimit > 0" style="margin-bottom: 6rpx;">: ¥{{ formatAmount(item.discountLimit) }}</view>
<view class="coupon-time" v-if="item.validEndTime">: {{ formatTimeStr(item.validEndTime) }}</view>
</view>
<view class="coupon-status-stamp" v-if="item.status === 1">使</view>
<view class="coupon-status-stamp" v-else-if="item.status === 2">已过期</view>
<view class="coupon-status-stamp" v-else-if="item.status === 3">已作废</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import NavHeader from "@/components/NavHeader/NavHeader.vue";
import { getLuCouponPage } from "@/api/service.js";
import { formatTime } from "@/utils/date.js";
export default {
components: {
NavHeader,
},
data() {
return {
statusBarHeight: 0,
currentTab: '0', // 默认待使用
coupons: [],
pageNo: 1,
pageSize: 10,
total: 0,
loading: false,
refreshing: false,
hasMore: true,
};
},
computed: {
headerHeight() {
return this.statusBarHeight + 44;
},
},
onLoad() {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight || 0;
this.loadData();
},
methods: {
switchTab(tab) {
if (this.currentTab === tab) return;
this.currentTab = tab;
this.refreshData();
},
async loadData() {
if (this.loading || !this.hasMore) return;
this.loading = true;
try {
const params = {
pageNo: this.pageNo,
pageSize: this.pageSize
};
if (this.currentTab !== 'all') {
params.status = Number(this.currentTab);
}
const res = await getLuCouponPage(params);
if (res && res.list) {
if (this.pageNo === 1) {
this.coupons = res.list;
} else {
this.coupons = this.coupons.concat(res.list);
}
this.total = res.total || 0;
this.hasMore = this.coupons.length < this.total;
this.pageNo++;
} else {
this.hasMore = false;
}
} catch (error) {
console.error("加载优惠卷失败:", error);
uni.showToast({
title: "加载失败",
icon: "none"
});
} finally {
this.loading = false;
this.refreshing = false;
}
},
refreshData() {
this.pageNo = 1;
this.hasMore = true;
this.coupons = [];
this.loadData();
},
handleRefresh() {
if (this.refreshing) return;
this.refreshing = true;
this.pageNo = 1;
this.hasMore = true;
this.loadData();
},
handleLoadMore() {
this.loadData();
},
2026-03-21 14:15:37 +00:00
goToDetail(item) {
uni.navigateTo({
url: `/pages/profileSub/couponDetail?id=${item.id}`
});
},
2026-03-21 13:38:13 +00:00
isCouponDisabled(item) {
return item.status === 1 || item.status === 2 || item.status === 3;
},
formatTimeStr(timestamp) {
if (!timestamp) return '';
return formatTime(timestamp, 'YYYY-MM-DD HH:mm:ss');
},
// 将分转换为元,并处理小数显示
formatAmount(amount) {
if (!amount) return '0';
const yuan = amount / 100;
return Number.isInteger(yuan) ? yuan.toString() : yuan.toFixed(2).replace(/\.?0+$/, '');
}
}
}
</script>
<style lang="scss" scoped>
.my-coupons-page {
min-height: 100vh;
background-color: #f5f5f5;
}
.header-fixed-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 100;
background-color: #fff;
}
.main-wrap {
display: flex;
flex-direction: column;
height: 100vh;
box-sizing: border-box;
}
.tab-bar {
display: flex;
background-color: #fff;
height: 88rpx;
border-bottom: 1rpx solid #f0f0f0;
.tab-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
position: relative;
.tab-text {
font-size: 28rpx;
color: #666;
}
&.active {
.tab-text {
color: #d51c3c;
font-weight: 500;
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 6rpx;
background-color: #d51c3c;
border-radius: 4rpx;
}
}
}
}
.coupon-list {
flex: 1;
padding: 20rpx;
box-sizing: border-box;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 200rpx;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
opacity: 0.5;
}
.empty-text {
font-size: 28rpx;
color: #999;
}
}
.coupon-item {
position: relative;
display: flex;
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
padding: 30rpx;
align-items: center;
overflow: hidden;
&.disabled {
opacity: 0.6;
background-color: #fafafa;
.coupon-price {
color: #999 !important;
}
.coupon-name {
color: #999 !important;
}
}
.coupon-left {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160rpx;
border-right: 2rpx dashed #eee;
padding-right: 20rpx;
.coupon-price {
color: #d51c3c;
.symbol {
font-size: 24rpx;
font-weight: bold;
}
.amount {
font-size: 48rpx;
font-weight: bold;
}
}
.coupon-condition {
font-size: 20rpx;
color: #666;
margin-top: 10rpx;
}
}
.coupon-right {
flex: 1;
padding-left: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
.coupon-name {
font-size: 32rpx;
color: #333;
font-weight: 500;
margin-bottom: 16rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.coupon-time {
font-size: 22rpx;
color: #999;
}
}
.coupon-status-stamp {
position: absolute;
right: -20rpx;
top: 20rpx;
width: 120rpx;
height: 40rpx;
line-height: 40rpx;
text-align: center;
background-color: rgba(153, 153, 153, 0.1);
color: #999;
font-size: 20rpx;
transform: rotate(45deg);
transform-origin: center;
border: 1rpx solid #999;
}
}
</style>