feat(M08-A): 增加权益可视化选择器
This commit is contained in:
@@ -32,15 +32,82 @@ Page({
|
||||
packageHoldingId: '',
|
||||
packageMinutes: '',
|
||||
packageCreditYuan: '',
|
||||
benefitLoading: false,
|
||||
availableCoupons: [],
|
||||
availablePackages: [],
|
||||
selectedCouponName: '',
|
||||
selectedPackageName: '',
|
||||
},
|
||||
onLoad(options) {
|
||||
this.setData({
|
||||
storeId: options.storeId || '',
|
||||
roomId: options.roomId || '',
|
||||
})
|
||||
this.loadBenefitOptions()
|
||||
},
|
||||
|
||||
updateBenefitField(event) {
|
||||
async loadBenefitOptions() {
|
||||
this.setData({ benefitLoading: true })
|
||||
try {
|
||||
await ensureLogin()
|
||||
const response = await request('/profile/benefits')
|
||||
this.setData({
|
||||
availableCoupons: (response.data.coupons || [])
|
||||
.filter((item) => canUseCoupon(item, this.data.storeId, this.data.roomId))
|
||||
.map(formatCouponOption),
|
||||
availablePackages: (response.data.packages || [])
|
||||
.filter((item) => canUsePackage(item, this.data.storeId, this.data.roomId))
|
||||
.map(formatPackageOption),
|
||||
})
|
||||
} catch (error) {
|
||||
this.setData({ errorMessage: error.message || '权益加载失败' })
|
||||
} finally {
|
||||
this.setData({ benefitLoading: false })
|
||||
}
|
||||
},
|
||||
|
||||
selectCoupon(event) {
|
||||
const couponGrantId = event.currentTarget.dataset.id || ''
|
||||
const current = this.data.couponGrantId === couponGrantId
|
||||
const selected = current
|
||||
? null
|
||||
: this.data.availableCoupons.find((item) => item.couponGrantId === couponGrantId)
|
||||
this.setData({
|
||||
couponGrantId: current ? '' : couponGrantId,
|
||||
selectedCouponName: selected ? selected.name : '',
|
||||
order: null,
|
||||
payment: null,
|
||||
successMessage: '',
|
||||
errorMessage: '',
|
||||
})
|
||||
},
|
||||
|
||||
selectPackage(event) {
|
||||
const packageHoldingId = event.currentTarget.dataset.id || ''
|
||||
const current = this.data.packageHoldingId === packageHoldingId
|
||||
const selected = current
|
||||
? null
|
||||
: this.data.availablePackages.find((item) => item.packageHoldingId === packageHoldingId)
|
||||
this.setData({
|
||||
packageHoldingId: current ? '' : packageHoldingId,
|
||||
selectedPackageName: selected ? selected.name : '',
|
||||
packageMinutes: selected && selected.remainingMinutes > 0
|
||||
? String(Math.min(selected.remainingMinutes, this.data.durationMinutes))
|
||||
: '',
|
||||
packageCreditYuan: selected && selected.remainingAmountCents > 0
|
||||
? yuanInput(Math.min(
|
||||
selected.remainingAmountCents,
|
||||
this.data.quote?.totalCents || selected.remainingAmountCents
|
||||
))
|
||||
: '',
|
||||
order: null,
|
||||
payment: null,
|
||||
successMessage: '',
|
||||
errorMessage: '',
|
||||
})
|
||||
},
|
||||
|
||||
updatePackageUsage(event) {
|
||||
const field = event.currentTarget.dataset.field
|
||||
if (!field) return
|
||||
this.setData({
|
||||
@@ -95,7 +162,52 @@ Page({
|
||||
if (!this.data.order || !this.data.order.orderId) {
|
||||
this.setData({ errorMessage: '请先预占订单' })
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function canUseCoupon(item, storeId, roomId) {
|
||||
return item.status === 'AVAILABLE'
|
||||
&& Number(item.remainingUses || 0) > 0
|
||||
&& (!item.storeId || String(item.storeId) === String(storeId))
|
||||
&& (!item.roomId || String(item.roomId) === String(roomId))
|
||||
}
|
||||
|
||||
function canUsePackage(item, storeId, roomId) {
|
||||
return item.status === 'ACTIVE'
|
||||
&& (Number(item.remainingMinutes || 0) > 0 || Number(item.remainingAmountCents || 0) > 0)
|
||||
&& (!item.storeId || String(item.storeId) === String(storeId))
|
||||
&& (!item.roomId || String(item.roomId) === String(roomId))
|
||||
}
|
||||
|
||||
function formatCouponOption(item) {
|
||||
const isTime = item.couponType === 'TIME'
|
||||
return {
|
||||
...item,
|
||||
valueText: isTime ? `${item.timeMinutes || 0}分钟` : cents(item.discountAmountCents),
|
||||
thresholdText: Number(item.minOrderAmountCents || 0) > 0
|
||||
? `满${cents(item.minOrderAmountCents)}可用`
|
||||
: '无门槛',
|
||||
scopeText: scopeText(item),
|
||||
}
|
||||
}
|
||||
|
||||
function formatPackageOption(item) {
|
||||
return {
|
||||
...item,
|
||||
remainingAmountText: cents(item.remainingAmountCents),
|
||||
scopeText: scopeText(item),
|
||||
}
|
||||
}
|
||||
|
||||
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 yuanInput(centsValue) {
|
||||
return (Number(centsValue || 0) / 100).toFixed(2)
|
||||
}
|
||||
await this.withRequest(async () => {
|
||||
const response = await request('/payments', {
|
||||
method: 'POST',
|
||||
|
||||
@@ -10,11 +10,41 @@
|
||||
</view>
|
||||
|
||||
<view class="section-title">权益抵扣</view>
|
||||
<view class="benefit-grid">
|
||||
<input placeholder="优惠券ID" value="{{couponGrantId}}" data-field="couponGrantId" bindinput="updateBenefitField" />
|
||||
<input placeholder="套餐ID" value="{{packageHoldingId}}" data-field="packageHoldingId" bindinput="updateBenefitField" />
|
||||
<input placeholder="套餐分钟" type="number" value="{{packageMinutes}}" data-field="packageMinutes" bindinput="updateBenefitField" />
|
||||
<input placeholder="套餐抵扣元" type="digit" value="{{packageCreditYuan}}" data-field="packageCreditYuan" bindinput="updateBenefitField" />
|
||||
<view wx:if="{{benefitLoading}}" class="muted">权益加载中</view>
|
||||
<view wx:if="{{!benefitLoading && availableCoupons.length === 0 && availablePackages.length === 0}}" class="muted">暂无可用权益</view>
|
||||
<view wx:if="{{availableCoupons.length}}" class="benefit-list">
|
||||
<view
|
||||
wx:for="{{availableCoupons}}"
|
||||
wx:key="couponGrantId"
|
||||
class="benefit-option {{couponGrantId === item.couponGrantId ? 'active' : ''}}"
|
||||
data-id="{{item.couponGrantId}}"
|
||||
bindtap="selectCoupon"
|
||||
>
|
||||
<view class="option-head">
|
||||
<view class="option-title">{{item.name}}</view>
|
||||
<view class="option-value">{{item.valueText}}</view>
|
||||
</view>
|
||||
<view class="muted">{{item.thresholdText}} · {{item.scopeText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{availablePackages.length}}" class="benefit-list">
|
||||
<view
|
||||
wx:for="{{availablePackages}}"
|
||||
wx:key="packageHoldingId"
|
||||
class="benefit-option {{packageHoldingId === item.packageHoldingId ? 'active' : ''}}"
|
||||
data-id="{{item.packageHoldingId}}"
|
||||
bindtap="selectPackage"
|
||||
>
|
||||
<view class="option-head">
|
||||
<view class="option-title">{{item.name}}</view>
|
||||
<view class="option-value">{{item.remainingAmountText}}</view>
|
||||
</view>
|
||||
<view class="muted">剩余 {{item.remainingMinutes}} 分钟 · {{item.scopeText}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{packageHoldingId}}" class="benefit-grid">
|
||||
<input placeholder="套餐分钟" type="number" value="{{packageMinutes}}" data-field="packageMinutes" bindinput="updatePackageUsage" />
|
||||
<input placeholder="套餐抵扣元" type="digit" value="{{packageCreditYuan}}" data-field="packageCreditYuan" bindinput="updatePackageUsage" />
|
||||
</view>
|
||||
|
||||
<view class="action-grid">
|
||||
@@ -39,6 +69,8 @@
|
||||
<view class="result-title">订单</view>
|
||||
<view>订单编号:{{order.orderId}}</view>
|
||||
<view wx:if="{{order.reservationId}}">预占编号:{{order.reservationId}}</view>
|
||||
<view wx:if="{{selectedCouponName}}">已选优惠券:{{selectedCouponName}}</view>
|
||||
<view wx:if="{{selectedPackageName}}">已选套餐:{{selectedPackageName}}</view>
|
||||
<view wx:if="{{order.quote.discountCents}}">优惠抵扣:{{order.quote.discountCents}}分</view>
|
||||
<view wx:if="{{order.quote.packageCreditCents}}">套餐抵扣:{{order.quote.packageCreditCents}}分</view>
|
||||
<button size="mini" data-order-id="{{order.orderId}}" bindtap="openOrder">查看订单</button>
|
||||
|
||||
@@ -40,6 +40,46 @@
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.benefit-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.benefit-option {
|
||||
padding: 22rpx;
|
||||
border: 2rpx solid #d9dde5;
|
||||
border-radius: 12rpx;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.benefit-option.active {
|
||||
border-color: #1f6feb;
|
||||
background: #eef5ff;
|
||||
}
|
||||
|
||||
.option-head {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.option-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.option-value {
|
||||
flex: 0 0 auto;
|
||||
color: #1f6feb;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.action-grid button {
|
||||
flex: 1 1 280rpx;
|
||||
}
|
||||
@@ -67,6 +107,11 @@
|
||||
color: #17823b;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: #777777;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.error {
|
||||
margin: 16rpx 0;
|
||||
color: #c73535;
|
||||
|
||||
Reference in New Issue
Block a user