feat: migrate comprehensive venue module
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<view class="detail-page page-bottom-safe" v-if="course">
|
||||
<image :src="course.cover_image" mode="aspectFill" class="hero-image" />
|
||||
<view class="content">
|
||||
<view class="title-card card">
|
||||
<view class="title-row">
|
||||
<text class="course-title">{{ course.title }}</text>
|
||||
</view>
|
||||
<view class="price-row">
|
||||
<text class="price-text">¥{{ course.price }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="info-card card">
|
||||
<view v-for="item in infoItems" :key="item.label" class="info-row">
|
||||
<text class="info-label">{{ item.label }}</text>
|
||||
<text class="info-value">{{ item.value }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="section-header">
|
||||
<text class="section-title">课程详情</text>
|
||||
</view>
|
||||
<view class="rich-card card">
|
||||
<rich-text :nodes="course.detail_html"></rich-text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="fixed-action-bar">
|
||||
<button class="primary-button submit-btn" :disabled="!canBook" @tap="goConfirm">
|
||||
{{ canBook ? '立即预约' : '暂不可约' }}
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { courseApi } from '@/modules/comprehensive/api/course'
|
||||
import { formatTimeRange } from '@/modules/comprehensive/utils/date'
|
||||
|
||||
const course = ref(null)
|
||||
|
||||
const canBook = computed(() => {
|
||||
return course.value && course.value.remaining_count > 0 && course.value.status === 'published'
|
||||
})
|
||||
|
||||
const infoItems = computed(() => {
|
||||
if (!course.value) return []
|
||||
return [
|
||||
{ label: '上课时间', value: formatTimeRange(course.value.start_at, course.value.end_at) },
|
||||
{ label: '授课教练', value: course.value.coach_name },
|
||||
{ label: '上课场地', value: course.value.courts.map((item) => item.name).join('、') }
|
||||
]
|
||||
})
|
||||
|
||||
onLoad(async (query) => {
|
||||
course.value = await courseApi.getCourseDetail(query.id)
|
||||
})
|
||||
|
||||
const goConfirm = () => {
|
||||
if (!canBook.value) return
|
||||
uni.navigateTo({ url: `/pages/comprehensive/courses/confirm?id=${course.value.id}` })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-page {
|
||||
min-height: 100vh;
|
||||
background: $bg-color-soft;
|
||||
}
|
||||
|
||||
.hero-image {
|
||||
width: 100%;
|
||||
height: 420rpx;
|
||||
background: $bg-color-hover;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: $spacing-md;
|
||||
}
|
||||
|
||||
.title-card {
|
||||
margin-top: -54rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.title-row,
|
||||
.price-row,
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: $spacing-md;
|
||||
}
|
||||
|
||||
.course-title {
|
||||
flex: 1;
|
||||
color: $text-color-main;
|
||||
font-size: 38rpx;
|
||||
font-weight: 900;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.price-row {
|
||||
margin-top: $spacing-md;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: $text-color-muted;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.info-card,
|
||||
.rich-card {
|
||||
margin-top: $spacing-md;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
min-height: 72rpx;
|
||||
border-bottom: 1rpx solid $bg-color-hover;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
flex: 1;
|
||||
color: $text-color-main;
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.rich-card {
|
||||
color: $text-color-regular;
|
||||
font-size: 28rpx;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user