feat(M08-A): 接入顾客端权益明细

This commit is contained in:
Codex
2026-06-25 12:04:55 +08:00
parent fb1b593b24
commit e24920c9cc
20 changed files with 661 additions and 38 deletions
+80
View File
@@ -0,0 +1,80 @@
const { request, ensureLogin, cents } = require('../../utils/api.js')
Page({
data: {
loading: false,
errorMessage: '',
coupons: [],
packages: [],
},
onLoad() {
this.loadBenefits()
},
async loadBenefits() {
this.setData({ loading: true, errorMessage: '' })
try {
await ensureLogin()
const response = await request('/profile/benefits')
this.setData({
coupons: (response.data.coupons || []).map(formatCoupon),
packages: (response.data.packages || []).map(formatPackage),
})
} catch (error) {
this.setData({ errorMessage: error.message || '权益明细加载失败' })
} finally {
this.setData({ loading: false })
}
},
})
function formatCoupon(item) {
const isTime = item.couponType === 'TIME'
return {
...item,
statusText: benefitStatus(item.status),
valueText: isTime ? `${item.timeMinutes || 0}分钟` : cents(item.discountAmountCents),
thresholdText: Number(item.minOrderAmountCents || 0) > 0
? `${cents(item.minOrderAmountCents)}可用`
: '无门槛',
validText: `${formatDate(item.validFrom)}${formatDate(item.validTo)}`,
scopeText: scopeText(item),
}
}
function formatPackage(item) {
return {
...item,
statusText: benefitStatus(item.status),
priceText: cents(item.priceCents),
remainingAmountText: cents(item.remainingAmountCents),
totalAmountText: cents(item.amountCentsTotal),
validText: `${formatDate(item.validFrom)}${formatDate(item.validTo)}`,
scopeText: scopeText(item),
}
}
function benefitStatus(status) {
return ({
AVAILABLE: '可用',
ACTIVE: '可用',
FROZEN: '锁定中',
USED: '已使用',
EXHAUSTED: '已用尽',
EXPIRED: '已过期',
})[status] || status
}
function scopeText(item) {
if (item.roomId) return `指定房间 ${item.roomId}`
if (item.roomCategoryId) return `指定房型 ${item.roomCategoryId}`
if (item.storeId) return `指定门店 ${item.storeId}`
return item.holidayOnly ? '节假日可用' : '全部门店可用'
}
function formatDate(value) {
const date = new Date(value)
const pad = (input) => String(input).padStart(2, '0')
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
}
+3
View File
@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "权益明细"
}
+43
View File
@@ -0,0 +1,43 @@
<scroll-view class="scrollarea" scroll-y type="list">
<view class="page">
<view class="title">权益明细</view>
<view wx:if="{{errorMessage}}" class="error">{{errorMessage}}</view>
<view class="section-title">优惠券</view>
<view wx:if="{{!loading && coupons.length === 0}}" class="empty">暂无优惠券</view>
<view wx:for="{{coupons}}" wx:key="couponGrantId" class="benefit-card">
<view class="card-head">
<view class="benefit-name">{{item.name}}</view>
<view class="status">{{item.statusText}}</view>
</view>
<view class="benefit-value">{{item.valueText}}</view>
<view class="muted">{{item.thresholdText}}</view>
<view class="muted">{{item.scopeText}}</view>
<view class="muted">{{item.validText}}</view>
<view class="muted">剩余次数:{{item.remainingUses}}</view>
</view>
<view class="section-title">套餐</view>
<view wx:if="{{!loading && packages.length === 0}}" class="empty">暂无套餐</view>
<view wx:for="{{packages}}" wx:key="packageHoldingId" class="benefit-card">
<view class="card-head">
<view class="benefit-name">{{item.name}}</view>
<view class="status">{{item.statusText}}</view>
</view>
<view class="row">
<text>剩余分钟</text>
<text>{{item.remainingMinutes}} / {{item.minutesTotal}}</text>
</view>
<view class="row">
<text>剩余金额</text>
<text>{{item.remainingAmountText}} / {{item.totalAmountText}}</text>
</view>
<view class="row">
<text>购买价</text>
<text>{{item.priceText}}</text>
</view>
<view class="muted">{{item.scopeText}}</view>
<view class="muted">{{item.validText}}</view>
</view>
</view>
</scroll-view>
+72
View File
@@ -0,0 +1,72 @@
.scrollarea {
height: 100vh;
background: #f5f6f8;
}
.page {
padding: 32rpx;
}
.title {
margin-bottom: 24rpx;
font-size: 40rpx;
font-weight: 600;
}
.section-title {
margin: 28rpx 0 16rpx;
font-size: 32rpx;
font-weight: 600;
}
.benefit-card {
margin-top: 20rpx;
padding: 28rpx;
border-radius: 16rpx;
background: #ffffff;
}
.card-head,
.row {
display: flex;
justify-content: space-between;
gap: 24rpx;
}
.benefit-name {
font-size: 32rpx;
font-weight: 600;
}
.status {
color: #1f6feb;
white-space: nowrap;
}
.benefit-value {
margin-top: 16rpx;
color: #b42318;
font-size: 44rpx;
font-weight: 700;
}
.row {
margin-top: 16rpx;
color: #475467;
}
.muted {
margin-top: 8rpx;
color: #667085;
}
.empty {
padding: 60rpx 0;
color: #888888;
text-align: center;
}
.error {
margin: 16rpx 0;
color: #c73535;
}