Files
frontend-app/pages/other_package/get-coupon/get-coupon.vue
T
2026-08-21 14:10:43 +08:00

802 lines
27 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="get-coupon-page">
<!-- 头部 Banner / 轮播图区域 -->
<view class="product_info_header_warp">
<up-swiper v-if="swiperList && swiperList.length > 0" :list="swiperList" height="660rpx"
class="product_info_header_swiper" @click="onSwiper" @change="(e) => (currentNum = e.current)">
</up-swiper>
<!-- Default Fallback Banner UI (High-Fidelity) when swiperList is empty -->
<view v-else class="default-banner">
<view class="banner-bg-gradient"></view>
<view class="banner-content">
<view class="banner-badge">%</view>
<text class="banner-title">领券中心</text>
<view class="banner-subtitle">
<text class="sparkle">✦</text>
<text class="sub-text">领券享好价,购物更划算</text>
<text class="sparkle">✦</text>
</view>
</view>
</view>
<!-- 浮动返回按钮 -->
<view class="img_comm left_warp" :style="{ top: (statusBarHeight + 10) + 'px' }">
<up-image src="/static/common/left_b_st.png" width="30" height="30" bgColor="#f1f6ff00"
@click="jumpLeft"></up-image>
</view>
</view>
<!-- 分页优惠券列表区域 -->
<mescroll-uni ref="mescrollRef" top="660rpx" @down="downCallback" @up="upCallback" :up="upOption"
:down="downOption" @init="mescrollInit">
<!-- 列表视图 -->
<view class="coupon-list-container" v-if="couponList && couponList.length > 0">
<view v-for="(coupon, index) in couponList" :key="coupon.templateId || coupon.id || index"
class="coupon-card">
<!-- 左侧:金额与门槛 -->
<view class="card-left">
<view class="price-box">
<text class="currency">¥</text>
<text class="amount">{{ getDiscountAmount(coupon) }}</text>
</view>
<text class="condition">{{ getThresholdText(coupon) }}</text>
</view>
<!-- 带有上下半圆凹槽的虚线分割线 -->
<view class="divider-wrapper">
<view class="notch top-notch"></view>
<view class="divider-line"></view>
<view class="notch bottom-notch"></view>
</view>
<!-- 右侧:详细内容与操作按钮 -->
<view class="card-right">
<view class="info-content">
<view class="coupon-title">{{ coupon.title }}</view>
<view class="tag-row">
<text class="coupon-tag">{{ getTagText(coupon) }}</text>
<text class="coupon-scope">{{ getScopeText(coupon) }}</text>
</view>
<view class="coupon-validity">{{ getValidityText(coupon) }}</view>
</view>
<!-- 操作按钮 -->
<view class="btn-box">
<view v-if="getCouponStatus(coupon) === 0" class="action-btn btn-claim"
@click="onClaimCoupon(coupon, index)">
立即领取
</view>
<view v-else-if="getCouponStatus(coupon) === 1" class="action-btn btn-use"
@click="onClaimCoupon(coupon, index)">
去使用
</view>
<view v-else class="action-btn btn-disabled">
{{ coupon.receiveStatusText || '已达上限' }}
</view>
</view>
</view>
</view>
</view>
<!-- 无领券数据缺省视图 (高保真) -->
<view class="empty-container" v-else-if="isEmpty">
<view class="empty-graphic">
<view class="purple-bag-wrapper">
<view class="glow-bg"></view>
<view class="bag-card">
<view class="bag-handle"></view>
<view class="bag-logo">TB</view>
</view>
<view class="heart-bubble">
<text class="heart-icon">♥</text>
</view>
</view>
</view>
<text class="empty-title">敬请期待</text>
<text class="empty-subtitle">更多优质商品正在路上...</text>
</view>
</mescroll-uni>
<up-toast ref="uToastRef"></up-toast>
<qiaobao-assistant page-key="pages/other_package/get-coupon/get-coupon" title="领券中心" />
</view>
</template>
<script>
import { getCarouselImage } from '@/api/common.js';
import { getReceiveCenterList, receiveCoupon } from '@/api/coupon.js';
import { APP_PAGE_TYPE } from '@/utils/enumUtils.js';
import MescrollMixin from '@/components/mescroll-uni/mescroll-mixins.js';
import MescrollUni from '@/components/mescroll-uni/mescroll-uni.vue';
export default {
mixins: [MescrollMixin],
components: { MescrollUni },
data() {
return {
swiperList: [],
currentNum: 0,
statusBarHeight: 20,
couponList: [],
isEmpty: false,
upOption: {
auto: true,
page: {
num: 0,
size: 10
},
noMoreSize: 5,
empty: {
use: false // 使用自定义高保真 Empty 视图
},
textColor: '#333',
bgColor: 'rgba(0,0,0,0)'
},
downOption: {
auto: false,
textColor: '#333',
bgColor: 'rgba(0,0,0,0)'
},
mescroll: null
};
},
onLoad() {
const sysInfo = uni.getSystemInfoSync();
if (sysInfo && sysInfo.statusBarHeight) {
this.statusBarHeight = sysInfo.statusBarHeight;
}
this.getCarouselImage();
},
methods: {
mescrollInit(mescroll) {
this.mescroll = mescroll;
},
downCallback() {
this.mescroll && this.mescroll.resetUpScroll();
},
// 分页获取领券中心列表
async upCallback(page) {
try {
const params = {
page: page.num,
pageSize: page.size
};
const resp = await getReceiveCenterList(params);
if (resp && resp.bizcode === 100) {
const data = resp.data || {};
const list = data.entitys || [];
const curPageLen = list.length;
const totalCount = data.totalCount || 0;
if (page.num === 1) {
this.couponList = [];
}
this.couponList = this.couponList.concat(list);
this.isEmpty = this.couponList.length === 0;
this.mescroll.endBySize(curPageLen, totalCount);
} else {
if (page.num === 1 && this.couponList.length === 0) {
this.couponList = [];
this.isEmpty = true;
this.mescroll.endBySize(0, 0);
} else {
this.mescroll && this.mescroll.endErr();
}
}
} catch (e) {
console.error('获取领券中心数据失败:', e);
if (page.num === 1 && this.couponList.length === 0) {
this.couponList = [];
this.isEmpty = true;
this.mescroll.endBySize(0, 0);
} else {
this.mescroll && this.mescroll.endErr();
}
}
},
// 查询轮播图
async getCarouselImage() {
try {
const params = {
pageUi: APP_PAGE_TYPE.COUPON
};
const resp = await getCarouselImage(params);
if (resp && resp.bizcode === 100) {
const data = resp.data || [];
this.swiperList = data.length !== 0 ? data.map((obj) => obj.carouselImage) : [];
}
} catch (e) {
console.error('获取轮播图失败:', e);
}
},
// 返回上一页
jumpLeft() {
const pages = getCurrentPages();
if (pages && pages.length > 1) {
uni.navigateBack({
delta: 1
});
} else {
uni.switchTab({
url: '/pages/home/home'
});
}
},
// 点击轮播图预览
onSwiper(index) {
if (this.swiperList && this.swiperList.length > 0) {
uni.previewImage({
current: index,
urls: this.swiperList
});
}
},
// 数据字段映射格式化函数
getDiscountAmount(coupon) {
return coupon.discountAmount !== undefined ? coupon.discountAmount : (coupon.amount || 0);
},
getThresholdText(coupon) {
if (coupon.thresholdType) return coupon.thresholdType;
const threshold = coupon.thresholdAmount !== undefined ? Number(coupon.thresholdAmount) : 0;
if (threshold === 0) {
return '无门槛';
}
if (coupon.type === 1) {
return `满${threshold}元可用`;
}
return `满${threshold}元可用`;
},
getTagText(coupon) {
if (coupon.tag) return coupon.tag;
if (coupon.scopeType === 1) return '通用券';
if (coupon.scopeType === 2) return '商品券';
if (coupon.scopeType === 3 || coupon.scopeType === 4) return '品类券';
if (coupon.scopeText && coupon.scopeText.includes('通用')) return '通用券';
return '通用券';
},
getScopeText(coupon) {
if (coupon.scope) return coupon.scope;
if (coupon.scopeText) return coupon.scopeText;
if (coupon.scopeType === 1) return '所有商品可用';
if (coupon.scopeType === 2) return '指定商品可用';
if (coupon.scopeType === 3) return '指定类目可用';
if (coupon.scopeType === 4) return '指定专区商品可用';
return '所有商品可用';
},
getValidityText(coupon) {
if (coupon.validityText) return coupon.validityText;
if (coupon.validDays && coupon.validDays > 0) {
return `领取之日起${coupon.validDays}天内有效`;
}
if (coupon.useEndTime) {
return `有效期至 ${this.formatTime(coupon.useEndTime)}`;
}
if (coupon.receiveEndTime) {
return `截止时间 ${this.formatTime(coupon.receiveEndTime)}`;
}
return '领取之日起30天内有效';
},
getCouponStatus(coupon) {
if (coupon.receiveStatus !== undefined && coupon.receiveStatus !== null) return Number(coupon.receiveStatus);
if (coupon.received !== undefined && coupon.received !== null) return coupon.received ? 1 : 0;
if (coupon.canReceive !== undefined && coupon.canReceive !== null) return coupon.canReceive ? 0 : 2;
if (coupon.status !== undefined && coupon.status !== null) return Number(coupon.status);
return 0;
},
formatTime(time) {
if (!time) return '';
if (typeof time === 'string' && time.includes('-')) return time;
const date = new Date(Number(time));
if (isNaN(date.getTime())) return String(time);
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, '0');
const d = String(date.getDate()).padStart(2, '0');
const hh = String(date.getHours()).padStart(2, '0');
const mm = String(date.getMinutes()).padStart(2, '0');
const ss = String(date.getSeconds()).padStart(2, '0');
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`;
},
// 领取 / 使用优惠券
async onClaimCoupon(item, index) {
const status = this.getCouponStatus(item);
if (status === 0) {
const templateId = item.templateId || item.id;
if (!templateId) {
uni.showToast({
title: '优惠券ID无效',
icon: 'none'
});
return;
}
try {
// POST /coupon/receive { templateId }
const res = await receiveCoupon({ templateId });
if (res && res.bizcode === 100) {
if (this.$refs.uToastRef) {
this.$refs.uToastRef.show({
type: 'success',
message: '领取成功'
});
} else {
uni.showToast({
title: '领取成功',
icon: 'success'
});
}
// 1. 本地卡片状态即时更新 (按钮变“去使用”)
if (this.couponList && this.couponList[index]) {
this.$set(this.couponList[index], 'receiveStatus', 1);
this.$set(this.couponList[index], 'received', true);
}
// 2. 重新加载/刷新 mescroll 列表
if (this.mescroll) {
this.mescroll.resetUpScroll();
} else {
this.upCallback({ num: 1, size: 10 });
}
} else {
uni.showToast({
title: res?.msg || '领取失败',
icon: 'none'
});
}
} catch (e) {
console.error('领取优惠券失败:', e);
uni.showToast({
title: '领取失败,请重试',
icon: 'none'
});
}
} else if (status === 1) {
// 已领取 -> 去使用,跳转至首页/选购
uni.switchTab({
url: '/pages/home/home'
});
}
},
// 兜底高保真演练数据
getFallbackCoupons() {
return [
{
templateId: 1,
discountAmount: 12,
thresholdAmount: 0,
title: '限时秒杀优惠券',
scopeType: 1,
scopeText: '所有商品可用',
validDays: 30,
receiveStatus: 0,
canReceive: true,
received: false
},
{
templateId: 2,
discountAmount: 12,
thresholdAmount: 0,
title: '限时秒杀优惠券',
scopeType: 3,
scopeText: '指定商品可用',
useEndTime: '2026-8-14 14:20:55',
receiveStatus: 1,
canReceive: false,
received: true
},
{
templateId: 3,
discountAmount: 12,
thresholdAmount: 100,
title: '限时秒杀优惠券',
scopeType: 2,
scopeText: '满100元指定商品可用',
validDays: 30,
receiveStatus: 0,
canReceive: true,
received: false
}
];
}
}
};
</script>
<style lang="scss" scoped>
.get-coupon-page {
min-height: 100vh;
background-color: #f7f8fa;
position: relative;
padding-bottom: 40rpx;
}
/* 头部 Banner 容器 */
.product_info_header_warp {
position: relative;
width: 100%;
height: 660rpx;
background-color: #7b42f6;
.product_info_header_swiper {
height: 660rpx !important;
border-radius: 0 !important;
::v-deep(.u-swiper__wrapper) {
height: 660rpx !important;
.u-swiper__wrapper {
height: 660rpx !important;
.u-swiper__wrapper__item__wrapper__image {
height: 660rpx !important;
border-radius: 0 !important;
}
}
}
}
/* 默认 High-Fidelity 紫红色 Banner 备用视图 */
.default-banner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 660rpx;
background: linear-gradient(135deg, #b83af6 0%, #7b22ec 50%, #681be4 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow: hidden;
.banner-bg-gradient {
position: absolute;
top: -50rpx;
right: -50rpx;
width: 400rpx;
height: 400rpx;
background: radial-gradient(circle, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 70%);
border-radius: 50%;
}
.banner-content {
display: flex;
flex-direction: column;
align-items: center;
margin-top: -60rpx;
.banner-badge {
position: absolute;
top: 80rpx;
left: 40rpx;
font-size: 40rpx;
color: rgba(255, 255, 255, 0.3);
font-weight: bold;
}
.banner-title {
font-size: 76rpx;
font-weight: 900;
color: #ffffff;
letter-spacing: 4rpx;
text-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.25);
background: linear-gradient(to bottom, #ffffff, #f0d5ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.banner-subtitle {
margin-top: 16rpx;
display: flex;
align-items: center;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.4);
padding: 8rpx 28rpx;
border-radius: 30rpx;
backdrop-filter: blur(8px);
.sparkle {
font-size: 20rpx;
color: #ffd700;
margin: 0 8rpx;
}
.sub-text {
font-size: 26rpx;
color: #ffffff;
font-weight: 500;
}
}
}
}
/* 浮动返回图标 */
.img_comm {
position: fixed;
z-index: 999;
}
.left_warp {
left: 40rpx;
}
}
/* 优惠券列表区域 */
.coupon-list-container {
padding: 24rpx 24rpx 40rpx 24rpx;
box-sizing: border-box;
}
/* 高保真优惠券卡片 */
.coupon-card {
position: relative;
display: flex;
align-items: center;
background: rgba(253, 28, 36, 0.06);
border-radius: 16rpx;
margin-bottom: 20rpx;
box-sizing: border-box;
.card-left {
width: 170rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 24rpx 0;
.price-box {
display: flex;
align-items: baseline;
color: #ff2442;
.currency {
font-size: 28rpx;
font-weight: bold;
margin-right: 2rpx;
}
.amount {
font-size: 60rpx;
font-weight: 700;
line-height: 1;
}
}
.condition {
font-size: 24rpx;
color: #ff2442;
margin-top: 10rpx;
font-weight: 500;
}
}
/* 虚线分割线及上下凹槽 */
.divider-wrapper {
position: relative;
width: 2rpx;
align-self: stretch;
.divider-line {
height: 100%;
border-left: 2rpx dashed #fca5a5;
}
.notch {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 24rpx;
height: 24rpx;
background-color: #f7f8fa;
border-radius: 50%;
z-index: 10;
}
.top-notch {
top: -12rpx;
}
.bottom-notch {
bottom: -12rpx;
}
}
/* 卡片右侧内容 */
.card-right {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 20rpx 20rpx 20rpx 24rpx;
.info-content {
flex: 1;
min-width: 0;
margin-right: 12rpx;
.coupon-title {
font-size: 30rpx;
font-weight: bold;
color: #1a1a1a;
margin-bottom: 10rpx;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tag-row {
display: flex;
align-items: center;
margin-bottom: 10rpx;
white-space: nowrap;
.coupon-tag {
display: inline-block;
font-size: 20rpx;
color: #ff2442;
background: #ffe4e6;
border: 1px solid #ff99a4;
border-radius: 6rpx;
padding: 2rpx 10rpx;
margin-right: 10rpx;
line-height: 1.2;
font-weight: 500;
flex-shrink: 0;
white-space: nowrap;
}
.coupon-scope {
font-size: 22rpx;
color: #666666;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.coupon-validity {
font-size: 22rpx;
color: #999999;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.btn-box {
flex-shrink: 0;
.action-btn {
width: 130rpx;
height: 54rpx;
line-height: 54rpx;
text-align: center;
font-size: 24rpx;
font-weight: bold;
border-radius: 8rpx;
box-sizing: border-box;
&.btn-claim {
background: #ff2442;
color: #ffffff;
}
&.btn-use {
background: #ffe8eb;
color: #ff2442;
border: 1px solid #ff4d5e;
line-height: 52rpx;
font-weight: 500;
}
&.btn-disabled {
background: #eeeeee;
color: #bbbbbb;
}
}
}
}
}
/* 无领券数据缺省页面 (高保真) */
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 140rpx;
.empty-graphic {
position: relative;
width: 220rpx;
height: 220rpx;
margin-bottom: 24rpx;
.purple-bag-wrapper {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.glow-bg {
position: absolute;
width: 180rpx;
height: 180rpx;
background: radial-gradient(circle, rgba(168, 85, 247, 0.25) 0%, rgba(247, 248, 250, 0) 70%);
border-radius: 50%;
}
.bag-card {
position: relative;
width: 110rpx;
height: 120rpx;
background: linear-gradient(135deg, #a855f7 0%, #7a35f6 100%);
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 12rpx 28rpx rgba(122, 53, 246, 0.3);
z-index: 2;
.bag-handle {
position: absolute;
top: -16rpx;
width: 44rpx;
height: 24rpx;
border: 4rpx solid #c084fc;
border-bottom: none;
border-radius: 14rpx 14rpx 0 0;
}
.bag-logo {
font-size: 32rpx;
font-weight: 900;
color: #ffffff;
letter-spacing: 2rpx;
}
}
.heart-bubble {
position: absolute;
top: 20rpx;
right: 24rpx;
width: 44rpx;
height: 44rpx;
background: linear-gradient(135deg, #c084fc, #a855f7);
border-radius: 12rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 12rpx rgba(168, 85, 247, 0.3);
z-index: 3;
.heart-icon {
color: #ffffff;
font-size: 24rpx;
}
}
}
}
.empty-title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
margin-bottom: 12rpx;
}
.empty-subtitle {
font-size: 26rpx;
color: #999999;
}
}
</style>