vending-machine/mobile/pages/stamps/stamps.vue
18631081161 d43406380c
Some checks reported errors
continuous-integration/drone/push Build encountered an error
逻辑优化
2026-04-13 13:43:06 +08:00

336 lines
7.7 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="stamps-page">
<!-- 自定义导航栏 -->
<view class="custom-nav" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-inner">
<view class="nav-back" @click="goBack">
<text class="nav-back-icon"></text>
</view>
<text class="nav-title">{{ t('stamps.title') }}</text>
<view class="nav-placeholder" />
</view>
</view>
<!-- 页面内容 -->
<view :style="{ paddingTop: (statusBarHeight + 44) + 'px' }">
<!-- 印花Banner图 -->
<view class="banner-wrap">
<image
v-if="bannerUrl"
class="stamp-banner"
:src="bannerUrl"
mode="aspectFill"
/>
</view>
<!-- 可兑换优惠券标题行 -->
<view class="coupon-header">
<text class="coupon-title">{{ t('home.redeemableCoupons') || '可兑换优惠券' }}</text>
<view class="coupon-guide-link" @click="handleGuideEntry">
<image class="guide-icon-img" src="/static/ic_explain.png" mode="aspectFit" />
<text class="guide-text">{{ t('home.guide') }}</text>
</view>
</view>
<!-- 优惠券列表 -->
<view class="coupon-list">
<view class="coupon-card" v-for="coupon in stampCoupons" :key="coupon.id">
<image class="coupon-bg" src="/static/ic_coupon_bg.png" mode="aspectFill" />
<view class="coupon-inner">
<view class="coupon-info">
<text class="coupon-name">{{ coupon.name }}</text>
<view class="coupon-detail-row">
<text class="coupon-dot">●</text>
<text class="coupon-detail-label">{{ t('couponCard.expireLabel') }}</text>
<text class="coupon-detail-value highlight">{{ formatExpire(coupon.expireAt) }}</text>
</view>
<view class="coupon-detail-row">
<text class="coupon-dot">●</text>
<text class="coupon-detail-label">{{ t('couponCard.conditionLabel') }}</text>
<text class="coupon-detail-value highlight">{{ coupon.pointsCost ?? 0 }}{{ t('couponCard.pointsUnit') }}</text>
</view>
</view>
<view class="coupon-action">
<view
class="redeem-btn"
:class="{ 'redeemed-btn': coupon.isRedeemed }"
@click="onRedeemClick(coupon)"
>
<text class="redeem-btn-text">{{ coupon.isRedeemed ? t('couponCard.redeemedBtn') : t('couponCard.redeemBtn') }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<EmptyState v-if="stampCoupons.length === 0" text="暂无印花优惠券" />
<!-- 使用说明弹窗 -->
<CouponGuidePopup :visible="showGuide" :content="guideContent" @close="showGuide = false" />
<!-- 兑换确认弹窗 -->
<RedeemPopup
:visible="showRedeem"
:coupon="selectedCoupon"
@confirm="onRedeemConfirm"
@cancel="showRedeem = false"
/>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { getStampBanner, getCouponGuide } from '../../api/content.js'
import { resolveImageUrl } from '../../utils/image.js'
import { getStampCoupons, redeemStampCoupon } from '../../api/coupon.js'
import { useUserStore } from '../../stores/user.js'
import RedeemPopup from '../../components/RedeemPopup.vue'
import CouponGuidePopup from '../../components/CouponGuidePopup.vue'
import EmptyState from '../../components/EmptyState.vue'
const { t } = useI18n()
const userStore = useUserStore()
// 状态栏高度
const statusBarHeight = ref(0)
try {
const sysInfo = uni.getSystemInfoSync()
statusBarHeight.value = sysInfo.statusBarHeight || 0
} catch (e) {}
function goBack() {
uni.navigateBack()
}
// 数据
const bannerUrl = ref('')
const stampCoupons = ref([])
// 弹窗状态
const showRedeem = ref(false)
const selectedCoupon = ref(null)
const showGuide = ref(false)
const guideContent = ref('')
// 格式化到期时间
function formatExpire(dateStr) {
if (!dateStr) return t('couponCard.noLimit')
const d = new Date(dateStr)
return `${d.getFullYear()}/${d.getMonth() + 1}/${d.getDate()}`
}
// 使用说明入口
async function handleGuideEntry() {
try {
const res = await getCouponGuide()
guideContent.value = res.data?.content || res.data || ''
showGuide.value = true
} catch (e) {}
}
// 点击兑换按钮
function onRedeemClick(coupon) {
if (coupon.isRedeemed) return
selectedCoupon.value = coupon
showRedeem.value = true
}
// 确认兑换
async function onRedeemConfirm() {
if (!selectedCoupon.value) return
try {
await redeemStampCoupon(selectedCoupon.value.id)
showRedeem.value = false
selectedCoupon.value = null
uni.showToast({ title: t('home.redeemSuccess'), icon: 'none' })
await loadStampCoupons()
} catch (e) {
showRedeem.value = false
}
}
// 加载Banner
async function loadBanner() {
try {
const res = await getStampBanner()
bannerUrl.value = resolveImageUrl(res.data?.imageUrl || res.data || '')
} catch (e) {}
}
// 加载印花优惠券列表
async function loadStampCoupons() {
try {
const res = await getStampCoupons()
stampCoupons.value = res.data || []
} catch (e) {}
}
onMounted(() => {
loadBanner()
loadStampCoupons()
})
</script>
<style scoped>
.stamps-page {
min-height: 100vh;
background-color: #f5f0e8;
}
/* 自定义导航栏 */
.custom-nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
background-color: #DBDBDB;
}
.nav-inner {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16rpx;
}
.nav-back {
width: 60rpx;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
}
.nav-back-icon {
font-size: 48rpx;
color: #333;
}
.nav-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.nav-placeholder {
width: 60rpx;
}
/* Banner */
.banner-wrap {
padding: 24rpx 24rpx 0;
}
.stamp-banner {
width: 100%;
height: 338rpx;
border-radius: 20rpx;
}
/* 优惠券标题行 */
.coupon-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 36rpx 24rpx 16rpx;
}
.coupon-title {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
.coupon-guide-link {
display: flex;
align-items: center;
}
.guide-icon-img {
width: 28rpx;
height: 28rpx;
margin-right: 6rpx;
}
.guide-text {
font-size: 24rpx;
color: #999;
}
/* 优惠券列表 */
.coupon-list {
padding: 0 24rpx 24rpx;
}
.coupon-card {
position: relative;
margin-bottom: 20rpx;
border-radius: 20rpx;
overflow: hidden;
}
.coupon-bg {
position: absolute;
top: 0; left: 0;
width: 100%;
height: 100%;
}
.coupon-inner {
position: relative;
z-index: 1;
padding: 32rpx;
display: flex;
flex-direction: column;
min-height: 240rpx;
}
.coupon-info {
flex: 1;
}
.coupon-name {
font-size: 34rpx;
color: #333;
font-weight: 600;
display: block;
margin-left: 28rpx;
margin-bottom: 16rpx;
}
.coupon-detail-row {
display: flex;
align-items: center;
margin-left: 28rpx;
margin-bottom: 8rpx;
}
.coupon-dot {
font-size: 14rpx;
color: #D9DED7;
margin-right: 10rpx;
}
.coupon-detail-label {
font-size: 24rpx;
color: #666;
}
.coupon-detail-value {
font-size: 24rpx;
color: #666;
}
.coupon-detail-value.highlight {
color: #c9a96e;
font-weight: 500;
}
/* 兑换按钮 */
.coupon-action {
display: flex;
justify-content: flex-end;
margin-top: 16rpx;
}
.redeem-btn {
background: linear-gradient(135deg, #d4a855, #c9a96e);
border-radius: 28rpx;
width: 172rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
.redeemed-btn {
background: #cccccc;
}
.redeem-btn-text {
font-size: 28rpx;
color: #ffffff;
white-space: nowrap;
}
</style>