feat(收藏): 实现课程收藏功能并优化相关页面
- 新增 `favorite.js` API 文件,提供收藏列表、新增和取消收藏的接口 - 在 `CourseDetailPage.vue` 中实现收藏和取消收藏的逻辑,并添加交互反馈 - 在 `MyFavoritesPage.vue` 中替换模拟数据为真实接口调用,优化收藏课程加载逻辑 - 调整 `jsconfig.json` 配置文件,包含新增的 API 文件路径
Showing
4 changed files
with
126 additions
and
117 deletions
| ... | @@ -24,6 +24,6 @@ | ... | @@ -24,6 +24,6 @@ |
| 24 | "esModuleInterop": true, | 24 | "esModuleInterop": true, |
| 25 | "skipLibCheck": true | 25 | "skipLibCheck": true |
| 26 | }, | 26 | }, |
| 27 | - "include": ["src/**/*", "build/**/*", "vite.config.js"], | 27 | + "include": ["src/**/*", "build/**/*", "vite.config.js", "src/api/.js"], |
| 28 | "exclude": ["node_modules", "dist"] | 28 | "exclude": ["node_modules", "dist"] |
| 29 | } | 29 | } | ... | ... |
src/api/favorite.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-04-16 16:21:37 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-04-17 10:04:30 | ||
| 5 | + * @FilePath: /mlaj/src/api/favorite.js | ||
| 6 | + * @Description: 收藏相关接口 | ||
| 7 | + */ | ||
| 8 | +import { fn, fetch } from './fn' | ||
| 9 | + | ||
| 10 | +const Api = { | ||
| 11 | + GROUP_FAVORITE_LIST: '/srv/?a=group_favorite_list', | ||
| 12 | + FAVORITE_ADD: '/srv/?a=group_favorite', | ||
| 13 | + FAVORITE_CANCEL: '/srv/?a=group_unfavorite', | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * @description: 获取课程收藏列表 | ||
| 18 | + * @param: page 页码 | ||
| 19 | + * @param: limit 每页数量 | ||
| 20 | + * @return: data: { id: 收藏ID, title: 课程名称, price: 优惠价格, original_price: 原价, feature: 课程特色, highlights: 课程亮点, learning_goal: 学习目标, count: 课程章节数, cover: 封面图 } | ||
| 21 | + */ | ||
| 22 | +export const getGroupFavoriteListAPI = (params) => fn(fetch.get(Api.GROUP_FAVORITE_LIST, params)) | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * @description: 新增收藏 | ||
| 26 | + * @param: group_id 课程ID | ||
| 27 | + * @return: data: { } | ||
| 28 | + */ | ||
| 29 | +export const addFavoriteAPI = (params) => fn(fetch.post(Api.FAVORITE_ADD, params)) | ||
| 30 | + | ||
| 31 | +/** | ||
| 32 | + * @description: 取消收藏 | ||
| 33 | + * @param: group_id 课程ID | ||
| 34 | + * @return: data: { } | ||
| 35 | + */ | ||
| 36 | +export const cancelFavoriteAPI = (params) => fn(fetch.post(Api.FAVORITE_CANCEL, params)) |
| ... | @@ -37,28 +37,20 @@ | ... | @@ -37,28 +37,20 @@ |
| 37 | 37 | ||
| 38 | <!-- Course Image --> | 38 | <!-- Course Image --> |
| 39 | <div class="mb-6"> | 39 | <div class="mb-6"> |
| 40 | - <img | 40 | + <img :src="course?.cover || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" :alt="course?.title" |
| 41 | - :src="course?.cover || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" | 41 | + class="w-full h-auto rounded-xl shadow-md" /> |
| 42 | - :alt="course?.title" | ||
| 43 | - class="w-full h-auto rounded-xl shadow-md" | ||
| 44 | - /> | ||
| 45 | </div> | 42 | </div> |
| 46 | 43 | ||
| 47 | <!-- Tab Navigation --> | 44 | <!-- Tab Navigation --> |
| 48 | <FrostedGlass class="mb-6 rounded-xl overflow-hidden"> | 45 | <FrostedGlass class="mb-6 rounded-xl overflow-hidden"> |
| 49 | <div class="border-b border-gray-200"> | 46 | <div class="border-b border-gray-200"> |
| 50 | <div class="flex"> | 47 | <div class="flex"> |
| 51 | - <button | 48 | + <button v-for="(item, index) in curriculumItems" :key="index" @click="activeTab = item.title" :class="[ |
| 52 | - v-for="(item, index) in curriculumItems" | ||
| 53 | - :key="index" | ||
| 54 | - @click="activeTab = item.title" | ||
| 55 | - :class="[ | ||
| 56 | 'flex-1 py-3 font-medium text-center', | 49 | 'flex-1 py-3 font-medium text-center', |
| 57 | activeTab === item.title | 50 | activeTab === item.title |
| 58 | ? 'text-green-600 border-b-2 border-green-600 bg-green-50/50' | 51 | ? 'text-green-600 border-b-2 border-green-600 bg-green-50/50' |
| 59 | : 'text-gray-500' | 52 | : 'text-gray-500' |
| 60 | - ]" | 53 | + ]"> |
| 61 | - > | ||
| 62 | {{ item.title }} | 54 | {{ item.title }} |
| 63 | </button> | 55 | </button> |
| 64 | </div> | 56 | </div> |
| ... | @@ -83,8 +75,10 @@ | ... | @@ -83,8 +75,10 @@ |
| 83 | <p class="text-sm text-gray-600 mt-1">{{ item.duration }}分钟 · {{ item.schedule_time || 'N/A' }}个小节</p> | 75 | <p class="text-sm text-gray-600 mt-1">{{ item.duration }}分钟 · {{ item.schedule_time || 'N/A' }}个小节</p> |
| 84 | </div> | 76 | </div> |
| 85 | <div v-if="course?.schedule?.length > 4" class="flex justify-center mt-4"> | 77 | <div v-if="course?.schedule?.length > 4" class="flex justify-center mt-4"> |
| 86 | - <button @click="toggleSchedule" class="p-2 rounded-full hover:bg-green-50 text-green-600 hover:text-green-700 transition-all duration-300"> | 78 | + <button @click="toggleSchedule" |
| 87 | - <van-icon :name="isScheduleExpanded ? 'arrow-up' : 'arrow-down'" class="text-xl transform transition-transform duration-300" /> | 79 | + class="p-2 rounded-full hover:bg-green-50 text-green-600 hover:text-green-700 transition-all duration-300"> |
| 80 | + <van-icon :name="isScheduleExpanded ? 'arrow-up' : 'arrow-down'" | ||
| 81 | + class="text-xl transform transition-transform duration-300" /> | ||
| 88 | </button> | 82 | </button> |
| 89 | </div> | 83 | </div> |
| 90 | </div> | 84 | </div> |
| ... | @@ -109,12 +103,8 @@ | ... | @@ -109,12 +103,8 @@ |
| 109 | <h3 class="text-lg font-bold text-gray-800 mb-3">主讲老师</h3> | 103 | <h3 class="text-lg font-bold text-gray-800 mb-3">主讲老师</h3> |
| 110 | <div class="flex items-center"> | 104 | <div class="flex items-center"> |
| 111 | <div class="w-16 h-16 rounded-full overflow-hidden mr-4"> | 105 | <div class="w-16 h-16 rounded-full overflow-hidden mr-4"> |
| 112 | - <img | 106 | + <img :src="teacher?.avatar || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" alt="Teacher" |
| 113 | - :src="teacher?.avatar || 'https://cdn.ipadbiz.cn/mlaj/images/default_block.png'" | 107 | + class="w-full h-full object-cover" @error="handleImageError" /> |
| 114 | - alt="Teacher" | ||
| 115 | - class="w-full h-full object-cover" | ||
| 116 | - @error="handleImageError" | ||
| 117 | - /> | ||
| 118 | </div> | 108 | </div> |
| 119 | <div> | 109 | <div> |
| 120 | <h4 class="font-bold text-gray-900">{{ teacher?.name || '没字段' }}</h4> | 110 | <h4 class="font-bold text-gray-900">{{ teacher?.name || '没字段' }}</h4> |
| ... | @@ -129,17 +119,10 @@ | ... | @@ -129,17 +119,10 @@ |
| 129 | <h3 class="text-lg font-bold text-gray-800 mb-3">学员评价</h3> | 119 | <h3 class="text-lg font-bold text-gray-800 mb-3">学员评价</h3> |
| 130 | <div class="flex items-center mb-3"> | 120 | <div class="flex items-center mb-3"> |
| 131 | <div class="flex text-yellow-400 mr-2"> | 121 | <div class="flex text-yellow-400 mr-2"> |
| 132 | - <svg | 122 | + <svg v-for="star in 5" :key="star" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" |
| 133 | - v-for="star in 5" | 123 | + fill="currentColor"> |
| 134 | - :key="star" | ||
| 135 | - xmlns="http://www.w3.org/2000/svg" | ||
| 136 | - class="h-5 w-5" | ||
| 137 | - viewBox="0 0 20 20" | ||
| 138 | - fill="currentColor" | ||
| 139 | - > | ||
| 140 | <path | 124 | <path |
| 141 | - d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" | 125 | + d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" /> |
| 142 | - /> | ||
| 143 | </svg> | 126 | </svg> |
| 144 | </div> | 127 | </div> |
| 145 | <div class="text-gray-700">4.9 (126条评论)</div> | 128 | <div class="text-gray-700">4.9 (126条评论)</div> |
| ... | @@ -167,10 +150,8 @@ | ... | @@ -167,10 +150,8 @@ |
| 167 | </div> | 150 | </div> |
| 168 | </div> | 151 | </div> |
| 169 | 152 | ||
| 170 | - <button | 153 | + <button @click="router.push(`/courses/${course?.id}/reviews`)" |
| 171 | - @click="router.push(`/courses/${course?.id}/reviews`)" | 154 | + class="w-full text-center text-green-600 mt-3 text-sm"> |
| 172 | - class="w-full text-center text-green-600 mt-3 text-sm" | ||
| 173 | - > | ||
| 174 | 查看全部评价 | 155 | 查看全部评价 |
| 175 | </button> | 156 | </button> |
| 176 | </FrostedGlass> | 157 | </FrostedGlass> |
| ... | @@ -213,20 +194,12 @@ | ... | @@ -213,20 +194,12 @@ |
| 213 | </svg> | 194 | </svg> |
| 214 | 分享 | 195 | 分享 |
| 215 | </button> --> | 196 | </button> --> |
| 216 | - <button class="flex flex-col items-center text-gray-500 text-xs transition-transform duration-300" @click="toggleFavorite" :class="{ 'animate-favorite': isFavorite }"> | 197 | + <button class="flex flex-col items-center text-gray-500 text-xs transition-transform duration-300" |
| 217 | - <svg | 198 | + @click="toggleFavorite" :class="{ 'animate-favorite': isFavorite }"> |
| 218 | - xmlns="http://www.w3.org/2000/svg" | 199 | + <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 transition-transform duration-300" |
| 219 | - class="h-6 w-6 transition-transform duration-300" | 200 | + :fill="isFavorite ? 'red' : 'none'" viewBox="0 0 24 24" :stroke="isFavorite ? 'red' : 'currentColor'"> |
| 220 | - :fill="isFavorite ? 'red' : 'none'" | 201 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" |
| 221 | - viewBox="0 0 24 24" | 202 | + d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" /> |
| 222 | - :stroke="isFavorite ? 'red' : 'currentColor'" | ||
| 223 | - > | ||
| 224 | - <path | ||
| 225 | - stroke-linecap="round" | ||
| 226 | - stroke-linejoin="round" | ||
| 227 | - stroke-width="2" | ||
| 228 | - d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" | ||
| 229 | - /> | ||
| 230 | </svg> | 203 | </svg> |
| 231 | 收藏 | 204 | 收藏 |
| 232 | </button> | 205 | </button> |
| ... | @@ -238,34 +211,16 @@ | ... | @@ -238,34 +211,16 @@ |
| 238 | ¥{{ Math.round((course?.price || 0) * 1.2) }} | 211 | ¥{{ Math.round((course?.price || 0) * 1.2) }} |
| 239 | </div> | 212 | </div> |
| 240 | </div> | 213 | </div> |
| 241 | - <van-button | 214 | + <van-button v-if="!isPurchased" @click="handlePurchase" round block |
| 242 | - v-if="!isPurchased" | 215 | + color="linear-gradient(to right, #22c55e, #16a34a)" class="shadow-md"> |
| 243 | - @click="handlePurchase" | ||
| 244 | - round | ||
| 245 | - block | ||
| 246 | - color="linear-gradient(to right, #22c55e, #16a34a)" | ||
| 247 | - class="shadow-md" | ||
| 248 | - > | ||
| 249 | 立即购买 | 216 | 立即购买 |
| 250 | </van-button> | 217 | </van-button> |
| 251 | - <van-button | 218 | + <van-button v-else-if="!isReviewed" @click="showReviewPopup = true" round block |
| 252 | - v-else-if="!isReviewed" | 219 | + color="linear-gradient(to right, #3b82f6, #2563eb)" class="shadow-md"> |
| 253 | - @click="showReviewPopup = true" | ||
| 254 | - round | ||
| 255 | - block | ||
| 256 | - color="linear-gradient(to right, #3b82f6, #2563eb)" | ||
| 257 | - class="shadow-md" | ||
| 258 | - > | ||
| 259 | 立即评论 | 220 | 立即评论 |
| 260 | </van-button> | 221 | </van-button> |
| 261 | - <van-button | 222 | + <van-button v-else @click="router.push(`/courses/${course?.id}/reviews`)" round block |
| 262 | - v-else | 223 | + color="linear-gradient(to right, #6b7280, #4b5563)" class="shadow-md"> |
| 263 | - @click="router.push(`/courses/${course?.id}/reviews`)" | ||
| 264 | - round | ||
| 265 | - block | ||
| 266 | - color="linear-gradient(to right, #6b7280, #4b5563)" | ||
| 267 | - class="shadow-md" | ||
| 268 | - > | ||
| 269 | 查看评论 | 224 | 查看评论 |
| 270 | </van-button> | 225 | </van-button> |
| 271 | </div> | 226 | </div> |
| ... | @@ -273,10 +228,7 @@ | ... | @@ -273,10 +228,7 @@ |
| 273 | </div> | 228 | </div> |
| 274 | 229 | ||
| 275 | <!-- Review Popup --> | 230 | <!-- Review Popup --> |
| 276 | - <ReviewPopup | 231 | + <ReviewPopup v-model:show="showReviewPopup" @submit="handleReviewSubmit" /> |
| 277 | - v-model:show="showReviewPopup" | ||
| 278 | - @submit="handleReviewSubmit" | ||
| 279 | - /> | ||
| 280 | </AppLayout> | 232 | </AppLayout> |
| 281 | </template> | 233 | </template> |
| 282 | 234 | ||
| ... | @@ -286,13 +238,14 @@ import { useRoute, useRouter } from 'vue-router' | ... | @@ -286,13 +238,14 @@ import { useRoute, useRouter } from 'vue-router' |
| 286 | import AppLayout from '@/components/layout/AppLayout.vue' | 238 | import AppLayout from '@/components/layout/AppLayout.vue' |
| 287 | import FrostedGlass from '@/components/ui/FrostedGlass.vue' | 239 | import FrostedGlass from '@/components/ui/FrostedGlass.vue' |
| 288 | import ReviewPopup from '@/components/ui/ReviewPopup.vue' | 240 | import ReviewPopup from '@/components/ui/ReviewPopup.vue' |
| 289 | -import { courses } from '@/utils/mockData' | 241 | +// import { courses } from '@/utils/mockData' |
| 290 | import { useCart } from '@/contexts/cart' | 242 | import { useCart } from '@/contexts/cart' |
| 291 | import { useTitle } from '@vueuse/core'; | 243 | import { useTitle } from '@vueuse/core'; |
| 292 | import { showToast } from 'vant'; | 244 | import { showToast } from 'vant'; |
| 293 | 245 | ||
| 294 | // 导入接口 | 246 | // 导入接口 |
| 295 | import { getCourseDetailAPI } from "@/api/course"; | 247 | import { getCourseDetailAPI } from "@/api/course"; |
| 248 | +import { addFavoriteAPI, cancelFavoriteAPI } from "@/api/favorite"; | ||
| 296 | 249 | ||
| 297 | const $route = useRoute(); | 250 | const $route = useRoute(); |
| 298 | const $router = useRouter(); | 251 | const $router = useRouter(); |
| ... | @@ -315,9 +268,25 @@ const showReviewPopup = ref(false) | ... | @@ -315,9 +268,25 @@ const showReviewPopup = ref(false) |
| 315 | const { addToCart, proceedToCheckout } = useCart() | 268 | const { addToCart, proceedToCheckout } = useCart() |
| 316 | 269 | ||
| 317 | // Handle favorite toggle | 270 | // Handle favorite toggle |
| 318 | -const toggleFavorite = () => { | 271 | +// 收藏/取消收藏操作 |
| 272 | +const toggleFavorite = async () => { | ||
| 273 | + if (isFavorite.value) { | ||
| 274 | + const { code, msg } = await cancelFavoriteAPI({ | ||
| 275 | + group_id: course.value.id | ||
| 276 | + }) | ||
| 277 | + if (code) { | ||
| 319 | isFavorite.value = !isFavorite.value | 278 | isFavorite.value = !isFavorite.value |
| 320 | - // TODO: 后续对接收藏接口 | 279 | + showToast('取消收藏') |
| 280 | + } | ||
| 281 | + } else { | ||
| 282 | + const { code, msg } = await addFavoriteAPI({ | ||
| 283 | + group_id: course.value.id | ||
| 284 | + }) | ||
| 285 | + if (code) { | ||
| 286 | + isFavorite.value = !isFavorite.value | ||
| 287 | + showToast('收藏成功') | ||
| 288 | + } | ||
| 289 | + } | ||
| 321 | } | 290 | } |
| 322 | 291 | ||
| 323 | // Curriculum items | 292 | // Curriculum items |
| ... | @@ -384,7 +353,7 @@ const handleReviewSubmit = (review) => { | ... | @@ -384,7 +353,7 @@ const handleReviewSubmit = (review) => { |
| 384 | onMounted(async () => { | 353 | onMounted(async () => { |
| 385 | const id = route.params.id | 354 | const id = route.params.id |
| 386 | // 调用接口获取课程详情 | 355 | // 调用接口获取课程详情 |
| 387 | - const res = await getCourseDetailAPI({i: id}); | 356 | + const res = await getCourseDetailAPI({ i: id }); |
| 388 | if (res.code) { | 357 | if (res.code) { |
| 389 | const foundCourse = res.data; | 358 | const foundCourse = res.data; |
| 390 | if (foundCourse) { | 359 | if (foundCourse) { |
| ... | @@ -431,9 +400,11 @@ const toggleSchedule = () => { | ... | @@ -431,9 +400,11 @@ const toggleSchedule = () => { |
| 431 | 0% { | 400 | 0% { |
| 432 | transform: scale(1); | 401 | transform: scale(1); |
| 433 | } | 402 | } |
| 403 | + | ||
| 434 | 50% { | 404 | 50% { |
| 435 | transform: scale(1.3); | 405 | transform: scale(1.3); |
| 436 | } | 406 | } |
| 407 | + | ||
| 437 | 100% { | 408 | 100% { |
| 438 | transform: scale(1); | 409 | transform: scale(1); |
| 439 | } | 410 | } | ... | ... |
| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | <div class="bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen pb-20"> | 2 | <div class="bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen pb-20"> |
| 3 | <!-- 分类切换 --> | 3 | <!-- 分类切换 --> |
| 4 | <div class="px-4 py-3"> | 4 | <div class="px-4 py-3"> |
| 5 | - <van-tabs v-model:active="activeTab" sticky swipeable title-active-color="#4caf50" color="#4caf50"> | 5 | + <van-tabs v-model:active="activeTab" @click-tab="onClickTab" sticky swipeable title-active-color="#4caf50" color="#4caf50"> |
| 6 | <van-tab title="课程" name="courses"> | 6 | <van-tab title="课程" name="courses"> |
| 7 | <van-list | 7 | <van-list |
| 8 | v-model:loading="coursesLoading" | 8 | v-model:loading="coursesLoading" |
| ... | @@ -24,7 +24,7 @@ | ... | @@ -24,7 +24,7 @@ |
| 24 | </van-tab> | 24 | </van-tab> |
| 25 | 25 | ||
| 26 | <van-tab title="活动" name="activities"> | 26 | <van-tab title="活动" name="activities"> |
| 27 | - <van-list | 27 | + <!--<van-list |
| 28 | v-model:loading="activitiesLoading" | 28 | v-model:loading="activitiesLoading" |
| 29 | :finished="activitiesFinished" | 29 | :finished="activitiesFinished" |
| 30 | finished-text="没有更多了" | 30 | finished-text="没有更多了" |
| ... | @@ -34,13 +34,13 @@ | ... | @@ -34,13 +34,13 @@ |
| 34 | <ActivityCard v-for="activity in favoriteActivities" :key="activity.id" :activity="activity" /> | 34 | <ActivityCard v-for="activity in favoriteActivities" :key="activity.id" :activity="activity" /> |
| 35 | </van-list> | 35 | </van-list> |
| 36 | 36 | ||
| 37 | - <!-- 无数据提示 --> | 37 | + <!~~ 无数据提示 ~~> |
| 38 | <div v-if="!activitiesLoading && favoriteActivities.length === 0" class="flex flex-col items-center justify-center py-12"> | 38 | <div v-if="!activitiesLoading && favoriteActivities.length === 0" class="flex flex-col items-center justify-center py-12"> |
| 39 | <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | 39 | <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor"> |
| 40 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" /> | 40 | <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" /> |
| 41 | </svg> | 41 | </svg> |
| 42 | <p class="mt-4 text-gray-500">暂无收藏活动</p> | 42 | <p class="mt-4 text-gray-500">暂无收藏活动</p> |
| 43 | - </div> | 43 | + </div>--> |
| 44 | </van-tab> | 44 | </van-tab> |
| 45 | </van-tabs> | 45 | </van-tabs> |
| 46 | </div> | 46 | </div> |
| ... | @@ -55,6 +55,9 @@ import ActivityCard from '@/components/ui/ActivityCard.vue'; | ... | @@ -55,6 +55,9 @@ import ActivityCard from '@/components/ui/ActivityCard.vue'; |
| 55 | import { courses as mockCourses, activities as mockActivities } from '@/utils/mockData'; | 55 | import { courses as mockCourses, activities as mockActivities } from '@/utils/mockData'; |
| 56 | import { useTitle } from '@vueuse/core'; | 56 | import { useTitle } from '@vueuse/core'; |
| 57 | 57 | ||
| 58 | +// 导入接口 | ||
| 59 | +import { getGroupFavoriteListAPI } from '@/api/favorite'; | ||
| 60 | + | ||
| 58 | const $route = useRoute(); | 61 | const $route = useRoute(); |
| 59 | const $router = useRouter(); | 62 | const $router = useRouter(); |
| 60 | useTitle($route.meta.title); | 63 | useTitle($route.meta.title); |
| ... | @@ -67,8 +70,8 @@ const favoriteActivities = ref([]); | ... | @@ -67,8 +70,8 @@ const favoriteActivities = ref([]); |
| 67 | // 课程列表加载状态 | 70 | // 课程列表加载状态 |
| 68 | const coursesLoading = ref(false); | 71 | const coursesLoading = ref(false); |
| 69 | const coursesFinished = ref(false); | 72 | const coursesFinished = ref(false); |
| 70 | -const coursePage = ref(1); | 73 | +const coursePage = ref(0); |
| 71 | -const coursePageSize = 10; | 74 | +const courseLimit = ref(5); |
| 72 | 75 | ||
| 73 | // 活动列表加载状态 | 76 | // 活动列表加载状态 |
| 74 | const activitiesLoading = ref(false); | 77 | const activitiesLoading = ref(false); |
| ... | @@ -77,42 +80,41 @@ const activitiesPage = ref(1); | ... | @@ -77,42 +80,41 @@ const activitiesPage = ref(1); |
| 77 | const activitiesPageSize = 10; | 80 | const activitiesPageSize = 10; |
| 78 | 81 | ||
| 79 | // 加载收藏课程 | 82 | // 加载收藏课程 |
| 80 | -const onCoursesLoad = () => { | 83 | +const onCoursesLoad = async () => { |
| 81 | - coursesLoading.value = true; | 84 | + const nextPage = coursePage.value; |
| 82 | - // 模拟异步加载 | 85 | + const res = await getGroupFavoriteListAPI({ limit: courseLimit.value, page: nextPage }); |
| 83 | - setTimeout(() => { | 86 | + if (res.code) { |
| 84 | - const start = (coursePage.value - 1) * coursePageSize; | 87 | + favoriteCourses.value = [...favoriteCourses.value, ...res.data]; |
| 85 | - const end = start + coursePageSize; | 88 | + coursesFinished.value = res.data.length < courseLimit.value; |
| 86 | - const newCourses = mockCourses.slice(start, end); | 89 | + coursePage.value = nextPage + 1; |
| 87 | - | ||
| 88 | - favoriteCourses.value.push(...newCourses); | ||
| 89 | - coursesLoading.value = false; | ||
| 90 | - | ||
| 91 | - if (newCourses.length < coursePageSize) { | ||
| 92 | - coursesFinished.value = true; | ||
| 93 | - } else { | ||
| 94 | - coursePage.value += 1; | ||
| 95 | } | 90 | } |
| 96 | - }, 1000); | 91 | + coursesLoading.value = false; |
| 97 | }; | 92 | }; |
| 98 | 93 | ||
| 99 | // 加载收藏活动 | 94 | // 加载收藏活动 |
| 100 | -const onActivitiesLoad = () => { | 95 | +// const onActivitiesLoad = () => { |
| 101 | - activitiesLoading.value = true; | 96 | +// activitiesLoading.value = true; |
| 102 | - // 模拟异步加载 | 97 | +// // 模拟异步加载 |
| 103 | - setTimeout(() => { | 98 | +// setTimeout(() => { |
| 104 | - const start = (activitiesPage.value - 1) * activitiesPageSize; | 99 | +// const start = (activitiesPage.value - 1) * activitiesPageSize; |
| 105 | - const end = start + activitiesPageSize; | 100 | +// const end = start + activitiesPageSize; |
| 106 | - const newActivities = mockActivities.slice(start, end); | 101 | +// const newActivities = mockActivities.slice(start, end); |
| 102 | + | ||
| 103 | +// favoriteActivities.value.push(...newActivities); | ||
| 104 | +// activitiesLoading.value = false; | ||
| 107 | 105 | ||
| 108 | - favoriteActivities.value.push(...newActivities); | 106 | +// if (newActivities.length < activitiesPageSize) { |
| 109 | - activitiesLoading.value = false; | 107 | +// activitiesFinished.value = true; |
| 108 | +// } else { | ||
| 109 | +// activitiesPage.value += 1; | ||
| 110 | +// } | ||
| 111 | +// }, 1000); | ||
| 112 | +// }; | ||
| 110 | 113 | ||
| 111 | - if (newActivities.length < activitiesPageSize) { | 114 | +// 切换标签页 |
| 112 | - activitiesFinished.value = true; | 115 | +const onClickTab = ({ name, title }) => { |
| 113 | - } else { | 116 | + if (name === 'activities') { |
| 114 | - activitiesPage.value += 1; | 117 | + location.href = 'http://www.baidu.com' |
| 115 | } | 118 | } |
| 116 | - }, 1000); | ||
| 117 | }; | 119 | }; |
| 118 | </script> | 120 | </script> | ... | ... |
-
Please register or login to post a comment