修正多处问题
This commit is contained in:
@@ -228,16 +228,36 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 支付密码弹窗 -->
|
||||
<PayPasswordModal
|
||||
:visible="showPayPwd"
|
||||
title="支付验证"
|
||||
:subtitle="course.courseName"
|
||||
:amount="course.storedValueAmount || 0"
|
||||
amount-label="课程金额"
|
||||
@confirm="onPayPasswordConfirm"
|
||||
@cancel="onPayPasswordCancel"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getGroupCourseDetail, bookGroupCourse } from '@/api/groupCourse.js'
|
||||
import { getPrimaryMemberCard } from '@/api/main.js'
|
||||
import { getMemberId } from '@/utils/request.js'
|
||||
import PageHeader from '@/components/index/PageHeader.vue'
|
||||
import PayPasswordModal from '@/components/global/PayPasswordModal.vue'
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(true)
|
||||
|
||||
// 支付密码弹窗状态
|
||||
const showPayPwd = ref(false)
|
||||
const payPwdLoading = ref(false)
|
||||
let payPwdResolve = null
|
||||
|
||||
// 课程详情数据
|
||||
const course = ref({
|
||||
id: '',
|
||||
@@ -373,48 +393,89 @@ const formatDuration = (startStr, endStr) => {
|
||||
}
|
||||
|
||||
// 预约处理
|
||||
const handleBooking = () => {
|
||||
const handleBooking = async () => {
|
||||
if (!canBook.value) return
|
||||
|
||||
const memberId = getMemberId()
|
||||
if (!memberId) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
uni.showModal({
|
||||
title: '确认预约',
|
||||
content: `确定要预约「${course.value.courseName}」吗?`,
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
uni.showLoading({ title: '预约中...' })
|
||||
bookGroupCourse({
|
||||
courseId: course.value.id,
|
||||
memberId: '1', // 模拟会员ID
|
||||
memberCardRecordId: '1' // 模拟会员卡记录ID
|
||||
}).then((result) => {
|
||||
content: `确定要预约「${course.value.courseName}」吗?${course.value.storedValueAmount > 0 ? '\n金额: ¥' + course.value.storedValueAmount : ''}`,
|
||||
success: async (res) => {
|
||||
if (!res.confirm) return
|
||||
|
||||
// 弹出支付密码输入
|
||||
const payPassword = await requestPayPassword()
|
||||
if (!payPassword) return
|
||||
|
||||
uni.showLoading({ title: '预约中...' })
|
||||
try {
|
||||
const cardRes = await getPrimaryMemberCard(memberId)
|
||||
const memberCardRecordId = cardRes?.id || cardRes?.data?.id
|
||||
if (!memberCardRecordId) {
|
||||
uni.hideLoading()
|
||||
if (result.success) {
|
||||
uni.showToast({
|
||||
title: '预约成功',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result.message || '预约失败',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
uni.hideLoading()
|
||||
console.error('[detail.vue] 预约失败:', error)
|
||||
uni.showToast({ title: '请先购买会员卡', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const result = await bookGroupCourse({
|
||||
courseId: Number(course.value.id),
|
||||
memberId: Number(memberId),
|
||||
memberCardRecordId: Number(memberCardRecordId),
|
||||
payPassword: payPassword
|
||||
})
|
||||
uni.hideLoading()
|
||||
if (result.success) {
|
||||
uni.showToast({
|
||||
title: '预约失败',
|
||||
icon: 'none'
|
||||
title: '预约成功',
|
||||
icon: 'success'
|
||||
})
|
||||
setTimeout(() => {
|
||||
uni.redirectTo({ url: '/pages/memberInfo/myCourses' })
|
||||
}, 1500)
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: result.message || '预约失败',
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
uni.hideLoading()
|
||||
console.error('[detail.vue] 预约失败:', error)
|
||||
const errMsg = error?.message || error?.data?.message || '预约失败'
|
||||
uni.showToast({
|
||||
title: errMsg,
|
||||
icon: 'none',
|
||||
duration: 3000
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 弹出支付密码输入框,返回 Promise<string|null>
|
||||
function requestPayPassword() {
|
||||
return new Promise((resolve) => {
|
||||
payPwdResolve = resolve
|
||||
showPayPwd.value = true
|
||||
})
|
||||
}
|
||||
|
||||
function onPayPasswordConfirm(password) {
|
||||
payPwdResolve?.(password)
|
||||
showPayPwd.value = false
|
||||
}
|
||||
|
||||
function onPayPasswordCancel() {
|
||||
payPwdResolve?.(null)
|
||||
showPayPwd.value = false
|
||||
}
|
||||
|
||||
// 获取课程详情
|
||||
const fetchCourseDetail = async (id) => {
|
||||
try {
|
||||
@@ -439,15 +500,13 @@ const fetchCourseDetail = async (id) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 页面挂载时获取课程详情
|
||||
onMounted(() => {
|
||||
const pages = getCurrentPages()
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const options = currentPage.options || {}
|
||||
const courseId = options.id || '1'
|
||||
|
||||
console.log('[detail.vue] 页面挂载,课程ID:', courseId)
|
||||
fetchCourseDetail(courseId)
|
||||
// 页面加载时获取课程详情
|
||||
onLoad((options) => {
|
||||
const courseId = options.id
|
||||
console.log('[detail.vue] onLoad,课程ID:', courseId)
|
||||
if (courseId) {
|
||||
fetchCourseDetail(courseId)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -963,7 +1022,10 @@ onMounted(() => {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* 骨架屏样式 */
|
||||
.booking-btn.disabled text {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.skeleton {
|
||||
padding-bottom: calc(140rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
|
||||
|
||||
@@ -32,6 +32,18 @@
|
||||
@change="onTimePeriodChange"
|
||||
ref="timePeriodRef"
|
||||
/>
|
||||
|
||||
<!-- 常态化团课筛选按钮 -->
|
||||
<view class="recurring-filter">
|
||||
<view
|
||||
class="recurring-btn"
|
||||
:class="{ 'recurring-btn--active': isRecurring }"
|
||||
@tap="toggleRecurring"
|
||||
>
|
||||
<text class="recurring-icon">{{ isRecurring ? '✓' : '' }}</text>
|
||||
<text class="recurring-text">常态化团课</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 团课列表 -->
|
||||
@@ -46,6 +58,7 @@
|
||||
:key="course.id"
|
||||
:course="course"
|
||||
:courseTypeLabels="getCourseLabels(course)"
|
||||
:booked="isCourseBooked(course.id)"
|
||||
@booking="handleBooking"
|
||||
@detail="goDetail"
|
||||
></GroupCourseCard>
|
||||
@@ -74,6 +87,7 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { onShow } from '@dcloudio/uni-app'
|
||||
import TabBar from '@/components/TabBar.vue'
|
||||
import GroupCourseCard from '@/components/groupCourse/CourseCard.vue'
|
||||
import SearchBar from '@/components/groupCourse/SearchBar.vue'
|
||||
@@ -82,7 +96,8 @@ import TimePeriodSelector from '@/components/groupCourse/TimePeriodSelector.vue'
|
||||
import TimeRangePicker from '@/components/groupCourse/TimeRangePicker.vue'
|
||||
import PageHeader from '@/components/index/PageHeader.vue'
|
||||
import { useGroupCourseList } from '@/composables/useGroupCourseList.js'
|
||||
import { getTypeLabels, getGroupCourseTypes } from '@/api/groupCourse.js'
|
||||
import { getTypeLabels, getGroupCourseTypes, getMemberBookings } from '@/api/groupCourse.js'
|
||||
import { getMemberId } from '@/utils/request.js'
|
||||
|
||||
// 组件引用
|
||||
const searchBarRef = ref(null)
|
||||
@@ -93,6 +108,9 @@ const timeRangePickerRef = ref(null)
|
||||
// 课程类型标签缓存
|
||||
const courseTypeLabelsCache = ref({})
|
||||
|
||||
// 已预约课程ID集合
|
||||
const bookedCourseIds = ref(new Set())
|
||||
|
||||
// 使用组合式函数
|
||||
const {
|
||||
// 状态
|
||||
@@ -106,6 +124,7 @@ const {
|
||||
sortIndex,
|
||||
timePeriodOptions,
|
||||
timePeriodIndex,
|
||||
isRecurring,
|
||||
showTimePicker,
|
||||
startDate,
|
||||
endDate,
|
||||
@@ -126,13 +145,20 @@ const {
|
||||
onScrollToLower
|
||||
} = useGroupCourseList()
|
||||
|
||||
// 切换常态化团课筛选
|
||||
const toggleRecurring = () => {
|
||||
isRecurring.value = !isRecurring.value
|
||||
fetchCourseList()
|
||||
}
|
||||
|
||||
// 组件挂载时调用接口获取团课列表
|
||||
onMounted(async () => {
|
||||
console.log('[list.vue] 页面组件已挂载,开始获取团课列表')
|
||||
// 并行获取课程类型列表和课程列表
|
||||
// 并行获取课程类型列表、课程列表和已预约课程
|
||||
await Promise.all([
|
||||
fetchCourseTypes(),
|
||||
fetchCourseList()
|
||||
fetchCourseList(),
|
||||
fetchBookedCourses()
|
||||
])
|
||||
// 获取所有团课的类型标签
|
||||
await fetchAllCourseTypeLabels()
|
||||
@@ -144,6 +170,12 @@ onMounted(async () => {
|
||||
console.log(' - getAllSearchParams() 获取所有参数')
|
||||
})
|
||||
|
||||
// 页面显示时刷新已预约状态(从其他页面返回时)
|
||||
onShow(async () => {
|
||||
console.log('[list.vue] onShow 刷新已预约状态')
|
||||
await fetchBookedCourses()
|
||||
})
|
||||
|
||||
const fetchCourseTypes = async () => {
|
||||
try {
|
||||
const result = await getGroupCourseTypes()
|
||||
@@ -159,6 +191,33 @@ const fetchCourseTypes = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const fetchBookedCourses = async () => {
|
||||
const memberId = getMemberId()
|
||||
if (!memberId) {
|
||||
console.log('[list.vue] 未登录,跳过获取已预约课程')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await getMemberBookings(memberId)
|
||||
const bookings = Array.isArray(res) ? res : (res?.data || res?.content || [])
|
||||
if (Array.isArray(bookings)) {
|
||||
const ids = new Set()
|
||||
bookings.forEach(b => {
|
||||
// 只统计状态为正常的预约(status='0'),排除已取消(status='1')
|
||||
if (b.courseId && String(b.status) === '0') ids.add(Number(b.courseId))
|
||||
})
|
||||
bookedCourseIds.value = ids
|
||||
console.log('[list.vue] 已预约课程ID:', [...ids])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[list.vue] 获取已预约课程失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const isCourseBooked = (courseId) => {
|
||||
return bookedCourseIds.value.has(Number(courseId))
|
||||
}
|
||||
|
||||
const fetchAllCourseTypeLabels = async () => {
|
||||
const types = [...new Set(filteredCourseList.value.map(c => c.courseType))]
|
||||
for (const type of types) {
|
||||
@@ -227,4 +286,34 @@ defineExpose({
|
||||
.load-more-text {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 常态化团课筛选按钮 */
|
||||
.recurring-filter {
|
||||
padding-top: 8rpx;
|
||||
}
|
||||
|
||||
.recurring-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 12rpx 28rpx;
|
||||
border-radius: 32rpx;
|
||||
border: 2rpx solid #e0e0e0;
|
||||
background: #fff;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.recurring-btn--active {
|
||||
background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
|
||||
border-color: #66bb6a;
|
||||
color: #2e7d32;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.recurring-icon {
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user