vending-machine/mobile/pages/stamps/stamps.vue
2026-04-08 21:03:13 +08:00

192 lines
4.2 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">
<!-- 印花Banner图 -->
<image
v-if="bannerUrl"
class="stamp-banner"
:src="bannerUrl"
mode="widthFix"
/>
<!-- 印花优惠券列表 -->
<view class="stamp-list">
<view
v-for="coupon in stampCoupons"
:key="coupon.stampCouponId"
class="stamp-card"
>
<view class="stamp-info">
<text class="stamp-name">{{ coupon.name }}</text>
<text class="stamp-expire">{{ formatExpire(coupon.expireAt) }}</text>
</view>
<view
class="stamp-btn"
:class="{ disabled: resolveBtn(coupon).disabled }"
@click="onRedeemClick(coupon)"
>
<text class="stamp-btn-text">
{{ t('stamps.' + resolveBtn(coupon).label) }}
</text>
</view>
</view>
</view>
<!-- 兑换确认弹窗 -->
<RedeemPopup
:visible="showRedeem"
:coupon="selectedCoupon"
@confirm="onRedeemConfirm"
@cancel="showRedeem = false"
/>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { getStampBanner } from '../../api/content.js'
import { resolveImageUrl } from '../../utils/image.js'
import { getStampCoupons, redeemStampCoupon } from '../../api/coupon.js'
import { useUserStore } from '../../stores/user.js'
import { resolveStampRedeemButton } from '../../utils/stamps.js'
import RedeemPopup from '../../components/RedeemPopup.vue'
const { t } = useI18n()
const userStore = useUserStore()
// 数据
const bannerUrl = ref('')
const stampCoupons = ref([])
// 弹窗状态
const showRedeem = ref(false)
const selectedCoupon = ref(null)
// 解析按钮状态
function resolveBtn(coupon) {
const isMember = userStore.userInfo?.isMember || false
return resolveStampRedeemButton({ redeemed: coupon.redeemed, isMember })
}
// 格式化到期时间
function formatExpire(dateStr) {
if (!dateStr) return ''
const d = new Date(dateStr)
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
}
// 点击兑换按钮
function onRedeemClick(coupon) {
const btnState = resolveBtn(coupon)
if (btnState.disabled) return
if (btnState.action === 'goMembership') {
// 非会员跳转会员页需求6.4
uni.navigateTo({ url: '/pages/membership/membership' })
return
}
// 会员可兑换,弹出确认弹窗
selectedCoupon.value = coupon
showRedeem.value = true
}
// 确认兑换
async function onRedeemConfirm() {
if (!selectedCoupon.value) return
try {
await redeemStampCoupon(selectedCoupon.value.stampCouponId)
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: #f5f5f5;
}
.stamp-banner {
width: 100%;
}
.stamp-list {
padding: 24rpx;
}
.stamp-card {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.stamp-info {
flex: 1;
margin-right: 20rpx;
}
.stamp-name {
font-size: 30rpx;
color: #333;
font-weight: 500;
display: block;
margin-bottom: 8rpx;
}
.stamp-expire {
font-size: 22rpx;
color: #999;
display: block;
}
.stamp-btn {
padding: 14rpx 32rpx;
background-color: #007aff;
border-radius: 30rpx;
}
.stamp-btn.disabled {
background-color: #cccccc;
}
.stamp-btn-text {
font-size: 26rpx;
color: #ffffff;
white-space: nowrap;
}
</style>