整合api请求、添加购买会员卡页面、登陆页面
This commit is contained in:
@@ -231,11 +231,7 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { onPullDownRefresh } from '@dcloudio/uni-app'
|
||||
|
||||
// 测试模式
|
||||
const TEST_MODE = true
|
||||
|
||||
|
||||
import { getGroupCoursePage } from '@/api/main.js'
|
||||
|
||||
// 搜索关键词
|
||||
const keyword = ref('')
|
||||
@@ -395,6 +391,19 @@ const getCourseTypeName = (courseType) => {
|
||||
return typeMap[courseType] || '其他'
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算课程时长
|
||||
*/
|
||||
const calculateDuration = (startTime, endTime) => {
|
||||
if (!startTime || !endTime) return '60分钟'
|
||||
const start = new Date(startTime)
|
||||
const end = new Date(endTime)
|
||||
const diffMs = end - start
|
||||
if (diffMs <= 0) return ''
|
||||
const durationMinutes = Math.floor(diffMs / (1000 * 60))
|
||||
return `${durationMinutes}分钟`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取课程标签
|
||||
*/
|
||||
@@ -415,195 +424,6 @@ const getTagType = (course) => {
|
||||
return 'default'
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成单条课程数据
|
||||
*/
|
||||
const generateSingleCourse = (seed, filterParams) => {
|
||||
// 使用确定性随机函数
|
||||
const random = (n) => {
|
||||
const x = Math.sin(seed + n) * 10000
|
||||
return x - Math.floor(x)
|
||||
}
|
||||
|
||||
const prefixes = [
|
||||
'燃脂', '塑形', '增肌', '柔韧', '平衡', '协调', '爆发', '耐力', '核心', '康复',
|
||||
'晨练', '午间', '晚训', '周末', '精品', '强化', '入门', '进阶', '突破', '蜕变',
|
||||
'活力', '轻盈', '力量', '身心', '自由', '挑战', '超越', '燃烧', '唤醒', '放松',
|
||||
'瑜伽', '普拉提', '舞蹈', '搏击', '骑行', '拉伸', '冥想'
|
||||
]
|
||||
const suffixes = [
|
||||
'HIIT训练', '力量训练', '有氧操', '瑜伽课', '普拉提', '舞蹈课', '搏击操',
|
||||
'动感单车', '杠铃课', '私教课', '团课', '课程', '训练营', '特训班', '体验课',
|
||||
'拉伸课', '冥想课', '综合课', '专项课', '公开课'
|
||||
]
|
||||
const levels = ['初级', '中级', '高级']
|
||||
const courseTypes = ['1', '2', '3', '4', '5']
|
||||
const durations = [20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90]
|
||||
|
||||
// 根据筛选条件调整数据生成
|
||||
let levelValue = levels[Math.floor(random(1) * levels.length)]
|
||||
let courseType = courseTypes[Math.floor(random(2) * courseTypes.length)]
|
||||
let durationValue = durations[Math.floor(random(3) * durations.length)]
|
||||
let prefix = prefixes[Math.floor(random(4) * prefixes.length)]
|
||||
let suffix = suffixes[Math.floor(random(5) * suffixes.length)]
|
||||
|
||||
// 如果筛选了级别,强制匹配
|
||||
if (filterParams.level) {
|
||||
levelValue = filterParams.level
|
||||
}
|
||||
|
||||
// 如果筛选了时长,强制匹配
|
||||
if (filterParams.duration) {
|
||||
switch (filterParams.duration) {
|
||||
case 'short':
|
||||
durationValue = Math.floor(random(6) * 11) + 20 // 20-30分钟
|
||||
break
|
||||
case 'medium':
|
||||
durationValue = Math.floor(random(7) * 31) + 31 // 31-60分钟
|
||||
break
|
||||
case 'long':
|
||||
durationValue = Math.floor(random(8) * 41) + 61 // 61-100分钟
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果筛选了快捷类型,调整课程名称和类型
|
||||
if (filterParams.filter && filterParams.filter !== 'all') {
|
||||
switch (filterParams.filter) {
|
||||
case 'yoga':
|
||||
prefix = '瑜伽'
|
||||
suffix = '冥想课'
|
||||
courseType = '1'
|
||||
break
|
||||
case 'strength':
|
||||
prefix = '力量'
|
||||
suffix = '训练课'
|
||||
courseType = '3'
|
||||
break
|
||||
case 'cardio':
|
||||
prefix = '有氧'
|
||||
suffix = '燃脂课'
|
||||
courseType = '2'
|
||||
break
|
||||
case 'dance':
|
||||
prefix = '舞蹈'
|
||||
suffix = '律动课'
|
||||
courseType = '4'
|
||||
break
|
||||
case 'pilates':
|
||||
prefix = '普拉提'
|
||||
suffix = '核心课'
|
||||
courseType = '5'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有关键词搜索,调整名称
|
||||
let courseName = `${prefix}${suffix}`
|
||||
if (filterParams.keyword && filterParams.keyword.trim()) {
|
||||
if (courseName.includes(filterParams.keyword) || Math.random() > 0.5) {
|
||||
courseName = `${filterParams.keyword}${courseName}`
|
||||
}
|
||||
}
|
||||
|
||||
const maxMembers = Math.floor(random(9) * 30) + 10
|
||||
const currentMembers = Math.floor(random(10) * maxMembers)
|
||||
const isNew = random(11) > 0.7
|
||||
|
||||
return {
|
||||
id: Math.floor(seed),
|
||||
courseName: courseName,
|
||||
coverImage: `https://picsum.photos/id/${Math.floor(random(12) * 100) + 1}/400/300`,
|
||||
duration: durationValue,
|
||||
level: levelValue,
|
||||
currentMembers: currentMembers,
|
||||
maxMembers: maxMembers,
|
||||
courseType: courseType,
|
||||
status: '1',
|
||||
isNew: isNew
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机课程数据(保证每页都有数据)
|
||||
*/
|
||||
const generateMockDataForPage = (page, size, filterParams) => {
|
||||
console.log(`[生成数据] 第 ${page} 页,每页 ${size} 条,筛选条件:`, filterParams)
|
||||
|
||||
const coursesData = []
|
||||
const baseSeed = page * size * 10007 + Date.now()
|
||||
|
||||
// 生成指定数量的课程(确保每页都有数据)
|
||||
for (let i = 0; i < size; i++) {
|
||||
const seed = baseSeed + i * 12345
|
||||
const course = generateSingleCourse(seed, filterParams)
|
||||
coursesData.push(course)
|
||||
}
|
||||
|
||||
// 应用关键词搜索(在已生成数据中过滤)
|
||||
let filteredCourses = [...coursesData]
|
||||
|
||||
if (filterParams.keyword && filterParams.keyword.trim()) {
|
||||
const kw = filterParams.keyword.trim().toLowerCase()
|
||||
filteredCourses = filteredCourses.filter(c =>
|
||||
(c.courseName || '').toLowerCase().includes(kw)
|
||||
)
|
||||
}
|
||||
|
||||
// 如果关键词过滤后没有数据,生成一些匹配关键词的数据
|
||||
if (filteredCourses.length === 0 && filterParams.keyword && filterParams.keyword.trim()) {
|
||||
console.log(`关键词 "${filterParams.keyword}" 无匹配数据,生成匹配数据`)
|
||||
for (let i = 0; i < size; i++) {
|
||||
filteredCourses.push({
|
||||
id: Math.floor(Math.random() * 100000) + page * size + i,
|
||||
courseName: `${filterParams.keyword}课程${i + 1}`,
|
||||
coverImage: `https://picsum.photos/id/${(i % 50) + 1}/400/300`,
|
||||
duration: filterParams.duration === 'short' ? 25 : (filterParams.duration === 'medium' ? 45 : 60),
|
||||
level: filterParams.level || (i % 3 === 0 ? '初级' : (i % 3 === 1 ? '中级' : '高级')),
|
||||
currentMembers: Math.floor(Math.random() * 30) + 1,
|
||||
maxMembers: 30,
|
||||
courseType: '1',
|
||||
status: '1',
|
||||
isNew: true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 排序
|
||||
filteredCourses.sort((a, b) => {
|
||||
switch (filterParams.sort) {
|
||||
case 'participants': return b.currentMembers - a.currentMembers
|
||||
case 'startTime': return b.id - a.id
|
||||
case 'duration': return a.duration - b.duration
|
||||
default: return a.id - b.id
|
||||
}
|
||||
})
|
||||
|
||||
// 转换为显示格式
|
||||
const formattedCourses = filteredCourses.map(c => ({
|
||||
id: c.id,
|
||||
image: c.coverImage,
|
||||
tag: getTag(c),
|
||||
tagType: getTagType(c),
|
||||
courseType: getCourseTypeName(c.courseType),
|
||||
name: c.courseName,
|
||||
duration: `${c.duration}分钟`,
|
||||
level: c.level,
|
||||
participants: c.currentMembers,
|
||||
rawData: c
|
||||
}))
|
||||
|
||||
// 永远返回 true,实现真正的无限滚动
|
||||
const hasMoreData = true
|
||||
|
||||
console.log(`[生成数据完成] 第 ${page} 页生成了 ${formattedCourses.length} 条数据, hasMore=${hasMoreData}`)
|
||||
|
||||
return {
|
||||
content: formattedCourses,
|
||||
hasMore: hasMoreData
|
||||
}
|
||||
}
|
||||
|
||||
// 获取课程列表
|
||||
const fetchCourses = async (searchKeyword = '', filter = 'all', page = 0, size = 10, append = false) => {
|
||||
// 防止重复请求
|
||||
@@ -620,40 +440,39 @@ const fetchCourses = async (searchKeyword = '', filter = 'all', page = 0, size =
|
||||
try {
|
||||
let result
|
||||
|
||||
// 构建筛选参数对象
|
||||
const filterParams = {
|
||||
keyword: searchKeyword,
|
||||
filter: filter,
|
||||
level: selectedLevelValue.value,
|
||||
duration: selectedDurationValue.value,
|
||||
sort: selectedSortValue.value
|
||||
// 构建请求参数
|
||||
const params = {
|
||||
page: page,
|
||||
size: size,
|
||||
sort: selectedSortValue.value || 'id',
|
||||
order: 'desc',
|
||||
keyword: searchKeyword
|
||||
}
|
||||
|
||||
if (TEST_MODE) {
|
||||
// 模拟网络延迟
|
||||
const delay = 500 + Math.random() * 500
|
||||
await new Promise(resolve => setTimeout(resolve, delay))
|
||||
|
||||
result = generateMockDataForPage(page, size, filterParams)
|
||||
} else {
|
||||
// TODO: 替换为真实API调用
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
result = generateMockDataForPage(page, size, filterParams)
|
||||
}
|
||||
// 调用真实API
|
||||
result = await getGroupCoursePage(params)
|
||||
|
||||
// 检查是否为最后一页
|
||||
const isLastPage = result.hasMore === false
|
||||
|
||||
if (result && result.content && result.content.length > 0) {
|
||||
// 课程对象直接包含图片URL
|
||||
const processedCourses = result.content.map(course => ({
|
||||
// 筛选掉已结束和已满员的课程
|
||||
const filteredCourses = result.content.filter(course => {
|
||||
return course.status !== '2' && course.currentMembers < course.maxMembers
|
||||
})
|
||||
|
||||
// 将后端返回的数据转换为前端需要的格式
|
||||
const processedCourses = filteredCourses.map(course => ({
|
||||
id: course.id,
|
||||
image: course.image,
|
||||
tag: course.tag,
|
||||
tagType: course.tagType,
|
||||
courseType: course.courseType,
|
||||
name: course.name,
|
||||
duration: course.duration,
|
||||
level: course.level,
|
||||
participants: course.participants,
|
||||
rawData: course.rawData
|
||||
image: course.coverImage || '',
|
||||
tag: getTag(course),
|
||||
tagType: getTagType(course),
|
||||
courseType: getCourseTypeName(course.courseType),
|
||||
name: course.courseName || '未知课程',
|
||||
duration: calculateDuration(course.startTime, course.endTime),
|
||||
level: course.level || '初级',
|
||||
participants: course.currentMembers || 0,
|
||||
rawData: course
|
||||
}))
|
||||
|
||||
if (append) {
|
||||
@@ -672,13 +491,28 @@ const fetchCourses = async (searchKeyword = '', filter = 'all', page = 0, size =
|
||||
}
|
||||
|
||||
// 更新是否有更多数据
|
||||
hasMore.value = result.hasMore !== false
|
||||
hasMore.value = !isLastPage && processedCourses.length > 0
|
||||
|
||||
// 如果是最后一页且没有更多数据,显示提示
|
||||
if (isLastPage) {
|
||||
uni.showToast({
|
||||
title: '已经到底了',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// 没有数据时,确保不显示骨架屏
|
||||
skeletonCount.value = 0
|
||||
if (!append && courses.value.length === 0) {
|
||||
// 没有数据时,确保显示空状态,但 hasMore 设为 false 防止无限加载
|
||||
hasMore.value = false
|
||||
hasMore.value = false
|
||||
|
||||
// 如果是最后一页,显示提示
|
||||
if (isLastPage && courses.value.length > 0) {
|
||||
uni.showToast({
|
||||
title: '已经到底了',
|
||||
icon: 'none',
|
||||
duration: 2000
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -727,14 +561,10 @@ const loadMore = () => {
|
||||
const nextPage = currentPage.value + 1
|
||||
currentPage.value = nextPage
|
||||
|
||||
// 加载更多时添加骨架屏
|
||||
skeletonCount.value += pageSize
|
||||
|
||||
fetchCourses(keyword.value, activeFilter.value, nextPage, pageSize, true)
|
||||
.catch((err) => {
|
||||
console.error('加载更多失败:', err)
|
||||
loadingMore.value = false
|
||||
skeletonCount.value = Math.max(0, skeletonCount.value - pageSize)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -771,7 +601,7 @@ const toggleExpand = () => {
|
||||
// 课程点击
|
||||
const handleCourseClick = (course) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/course/detail?id=${course.id}`
|
||||
url: `/pages/memberInfo/courseDetail?id=${course.id}`
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user