Files
venue-miniapp/miniprogram/src/pages/comprehensive/membership/available-sessions.vue
T
gqt 6a18d3de61 feat: 页面重写与流程优化
- 门票Tab展示已购门票记录(可核销/已核销/已退款)
- 购买次数去掉合计金额行
- 登录后直接跳profile/detail,合并setup页(不区分模式)
- profile/detail增加返回首页按钮
- 约课页移除UnauthLoginPanel
- 401跳转改为reLaunch到login页,不显示toast
- membership/cards仅展示可用卡,恢复点击进详情
- available-sessions改为只读展示,移除预约逻辑
- profile/orders重写样式,单行状态筛选
- profile/balance重写样式,onShow自动刷新
- profile/index移除冗余菜单项(篮球门票/充值/我的预约/个人信息/切换场馆)
- profile/index移除AgreementLinks,我的次卡改为我的会员卡
- pages.json移除setup页注册
2026-07-13 21:43:59 +08:00

137 lines
3.1 KiB
Vue

<template>
<view class="available-page">
<view v-if="isLoading" class="load-more-tip">加载中...</view>
<view v-else-if="sessions.length" class="session-list">
<view v-for="session in sessions" :key="session.id" class="card session-card">
<image :src="session.cover_image" mode="aspectFill" class="session-cover" />
<view class="session-info">
<text class="session-title">{{ session.title }}</text>
<text class="time-line">{{ formatTimeRange(session.start_at, session.end_at) }}</text>
<text class="meta">{{ session.coach_name }}</text>
<view class="session-bottom">
<text class="meta">剩余 {{ session.remaining_count }}/{{ session.capacity }}</text>
<text class="session-price">¥{{ session.price }}</text>
</view>
</view>
</view>
<view v-if="!hasMore && sessions.length >= pageSize" class="load-more-tip">已加载全部</view>
</view>
<view v-else class="empty-state">暂无可约课程</view>
</view>
</template>
<script setup>
import { computed, ref } from 'vue'
import { onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { membershipApi } from '@/modules/comprehensive/api/membership'
import { formatTimeRange } from '@/modules/comprehensive/utils/date'
const sessions = ref([])
const page = ref(1)
const pageSize = 20
const total = ref(0)
const isLoading = ref(false)
const isLoadingMore = ref(false)
const hasMore = computed(() => sessions.value.length < total.value)
const loadSessions = async (reset) => {
if (reset) {
page.value = 1
isLoading.value = true
} else {
isLoadingMore.value = true
}
try {
const data = await membershipApi.getAvailableSessions({ page: page.value, pageSize })
sessions.value = reset ? data.items : [...sessions.value, ...data.items]
total.value = data.total
} finally {
isLoading.value = false
isLoadingMore.value = false
}
}
onPullDownRefresh(() => {
loadSessions(true).finally(() => uni.stopPullDownRefresh())
})
onReachBottom(() => {
if (!hasMore.value || isLoadingMore.value) return
page.value += 1
loadSessions(false)
})
loadSessions(true)
</script>
<style lang="scss" scoped>
.available-page {
min-height: 100vh;
background: $bg-color-soft;
}
.session-list {
display: flex;
flex-direction: column;
gap: $spacing-md;
padding: $spacing-md;
}
.session-card {
display: flex;
gap: $spacing-md;
}
.session-cover {
width: 180rpx;
height: 172rpx;
flex-shrink: 0;
border-radius: $border-radius-base;
background: $bg-color-hover;
}
.session-info {
flex: 1;
min-width: 0;
}
.session-title {
color: $text-color-main;
font-size: 30rpx;
font-weight: 900;
line-height: 1.35;
display: block;
}
.time-line {
display: block;
margin-top: $spacing-sm;
color: $color-primary;
font-size: 24rpx;
font-weight: 800;
}
.meta {
display: block;
margin-top: 6rpx;
color: $text-color-muted;
font-size: 23rpx;
}
.session-bottom {
display: flex;
justify-content: space-between;
gap: $spacing-sm;
margin-top: 6rpx;
}
.session-price {
color: $text-color-main;
font-size: 28rpx;
font-weight: 900;
}
</style>