You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

422 lines
9.4 KiB

<template>
<view class="coupon-page">
<view class="title-wrap">
<view class="title-search">
<view class="back-btn" @tap="back"
:style="{'top': menuButtonInfo.top + 'px','width': menuButtonInfo.height + 'px','height': menuButtonInfo.height + 'px'}">
<uni-icons type="left" size="28" color="#08735d"></uni-icons>
</view>
<view class="title-name" :style="{'padding-top': menuButtonInfo.top + 'px'}">
我的优惠券
</view>
</view>
<view class="hero-copy">
<view class="hero-title">可用优惠券</view>
<view class="hero-sub">仅展示当前校区未使用未锁定且未过期的优惠券</view>
</view>
</view>
<view class="content">
<view class="summary-card">
<view class="summary-left">
<view class="summary-label">当前校区</view>
<view class="summary-name">{{regionName || '未选择校区'}}</view>
</view>
<view class="summary-count">{{totalCount}} 张可用</view>
</view>
<view class="empty-card" v-if="couponList.length === 0 && loadStatus !== 'loading'">
<view class="empty-icon">券</view>
<view class="empty-title">暂无可用优惠券</view>
<view class="empty-sub">已使用、锁定和过期的优惠券不会显示在这里</view>
</view>
<view class="coupon-card" v-for="item in couponList" :key="item.id">
<view class="coupon-main">
<view class="amount-box">
<view class="amount"><text>¥</text>{{formatAmount(item.discountAmount)}}</view>
<view class="threshold">{{thresholdText(item.minAmount)}}</view>
</view>
<view class="coupon-info">
<view class="coupon-title">{{sceneText(item.applyScene)}}优惠券</view>
<view class="tag-row">
<text>{{issuerText(item)}}</text>
<text>未使用</text>
</view>
<view class="info-row">领取时间:{{formatDate(item.receiveTime)}}</view>
<view class="info-row">有效期至{{formatDate(item.validEndTime)}}</view>
</view>
</view>
</view>
<uni-load-more :status="loadStatus" v-if="couponList.length > 0 || loadStatus === 'loading'" />
</view>
</view>
</template>
<script>
export default {
data() {
return {
menuButtonInfo: {
top: 0,
height: 32
},
regionName: '',
totalCount: 0,
totalPages: 1,
loadStatus: 'more',
searchForm: {
pageNum: 1,
pageSize: 10,
userId: '',
regionId: ''
},
couponList: []
}
},
onReachBottom() {
if (this.loadStatus === 'loading' || this.searchForm.pageNum >= this.totalPages) return
this.searchForm.pageNum += 1
this.getCouponList()
},
onLoad(option) {
this.initQuery()
},
onShow() {
this.menuButtonInfo = uni.getMenuButtonBoundingClientRect()
},
methods: {
back() {
uni.navigateBack()
},
initQuery() {
const area = uni.getStorageSync('area')
this.searchForm.userId = uni.getStorageSync('id') || ''
if (area) {
try {
const areaInfo = typeof area === 'string' ? JSON.parse(area) : area
this.searchForm.regionId = areaInfo.id || ''
this.regionName = areaInfo.title || areaInfo.name || ''
} catch (e) {
this.searchForm.regionId = ''
this.regionName = ''
}
}
if (!this.searchForm.userId) {
this.tui.toast('请先登录')
return
}
if (!this.searchForm.regionId) {
this.tui.toast('请先选择校区')
return
}
this.getCouponList()
},
getCouponList() {
this.loadStatus = 'loading'
this.tui.request('/mall/coupon/userList', 'POST', this.searchForm, false, false).then((res) => {
if (res.code == 200) {
const result = res.result || {}
const records = Array.isArray(result.records) ? result.records : []
if (this.searchForm.pageNum == 1) {
this.couponList = records
} else {
this.couponList = [...this.couponList, ...records]
}
this.totalCount = result.total || this.couponList.length
this.totalPages = result.pages || 1
this.loadStatus = this.searchForm.pageNum >= this.totalPages ? 'nomore' : 'more'
} else {
this.loadStatus = 'nomore'
this.tui.toast(res.message)
}
}).catch(() => {
this.loadStatus = 'nomore'
this.tui.toast('优惠券加载失败')
})
},
formatAmount(value) {
const num = Number(value || 0)
if (Number.isNaN(num)) return '0'
return num % 1 === 0 ? String(num) : num.toFixed(2)
},
thresholdText(value) {
const amount = Number(value || 0)
if (!amount) return '无门槛'
return '满' + this.formatAmount(amount) + '可用'
},
sceneText(value) {
const sceneMap = {
0: '通用',
1: '外卖/买饭',
2: '快递/跑腿',
3: '二手交易',
4: '团购'
}
return sceneMap[value] || '通用'
},
issuerText(item) {
if (item.issuerType == 1) return '平台发放'
if (item.issuerType == 2) return '商家:' + (item.issuerId || '-')
return '发放方:' + (item.issuerId || '-')
},
formatDate(value) {
if (!value) return '-'
const text = String(value)
const match = text.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}))?/)
if (match) {
return `${match[1]}-${match[2]}-${match[3]} ${match[4] || '00'}:${match[5] || '00'}`
}
const date = new Date(text)
if (Number.isNaN(date.getTime())) return text
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
const day = String(date.getDate()).padStart(2, '0')
const hour = String(date.getHours()).padStart(2, '0')
const minute = String(date.getMinutes()).padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}`
}
}
}
</script>
<style>
page {
width: 100%;
min-height: 100%;
background: #f4f8f5;
color: #00231C;
font-size: 24rpx;
}
.coupon-page {
width: 100%;
min-height: 100vh;
padding-bottom: 40rpx;
box-sizing: border-box;
}
.title-wrap {
background: url('https://jewel-shop.oss-cn-beijing.aliyuncs.com/8bc15960c2dc40268e295d6dd23aecce.png') no-repeat;
background-size: 100%;
width: 100%;
height: 390rpx;
position: relative;
}
.title-search {
width: 100%;
height: 160rpx;
position: relative;
}
.back-btn {
position: absolute;
left: 18rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
}
.title-name {
font-size: 36rpx;
font-weight: 700;
text-align: center;
color: #063d32;
}
.hero-copy {
padding: 20rpx 48rpx 0;
}
.hero-title {
font-size: 48rpx;
font-weight: 800;
color: #063d32;
line-height: 1.3;
}
.hero-sub {
margin-top: 14rpx;
font-size: 26rpx;
color: rgba(0, 35, 28, 0.62);
}
.content {
width: 92%;
margin: -48rpx auto 0;
position: relative;
z-index: 1;
}
.summary-card {
width: 100%;
min-height: 112rpx;
background: #ffffff;
border-radius: 28rpx;
box-shadow: 0 18rpx 42rpx rgba(8, 115, 93, 0.08);
padding: 20rpx 28rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24rpx;
}
.summary-label {
font-size: 22rpx;
color: rgba(0, 35, 28, 0.48);
}
.summary-name {
margin-top: 4rpx;
font-size: 32rpx;
font-weight: 800;
color: #063d32;
}
.summary-count {
padding: 12rpx 22rpx;
border-radius: 999rpx;
background: linear-gradient(90deg, #e3ff96, #a6ffea);
color: #063d32;
font-size: 24rpx;
font-weight: 700;
}
.coupon-card {
width: 100%;
background: #fff;
border-radius: 24rpx;
margin-bottom: 18rpx;
overflow: hidden;
box-shadow: 0 12rpx 30rpx rgba(8, 115, 93, 0.06);
position: relative;
}
.coupon-card::before,
.coupon-card::after {
content: '';
position: absolute;
top: 106rpx;
width: 24rpx;
height: 24rpx;
border-radius: 50%;
background: #f4f8f5;
z-index: 2;
}
.coupon-card::before {
left: -12rpx;
}
.coupon-card::after {
right: -12rpx;
}
.coupon-main {
display: flex;
min-height: 168rpx;
}
.amount-box {
width: 214rpx;
background: linear-gradient(160deg, #08735d, #0fb894);
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20rpx 14rpx;
box-sizing: border-box;
}
.amount {
font-size: 54rpx;
font-weight: 900;
line-height: 1;
}
.amount text {
font-size: 28rpx;
margin-right: 4rpx;
}
.threshold {
margin-top: 10rpx;
font-size: 23rpx;
color: rgba(255, 255, 255, 0.86);
}
.coupon-info {
flex: 1;
padding: 22rpx 24rpx 18rpx;
box-sizing: border-box;
min-width: 0;
}
.coupon-title {
font-size: 30rpx;
font-weight: 800;
color: #063d32;
}
.tag-row {
display: flex;
flex-wrap: wrap;
margin: 10rpx 0 8rpx;
}
.tag-row text {
margin-right: 12rpx;
margin-bottom: 6rpx;
padding: 4rpx 12rpx;
border-radius: 999rpx;
background: rgba(8, 115, 93, 0.08);
color: #08735d;
font-size: 22rpx;
}
.info-row {
font-size: 22rpx;
line-height: 1.55;
color: rgba(0, 35, 28, 0.62);
word-break: break-all;
}
.empty-card {
width: 100%;
background: #fff;
border-radius: 28rpx;
padding: 70rpx 30rpx;
box-sizing: border-box;
text-align: center;
box-shadow: 0 12rpx 30rpx rgba(8, 115, 93, 0.06);
}
.empty-icon {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
margin: 0 auto 24rpx;
background: linear-gradient(160deg, #e3ff96, #a6ffea);
color: #08735d;
line-height: 96rpx;
font-size: 40rpx;
font-weight: 800;
}
.empty-title {
font-size: 32rpx;
font-weight: 800;
color: #063d32;
}
.empty-sub {
margin-top: 12rpx;
font-size: 24rpx;
color: rgba(0, 35, 28, 0.5);
}
</style>