fix: 修复月卡无法编辑次数的问题

This commit is contained in:
richarjiang
2026-09-07 18:10:39 +08:00
parent 86ad9ee64f
commit 87d946adb5
17 changed files with 564 additions and 91 deletions

View File

@@ -150,7 +150,7 @@
class="modal-input"
type="number"
v-model="form.totalTimesStr"
placeholder="次卡必填月卡留空"
placeholder="次卡必填月卡可填写次数"
placeholder-style="color:#bbb"
/>
</view>

View File

@@ -27,7 +27,7 @@
>
<text class="card-pill-name">{{ card.cardType.name }}</text>
<text class="card-pill-meta">
{{ card.remainingTimes == null ? '月卡不扣次' : `${card.remainingTimes}` }}
{{ card.remainingTimes === null ? '有效期内不限次' : `${card.remainingTimes}` }}
</text>
</view>
</view>
@@ -146,11 +146,11 @@ const selectedMembership = computed(
const canArrange = computed(() => Boolean(selectedMembership.value))
const isDurationCard = computed(() => selectedMembership.value?.remainingTimes == null)
const isCountLimited = computed(() => selectedMembership.value?.remainingTimes !== null)
const deductHint = computed(() => {
if (isDurationCard.value) {
return '将立即确认该课,卡不次,会员无需再确认。'
if (!isCountLimited.value) {
return '将立即确认该课,会员卡不次,会员无需再确认。'
}
return '将立即确认该课并扣除 1 次,会员无需再确认。'
})

View File

@@ -55,9 +55,16 @@
</picker>
</view>
<view v-if="isTimeBasedCard" class="field">
<text class="field-label">剩余次数</text>
<input class="field-input" type="number" v-model="editForm.remainingTimes" placeholder="请输入剩余次数" />
<view v-if="selectedCardType" class="field">
<text class="field-label">
{{ canLeaveTimesEmpty ? '剩余次数(留空不限次)' : '剩余次数' }}
</text>
<input
class="field-input"
type="number"
v-model="editForm.remainingTimes"
:placeholder="canLeaveTimesEmpty ? '留空表示不限次' : '请输入剩余次数'"
/>
</view>
<view class="field">
@@ -124,7 +131,7 @@ const editForm = ref({
membershipId: '' as string | '',
cardTypeIndex: 0,
cardTypeId: '',
remainingTimes: null as number | null,
remainingTimes: null as number | string | null,
startDate: '',
expireDate: '',
manuallyEditedExpire: false,
@@ -137,9 +144,15 @@ const membershipPickerLabels = computed(() =>
existingMemberships.value.map((item) => `${item.cardType.name} · ${item.status}`),
)
const isTimeBasedCard = computed(() => {
const card = cardTypes.value[editForm.value.cardTypeIndex]
return card && (card.type === 'TIMES' || card.type === 'TRIAL')
const selectedCardType = computed(() => cardTypes.value[editForm.value.cardTypeIndex] ?? null)
const canLeaveTimesEmpty = computed(() => {
const cardType = selectedCardType.value
const membership = editingMembership.value
if (!cardType || cardType.type !== 'DURATION') return false
return cardType.totalTimes === null || (
membership?.cardTypeId === cardType.id && membership.remainingTimes === null
)
})
function calculateExpireDate(startDate: string, durationDays: number): string {
@@ -183,11 +196,11 @@ function onMembershipPick(e: { detail: { value: number } }) {
function onCardTypeChange(e: { detail: { value: number } }) {
const idx = Number(e.detail.value)
const cardType = cardTypes.value[idx]
if (!cardType) return
editForm.value.cardTypeIndex = idx
editForm.value.cardTypeId = cardType.id
if (cardType.totalTimes != null) {
editForm.value.remainingTimes = cardType.totalTimes
}
editForm.value.remainingTimes = cardType.totalTimes
if (!editForm.value.manuallyEditedExpire) {
editForm.value.startDate = formatDateLocal(new Date())
editForm.value.expireDate = calculateExpireDate(formatDateLocal(new Date()), cardType.durationDays)
@@ -209,6 +222,17 @@ function onExpireDateChange(e: { detail: { value: string } }) {
editForm.value.manuallyEditedExpire = true
}
function parseRemainingTimes(): number | null | undefined {
const rawValue = editForm.value.remainingTimes
if (rawValue === null || (typeof rawValue === 'string' && rawValue.trim() === '')) {
return null
}
const remainingTimes = Number(rawValue)
if (!Number.isInteger(remainingTimes) || remainingTimes < 0) return undefined
return remainingTimes
}
async function loadPage() {
pageLoading.value = true
try {
@@ -237,6 +261,17 @@ async function onSave() {
uni.showToast({ title: '请选择卡类型', icon: 'none' })
return
}
const remainingTimes = parseRemainingTimes()
if (remainingTimes === undefined) {
uni.showToast({ title: '剩余次数需为非负整数', icon: 'none' })
return
}
if (remainingTimes === null && !canLeaveTimesEmpty.value) {
uni.showToast({ title: '请输入剩余次数', icon: 'none' })
return
}
submitting.value = true
try {
await adminStore.updateMemberProfile(userId.value, {
@@ -246,7 +281,7 @@ async function onSave() {
await adminStore.updateUserMembership(userId.value, {
...(editForm.value.membershipId ? { membershipId: editForm.value.membershipId } : {}),
cardTypeId: editForm.value.cardTypeId,
remainingTimes: isTimeBasedCard.value ? Number(editForm.value.remainingTimes) || 0 : null,
remainingTimes,
startDate: editForm.value.startDate,
expireDate: editForm.value.expireDate,
})

View File

@@ -207,13 +207,13 @@
</view>
<view v-if="cardData.totalTimes" class="feature-item">
<text class="feature-dot"></text>
<text class="feature-text">每次预约扣除 1 次课时次卡</text>
<text class="feature-text">每次预约扣除 1 次课时</text>
</view>
<view v-if="cardData.type === CardTypeCategory.DURATION" class="feature-item">
<text class="feature-dot"></text>
<text class="feature-text">如需退卡已上课时按次卡单价计费扣除不以月卡优惠价结算</text>
</view>
<view v-if="cardData.type === CardTypeCategory.DURATION" class="feature-item">
<view v-if="cardData.type === CardTypeCategory.DURATION && !cardData.totalTimes" class="feature-item">
<text class="feature-dot"></text>
<text class="feature-text">到期后自动失效</text>
</view>