feat: 购买次数卡弹窗新增数量选择功能并同步更新购买接口。

This commit is contained in:
邹方成 2026-01-02 16:32:12 +08:00
parent 4252a0ed61
commit a8fa8bf557
2 changed files with 71 additions and 8 deletions

View File

@ -354,8 +354,9 @@ export function getGamePassPackages(activity_id) {
/** /**
* 购买次数卡套餐 * 购买次数卡套餐
* @param {number} package_id - 套餐ID * @param {number} package_id - 套餐ID
* @param {number} count - 购买数量
*/ */
export function purchaseGamePass(package_id) { export function purchaseGamePass(package_id, count = 1) {
return authRequest({ url: '/api/app/game-passes/purchase', method: 'POST', data: { package_id } }) return authRequest({ url: '/api/app/game-passes/purchase', method: 'POST', data: { package_id, count } })
} }

View File

@ -37,7 +37,17 @@
<view class="pkg-original-price" v-if="pkg.original_price > pkg.price"> <view class="pkg-original-price" v-if="pkg.original_price > pkg.price">
¥{{ (pkg.original_price / 100).toFixed(2) }} ¥{{ (pkg.original_price / 100).toFixed(2) }}
</view> </view>
<button class="btn-buy" :loading="purchasingId === pkg.id">购买</button>
<view class="action-row">
<view class="stepper" @tap.stop>
<text class="step-btn minus" @tap="updateCount(pkg.id, -1)">-</text>
<text class="step-val">{{ counts[pkg.id] || 1 }}</text>
<text class="step-btn plus" @tap="updateCount(pkg.id, 1)">+</text>
</view>
<button class="btn-buy" :loading="purchasingId === pkg.id" @tap.stop="handlePurchase(pkg)">
购买
</button>
</view>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
@ -60,6 +70,15 @@ const emit = defineEmits(['update:visible', 'success'])
const loading = ref(false) const loading = ref(false)
const packages = ref([]) const packages = ref([])
const purchasingId = ref(null) const purchasingId = ref(null)
const counts = ref({})
function updateCount(pkgId, delta) {
const current = counts.value[pkgId] || 1
const newVal = current + delta
if (newVal >= 1 && newVal <= 99) {
counts.value[pkgId] = newVal
}
}
watch(() => props.visible, (val) => { watch(() => props.visible, (val) => {
if (val) { if (val) {
@ -78,6 +97,11 @@ async function fetchPackages() {
else if (res && Array.isArray(res.list)) list = res.list else if (res && Array.isArray(res.list)) list = res.list
else if (res && Array.isArray(res.data)) list = res.data else if (res && Array.isArray(res.data)) list = res.data
// counts
const countMap = {}
list.forEach(p => countMap[p.id] = 1)
counts.value = countMap
// //
// "" // ""
packages.value = list.map(p => { packages.value = list.map(p => {
@ -112,7 +136,8 @@ async function handlePurchase(pkg) {
// pay_params // pay_params
// API { order_no, ... } // API { order_no, ... }
const res = await purchaseGamePass(pkg.id) const count = counts.value[pkg.id] || 1
const res = await purchaseGamePass(pkg.id, count)
const orderNo = res.order_no || res.orderNo const orderNo = res.order_no || res.orderNo
if (!orderNo) throw new Error('下单失败') if (!orderNo) throw new Error('下单失败')
@ -290,10 +315,10 @@ function handleClose() {
background: linear-gradient(90deg, #FF6B00, #FF9F43); background: linear-gradient(90deg, #FF6B00, #FF9F43);
color: #FFF; color: #FFF;
font-size: 24rpx; font-size: 24rpx;
padding: 0 24rpx; padding: 0 20rpx;
height: 56rpx; height: 52rpx;
line-height: 56rpx; line-height: 52rpx;
border-radius: 28rpx; border-radius: 26rpx;
border: none; border: none;
font-weight: 600; font-weight: 600;
@ -301,4 +326,41 @@ function handleClose() {
opacity: 0.8; opacity: 0.8;
} }
} }
.action-row {
display: flex;
align-items: center;
gap: 12rpx;
margin-top: 8rpx;
}
.stepper {
display: flex;
align-items: center;
background: #F3F4F6;
border-radius: 12rpx;
padding: 2rpx;
.step-btn {
width: 44rpx;
height: 44rpx;
line-height: 40rpx;
text-align: center;
font-size: 32rpx;
color: #4B5563;
font-weight: 300;
}
.minus {
color: #9CA3AF;
}
.step-val {
width: 40rpx;
text-align: center;
font-size: 26rpx;
font-weight: bold;
color: #1F2937;
}
}
</style> </style>