perf: 支持课程补录

This commit is contained in:
richarjiang
2026-09-08 15:08:29 +08:00
parent d1f193e13e
commit b4b3caac70
24 changed files with 1074 additions and 945 deletions

View File

@@ -105,6 +105,12 @@
:refresher-triggered="refreshingHistory"
@refresherrefresh="onRefreshHistory"
>
<view v-if="supplements.length || supplementsError || supplementsLoading" class="supplement-history">
<text class="supplement-heading">历史课程补录</text>
<text v-if="supplementsLoading" class="supplement-message">正在加载补录记录</text>
<button v-else-if="supplementsError" class="supplement-retry" @tap="fetchSupplements">补录记录加载失败点击重试</button>
<LessonSupplementList v-else :records="supplements" />
</view>
<!-- Loading -->
<view v-if="bookingStore.loadingBookings && !refreshingHistory" class="loading-wrap">
<view v-for="i in 3" :key="i" class="skeleton-card">
@@ -117,7 +123,7 @@
</view>
<!-- Empty -->
<view v-else-if="historyBookings.length === 0" class="empty-wrap">
<view v-else-if="historyBookings.length === 0 && !supplements.length && !supplementsLoading && !supplementsError" class="empty-wrap">
<view class="empty-illustration">
<view class="empty-circle outer" />
<view class="empty-circle inner" />
@@ -164,13 +170,26 @@
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import type { BookingWithDetails } from '@mp-pilates/shared'
import type { BookingWithDetails, LessonSupplementRecord } from '@mp-pilates/shared'
import { BookingStatus } from '@mp-pilates/shared'
import { useBookingStore } from '../../stores/booking'
import { formatDate, getWeekdayLabel } from '../../utils/format'
import CustomNavBar from '../../components/CustomNavBar.vue'
import LessonSupplementList from '../../components/LessonSupplementList.vue'
import { get } from '../../utils/request'
const bookingStore = useBookingStore()
const supplements = ref<LessonSupplementRecord[]>([])
const supplementsLoading = ref(false)
const supplementsError = ref(false)
async function fetchSupplements() {
supplementsLoading.value = true
supplementsError.value = false
try { supplements.value = await get<LessonSupplementRecord[]>('/user/lesson-supplements') }
catch { supplementsError.value = true }
finally { supplementsLoading.value = false }
}
// ─── Nav bar height ──────────────────────────────────────
const navBarHeight = ref('64px')
@@ -310,7 +329,7 @@ async function onRefreshUpcoming() {
async function onRefreshHistory() {
refreshingHistory.value = true
await bookingStore.fetchMyBookings()
await Promise.all([bookingStore.fetchMyBookings(), fetchSupplements()])
refreshingHistory.value = false
}
@@ -351,6 +370,7 @@ async function handleCancel(booking: BookingWithDetails) {
// ─── Lifecycle ────────────────────────────────────────────
onMounted(() => {
fetchSupplements()
const windowInfo = uni.getWindowInfo()
const statusBarH = windowInfo.statusBarHeight ?? 20
navBarHeight.value = `${statusBarH + Math.round(88 * windowInfo.windowWidth / 750)}px`
@@ -366,11 +386,17 @@ onMounted(() => {
// for any state we didn't locally patch (e.g. server-side cascading fields).
onShow(() => {
if (!hasLoadedOnce.value) return
fetchSupplements()
bookingStore.fetchMyBookings(undefined, { silent: true })
})
</script>
<style lang="scss" scoped>
.supplement-history { padding: 28rpx 24rpx 0; }
.supplement-heading { display: block; margin-bottom: 20rpx; font-size: 26rpx; color: #786b61; }
.supplement-message, .supplement-retry { font-size: 24rpx; color: #8b817b; }
.supplement-retry { padding: 24rpx; line-height: 1.6; background: #f2e9e3; border-radius: 20rpx; &::after { border: none; } }
.bookings-page {
min-height: 100vh;
background: #f5f3f0;