fix(app): 优化首页会员卡闪烁和即将上课卡片交互

- CardShop: 采用 stale-while-revalidate 模式,仅首次加载显示骨架屏,
  切换 tab 回来时保留旧数据静默刷新,消除列表闪烁
- UpcomingBooking: 补充 PENDING_CONFIRMATION 状态的中文映射和样式
- UpcomingBooking: 卡片点击跳转到预约详情页
This commit is contained in:
richarjiang
2026-04-10 11:29:09 +08:00
parent 57e3227af0
commit 54e30da003
2 changed files with 21 additions and 2 deletions

View File

@@ -79,16 +79,25 @@ import { formatPrice, getCardCoverClass } from '../utils/format'
const cardTypes = ref<CardType[]>([])
const loading = ref(false)
const hasLoaded = ref(false)
async function fetchCardTypes() {
loading.value = true
// Stale-While-Revalidate: only show skeleton on first load
// Subsequent refreshes silently update data in background
if (!hasLoaded.value) {
loading.value = true
}
try {
const result = await get<CardType[]>('/membership/card-types')
cardTypes.value = result
.filter((c) => c.isActive)
.sort((a, b) => a.sortOrder - b.sortOrder)
hasLoaded.value = true
} catch {
uni.showToast({ title: '加载会员卡失败', icon: 'none' })
// Only show error toast on first load; silent fail on background refresh
if (!hasLoaded.value) {
uni.showToast({ title: '加载会员卡失败', icon: 'none' })
}
} finally {
loading.value = false
}

View File

@@ -9,6 +9,7 @@
v-for="booking in displayedBookings"
:key="booking.id"
class="booking-card"
@tap="goToBookingDetail(booking.id)"
>
<!-- Date column -->
<view class="date-col">
@@ -72,6 +73,7 @@ function formatTime(timeStr: string): string {
function statusLabel(status: BookingStatus): string {
const map: Record<BookingStatus, string> = {
[BookingStatus.PENDING_CONFIRMATION]: '待确认',
[BookingStatus.CONFIRMED]: '已确认',
[BookingStatus.CANCELLED]: '已取消',
[BookingStatus.COMPLETED]: '已完成',
@@ -81,6 +83,7 @@ function statusLabel(status: BookingStatus): string {
}
function statusDotClass(status: BookingStatus): string {
if (status === BookingStatus.PENDING_CONFIRMATION) return 'dot--pending'
if (status === BookingStatus.CONFIRMED) return 'dot--confirmed'
if (status === BookingStatus.COMPLETED) return 'dot--completed'
if (status === BookingStatus.CANCELLED) return 'dot--cancelled'
@@ -88,6 +91,7 @@ function statusDotClass(status: BookingStatus): string {
}
function statusTextClass(status: BookingStatus): string {
if (status === BookingStatus.PENDING_CONFIRMATION) return 'text--pending'
if (status === BookingStatus.CONFIRMED) return 'text--confirmed'
if (status === BookingStatus.COMPLETED) return 'text--completed'
if (status === BookingStatus.CANCELLED) return 'text--cancelled'
@@ -97,6 +101,10 @@ function statusTextClass(status: BookingStatus): string {
function goToBookings() {
uni.navigateTo({ url: '/pages/profile/bookings' })
}
function goToBookingDetail(id: string) {
uni.navigateTo({ url: `/pages/booking/detail?id=${id}` })
}
</script>
<style lang="scss" scoped>
@@ -207,6 +215,7 @@ function goToBookings() {
border-radius: 50%;
}
.dot--pending { background: #f39c12; }
.dot--confirmed { background: #27ae60; }
.dot--completed { background: #3498db; }
.dot--cancelled { background: #e74c3c; }
@@ -217,6 +226,7 @@ function goToBookings() {
color: #999;
}
.text--pending { color: #f39c12; }
.text--confirmed { color: #27ae60; }
.text--completed { color: #3498db; }
.text--cancelled { color: #e74c3c; }