5 changed files with 641 additions and 62 deletions
@ -0,0 +1,552 @@ |
|||
<template> |
|||
<view class="coupon-list-container"> |
|||
<!-- 状态筛选 --> |
|||
<view class="filter-tabs"> |
|||
<view |
|||
class="tab-item" |
|||
:class="[{ active: activeTab === 'all' }]" |
|||
@click="switchTab('all')" |
|||
> |
|||
全部 |
|||
</view> |
|||
<view |
|||
class="tab-item" |
|||
:class="[{ active: activeTab === 'unused' }]" |
|||
@click="switchTab('unused')" |
|||
> |
|||
未使用 |
|||
</view> |
|||
<view |
|||
class="tab-item" |
|||
:class="[{ active: activeTab === 'used' }]" |
|||
@click="switchTab('used')" |
|||
> |
|||
已使用 |
|||
</view> |
|||
<view |
|||
class="tab-item" |
|||
:class="[{ active: activeTab === 'expired' }]" |
|||
@click="switchTab('expired')" |
|||
> |
|||
已过期 |
|||
</view> |
|||
</view> |
|||
<!-- 优惠券列表 --> |
|||
<view class="coupon-list"> |
|||
<view |
|||
v-for="(coupon, index) in filteredCoupons" |
|||
:key="index" |
|||
class="coupon-item" |
|||
:class="[ |
|||
{ |
|||
used: coupon.status === 1, |
|||
expired: coupon.status === 2, |
|||
}, |
|||
]" |
|||
> |
|||
<!-- 优惠券主体 --> |
|||
<view class="coupon-main"> |
|||
<!-- 左侧金额区域 --> |
|||
<view class="coupon-amount"> |
|||
<view class="amount-value"> |
|||
<text class="currency">¥</text> |
|||
<text class="value">{{ formatAmount(coupon) }}</text> |
|||
</view> |
|||
<view class="amount-desc" v-if="coupon.minAmount > 0"> |
|||
满{{ coupon.minAmount }}可用 |
|||
</view> |
|||
<view class="amount-desc" v-else> 无门槛 </view> |
|||
</view> |
|||
|
|||
<!-- 右侧信息区域 --> |
|||
<view class="coupon-info"> |
|||
<view class="coupon-type">{{ |
|||
getCouponTypeName(coupon.type) |
|||
}}</view> |
|||
<view class="coupon-scope" v-if="coupon.scope === 0"> |
|||
全场通用 |
|||
</view> |
|||
<view class="coupon-scope" v-else> |
|||
<view class="scope-title" @click="toggleProductList(coupon.id)"> |
|||
指定商品可用 |
|||
<text |
|||
class="expand-icon" |
|||
:class="{ expanded: coupon.showProducts }" |
|||
>▼</text |
|||
> |
|||
</view> |
|||
<view class="product-list" v-if="coupon.showProducts"> |
|||
<view |
|||
class="product-item" |
|||
v-for="product in coupon.products" |
|||
:key="product.id" |
|||
> |
|||
{{ product.name }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 状态标识 --> |
|||
<view class="coupon-status"> |
|||
<view class="status-tag" :class="[getStatusClass(coupon.status)]"> |
|||
{{ getStatusText(coupon.status) }} |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 有效期和使用按钮 --> |
|||
<view class="coupon-bottom"> |
|||
<view class="coupon-validity"> |
|||
<text class="validity-label">有效期:</text> |
|||
<text class="validity-date" |
|||
>{{ coupon.startDate }} - {{ coupon.endDate }}</text |
|||
> |
|||
</view> |
|||
<view class="coupon-action" v-if="coupon.status === 0"> |
|||
<button class="use-btn" @click="useCoupon(coupon)">立即使用</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 空状态 --> |
|||
<view class="empty-state" v-if="filteredCoupons.length === 0"> |
|||
<view class="empty-icon">📋</view> |
|||
<view class="empty-text">暂无优惠券</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
data() { |
|||
return { |
|||
activeTab: "all", |
|||
coupons: [ |
|||
{ |
|||
id: 1, |
|||
type: 1, // 满减券 |
|||
amount: 20, |
|||
minAmount: 100, |
|||
status: 0, // 0:未使用 1:已使用 2:已过期 |
|||
startDate: "2024-01-01", |
|||
endDate: "2024-12-31", |
|||
scope: 0, // 0:不限商品 1:指定商品 |
|||
showProducts: false, |
|||
}, |
|||
{ |
|||
id: 2, |
|||
type: 2, // 折扣券 |
|||
discount: 8.5, // 8.5折 |
|||
minAmount: 50, |
|||
status: 1, |
|||
startDate: "2024-01-01", |
|||
endDate: "2024-06-30", |
|||
scope: 1, |
|||
showProducts: false, |
|||
products: [ |
|||
{ id: 1, name: "iPhone 15 Pro" }, |
|||
{ id: 2, name: "MacBook Air M2" }, |
|||
{ id: 3, name: "AirPods Pro" }, |
|||
], |
|||
}, |
|||
{ |
|||
id: 3, |
|||
type: 3, // 立减券 |
|||
amount: 10, |
|||
minAmount: 0, |
|||
status: 2, |
|||
startDate: "2023-12-01", |
|||
endDate: "2023-12-31", |
|||
scope: 0, |
|||
showProducts: false, |
|||
}, |
|||
{ |
|||
id: 4, |
|||
type: 4, // 兑换券 |
|||
amount: 0, |
|||
minAmount: 0, |
|||
status: 0, |
|||
startDate: "2024-01-01", |
|||
endDate: "2024-12-31", |
|||
scope: 1, |
|||
showProducts: false, |
|||
products: [ |
|||
{ id: 4, name: "星巴克咖啡券" }, |
|||
{ id: 5, name: "肯德基套餐券" }, |
|||
], |
|||
}, |
|||
], |
|||
}; |
|||
}, |
|||
computed: { |
|||
filteredCoupons() { |
|||
if (this.activeTab === "all") { |
|||
return this.coupons; |
|||
} |
|||
if (this.activeTab === "unused") { |
|||
return this.coupons.filter((coupon) => coupon.status === 0); |
|||
} |
|||
if (this.activeTab === "used") { |
|||
return this.coupons.filter((coupon) => coupon.status === 1); |
|||
} |
|||
if (this.activeTab === "expired") { |
|||
return this.coupons.filter((coupon) => coupon.status === 2); |
|||
} |
|||
return []; |
|||
}, |
|||
}, |
|||
mounted() { |
|||
console.log("couponList mounted"); |
|||
console.log("activeTab:", this.activeTab); |
|||
console.log("coupons:", this.coupons); |
|||
console.log("filteredCoupons:", this.filteredCoupons); |
|||
}, |
|||
methods: { |
|||
switchTab(tab) { |
|||
this.activeTab = tab; |
|||
}, |
|||
getCouponTypeName(type) { |
|||
const typeMap = { |
|||
1: "满减券", |
|||
2: "折扣券", |
|||
3: "立减券", |
|||
4: "兑换券", |
|||
}; |
|||
return typeMap[type] || "未知类型"; |
|||
}, |
|||
formatAmount(coupon) { |
|||
if (coupon.type === 2) { |
|||
// 折扣券显示折扣 |
|||
return coupon.discount + "折"; |
|||
} else if (coupon.type === 4) { |
|||
// 兑换券 |
|||
return "兑换"; |
|||
} else { |
|||
// 满减券和立减券显示金额 |
|||
return coupon.amount; |
|||
} |
|||
}, |
|||
getStatusText(status) { |
|||
const statusMap = { |
|||
0: "未使用", |
|||
1: "已使用", |
|||
2: "已过期", |
|||
}; |
|||
return statusMap[status] || "未知状态"; |
|||
}, |
|||
getStatusClass(status) { |
|||
const classMap = { |
|||
0: "unused", |
|||
1: "used", |
|||
2: "expired", |
|||
}; |
|||
return classMap[status] || ""; |
|||
}, |
|||
useCoupon(coupon) { |
|||
// 跳转到商品页面或购物车 |
|||
uni.showToast({ |
|||
title: "跳转到商品页面", |
|||
icon: "none", |
|||
}); |
|||
}, |
|||
toggleProductList(couponId) { |
|||
console.log("toggleProductList called with couponId:", couponId); |
|||
const couponIndex = this.coupons.findIndex((c) => c.id === couponId); |
|||
console.log("found coupon index:", couponIndex); |
|||
if (couponIndex !== -1) { |
|||
const newShowProducts = !this.coupons[couponIndex].showProducts; |
|||
console.log( |
|||
"toggling showProducts from", |
|||
this.coupons[couponIndex].showProducts, |
|||
"to", |
|||
newShowProducts |
|||
); |
|||
// 使用 Vue.set 确保响应式更新 |
|||
this.$set(this.coupons[couponIndex], "showProducts", newShowProducts); |
|||
console.log( |
|||
"after toggle, showProducts is:", |
|||
this.coupons[couponIndex].showProducts |
|||
); |
|||
} |
|||
}, |
|||
}, |
|||
onLoad() { |
|||
// 页面加载时获取优惠券数据 |
|||
// this.loadCoupons(); |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.coupon-list-container { |
|||
min-height: 100vh; |
|||
background-color: #f5f5f5; |
|||
} |
|||
|
|||
.header { |
|||
background: #fff; |
|||
padding: 20rpx 30rpx; |
|||
border-bottom: 1rpx solid #eee; |
|||
|
|||
.header-title { |
|||
font-size: 36rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
text-align: center; |
|||
} |
|||
} |
|||
|
|||
.filter-tabs { |
|||
display: flex; |
|||
background: #fff; |
|||
padding: 0 30rpx; |
|||
border-bottom: 1rpx solid #eee; |
|||
|
|||
.tab-item { |
|||
flex: 1; |
|||
padding: 30rpx 0; |
|||
text-align: center; |
|||
font-size: 28rpx; |
|||
color: #666; |
|||
position: relative; |
|||
|
|||
&.active { |
|||
color: #77f3f9; |
|||
font-weight: 600; |
|||
|
|||
&::after { |
|||
content: ""; |
|||
position: absolute; |
|||
bottom: 0; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
width: 60rpx; |
|||
height: 4rpx; |
|||
background: #77f3f9; |
|||
border-radius: 2rpx; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.coupon-list { |
|||
padding: 20rpx 30rpx; |
|||
} |
|||
|
|||
.coupon-item { |
|||
background: #fff; |
|||
border-radius: 16rpx; |
|||
margin-bottom: 20rpx; |
|||
overflow: hidden; |
|||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1); |
|||
position: relative; |
|||
|
|||
&.used, |
|||
&.expired { |
|||
opacity: 0.6; |
|||
|
|||
&::after { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.1); |
|||
pointer-events: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.coupon-main { |
|||
display: flex; |
|||
padding: 30rpx; |
|||
position: relative; |
|||
|
|||
&::after { |
|||
content: ""; |
|||
position: absolute; |
|||
right: 200rpx; |
|||
top: 20rpx; |
|||
bottom: 20rpx; |
|||
width: 2rpx; |
|||
background: linear-gradient( |
|||
to bottom, |
|||
transparent 0%, |
|||
#ddd 20%, |
|||
#ddd 80%, |
|||
transparent 100% |
|||
); |
|||
} |
|||
} |
|||
|
|||
.coupon-amount { |
|||
width: 200rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
.amount-value { |
|||
display: flex; |
|||
align-items: baseline; |
|||
margin-bottom: 10rpx; |
|||
|
|||
.currency { |
|||
font-size: 24rpx; |
|||
color: #ff4d4f; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.value { |
|||
font-size: 48rpx; |
|||
color: #ff4d4f; |
|||
font-weight: 700; |
|||
margin-left: 4rpx; |
|||
} |
|||
} |
|||
|
|||
.amount-desc { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
|
|||
.coupon-info { |
|||
flex: 1; |
|||
padding-left: 30rpx; |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
|
|||
.coupon-type { |
|||
font-size: 32rpx; |
|||
color: #333; |
|||
font-weight: 600; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.coupon-scope { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.coupon-scope { |
|||
.scope-title { |
|||
display: flex; |
|||
align-items: center; |
|||
cursor: pointer; |
|||
|
|||
.expand-icon { |
|||
margin-left: 8rpx; |
|||
font-size: 20rpx; |
|||
transition: transform 0.3s ease; |
|||
|
|||
&.expanded { |
|||
transform: rotate(180deg); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.product-list { |
|||
margin-top: 8rpx; |
|||
padding: 8rpx 12rpx; |
|||
background: #f8f8f8; |
|||
border-radius: 8rpx; |
|||
|
|||
.product-item { |
|||
font-size: 20rpx; |
|||
color: #666; |
|||
line-height: 1.5; |
|||
margin-bottom: 4rpx; |
|||
|
|||
&:last-child { |
|||
margin-bottom: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.coupon-status { |
|||
position: absolute; |
|||
top: 20rpx; |
|||
right: 20rpx; |
|||
|
|||
.status-tag { |
|||
padding: 8rpx 16rpx; |
|||
border-radius: 20rpx; |
|||
font-size: 20rpx; |
|||
color: #fff; |
|||
|
|||
&.unused { |
|||
background: #52c41a; |
|||
} |
|||
|
|||
&.used { |
|||
background: #999; |
|||
} |
|||
|
|||
&.expired { |
|||
background: #ff4d4f; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.coupon-bottom { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 20rpx 30rpx; |
|||
border-top: 1rpx solid #f0f0f0; |
|||
|
|||
.coupon-validity { |
|||
flex: 1; |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
|
|||
.validity-label { |
|||
margin-right: 8rpx; |
|||
} |
|||
|
|||
.validity-date { |
|||
line-height: 1.4; |
|||
} |
|||
} |
|||
|
|||
.coupon-action { |
|||
.use-btn { |
|||
width: 160rpx; |
|||
height: 60rpx; |
|||
background: linear-gradient(135deg, #fffdb7 0%, #97fffa 100%); |
|||
color: #000; |
|||
border: none; |
|||
border-radius: 20rpx; |
|||
font-size: 24rpx; |
|||
font-weight: 600; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.empty-state { |
|||
display: flex; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
justify-content: center; |
|||
padding: 120rpx 0; |
|||
|
|||
.empty-icon { |
|||
font-size: 120rpx; |
|||
margin-bottom: 30rpx; |
|||
opacity: 0.3; |
|||
} |
|||
|
|||
.empty-text { |
|||
font-size: 28rpx; |
|||
color: #999; |
|||
} |
|||
} |
|||
</style> |
Loading…
Reference in new issue