fix(app): 优化首页会员卡闪烁和即将上课卡片交互
- CardShop: 采用 stale-while-revalidate 模式,仅首次加载显示骨架屏, 切换 tab 回来时保留旧数据静默刷新,消除列表闪烁 - UpcomingBooking: 补充 PENDING_CONFIRMATION 状态的中文映射和样式 - UpcomingBooking: 卡片点击跳转到预约详情页
This commit is contained in:
@@ -79,16 +79,25 @@ import { formatPrice, getCardCoverClass } from '../utils/format'
|
|||||||
|
|
||||||
const cardTypes = ref<CardType[]>([])
|
const cardTypes = ref<CardType[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const hasLoaded = ref(false)
|
||||||
|
|
||||||
async function fetchCardTypes() {
|
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 {
|
try {
|
||||||
const result = await get<CardType[]>('/membership/card-types')
|
const result = await get<CardType[]>('/membership/card-types')
|
||||||
cardTypes.value = result
|
cardTypes.value = result
|
||||||
.filter((c) => c.isActive)
|
.filter((c) => c.isActive)
|
||||||
.sort((a, b) => a.sortOrder - b.sortOrder)
|
.sort((a, b) => a.sortOrder - b.sortOrder)
|
||||||
|
hasLoaded.value = true
|
||||||
} catch {
|
} 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 {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
v-for="booking in displayedBookings"
|
v-for="booking in displayedBookings"
|
||||||
:key="booking.id"
|
:key="booking.id"
|
||||||
class="booking-card"
|
class="booking-card"
|
||||||
|
@tap="goToBookingDetail(booking.id)"
|
||||||
>
|
>
|
||||||
<!-- Date column -->
|
<!-- Date column -->
|
||||||
<view class="date-col">
|
<view class="date-col">
|
||||||
@@ -72,6 +73,7 @@ function formatTime(timeStr: string): string {
|
|||||||
|
|
||||||
function statusLabel(status: BookingStatus): string {
|
function statusLabel(status: BookingStatus): string {
|
||||||
const map: Record<BookingStatus, string> = {
|
const map: Record<BookingStatus, string> = {
|
||||||
|
[BookingStatus.PENDING_CONFIRMATION]: '待确认',
|
||||||
[BookingStatus.CONFIRMED]: '已确认',
|
[BookingStatus.CONFIRMED]: '已确认',
|
||||||
[BookingStatus.CANCELLED]: '已取消',
|
[BookingStatus.CANCELLED]: '已取消',
|
||||||
[BookingStatus.COMPLETED]: '已完成',
|
[BookingStatus.COMPLETED]: '已完成',
|
||||||
@@ -81,6 +83,7 @@ function statusLabel(status: BookingStatus): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function statusDotClass(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.CONFIRMED) return 'dot--confirmed'
|
||||||
if (status === BookingStatus.COMPLETED) return 'dot--completed'
|
if (status === BookingStatus.COMPLETED) return 'dot--completed'
|
||||||
if (status === BookingStatus.CANCELLED) return 'dot--cancelled'
|
if (status === BookingStatus.CANCELLED) return 'dot--cancelled'
|
||||||
@@ -88,6 +91,7 @@ function statusDotClass(status: BookingStatus): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function statusTextClass(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.CONFIRMED) return 'text--confirmed'
|
||||||
if (status === BookingStatus.COMPLETED) return 'text--completed'
|
if (status === BookingStatus.COMPLETED) return 'text--completed'
|
||||||
if (status === BookingStatus.CANCELLED) return 'text--cancelled'
|
if (status === BookingStatus.CANCELLED) return 'text--cancelled'
|
||||||
@@ -97,6 +101,10 @@ function statusTextClass(status: BookingStatus): string {
|
|||||||
function goToBookings() {
|
function goToBookings() {
|
||||||
uni.navigateTo({ url: '/pages/profile/bookings' })
|
uni.navigateTo({ url: '/pages/profile/bookings' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function goToBookingDetail(id: string) {
|
||||||
|
uni.navigateTo({ url: `/pages/booking/detail?id=${id}` })
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -207,6 +215,7 @@ function goToBookings() {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dot--pending { background: #f39c12; }
|
||||||
.dot--confirmed { background: #27ae60; }
|
.dot--confirmed { background: #27ae60; }
|
||||||
.dot--completed { background: #3498db; }
|
.dot--completed { background: #3498db; }
|
||||||
.dot--cancelled { background: #e74c3c; }
|
.dot--cancelled { background: #e74c3c; }
|
||||||
@@ -217,6 +226,7 @@ function goToBookings() {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text--pending { color: #f39c12; }
|
||||||
.text--confirmed { color: #27ae60; }
|
.text--confirmed { color: #27ae60; }
|
||||||
.text--completed { color: #3498db; }
|
.text--completed { color: #3498db; }
|
||||||
.text--cancelled { color: #e74c3c; }
|
.text--cancelled { color: #e74c3c; }
|
||||||
|
|||||||
Reference in New Issue
Block a user