hookehuyr

feat(SummerCampCard): 添加轮播功能并更新数据传递方式

为 SummerCampCard 组件添加 Swiper 轮播功能,支持多张图片切换。同时,将静态数据改为通过 props 传递,提高组件的灵活性和复用性。
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
10 "dependencies": { 10 "dependencies": {
11 "@vant/use": "^1.6.0", 11 "@vant/use": "^1.6.0",
12 "dayjs": "^1.11.13", 12 "dayjs": "^1.11.13",
13 + "swiper": "^11.2.6",
13 "vant": "^4.9.18", 14 "vant": "^4.9.18",
14 "vconsole": "^3.15.1", 15 "vconsole": "^3.15.1",
15 "vue": "^3.5.13", 16 "vue": "^3.5.13",
...@@ -3342,6 +3343,24 @@ ...@@ -3342,6 +3343,24 @@
3342 "url": "https://github.com/sponsors/ljharb" 3343 "url": "https://github.com/sponsors/ljharb"
3343 } 3344 }
3344 }, 3345 },
3346 + "node_modules/swiper": {
3347 + "version": "11.2.6",
3348 + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.6.tgz",
3349 + "integrity": "sha512-8aXpYKtjy3DjcbzZfz+/OX/GhcU5h+looA6PbAzHMZT6ESSycSp9nAjPCenczgJyslV+rUGse64LMGpWE3PX9Q==",
3350 + "funding": [
3351 + {
3352 + "type": "patreon",
3353 + "url": "https://www.patreon.com/swiperjs"
3354 + },
3355 + {
3356 + "type": "open_collective",
3357 + "url": "http://opencollective.com/swiper"
3358 + }
3359 + ],
3360 + "engines": {
3361 + "node": ">= 4.7.0"
3362 + }
3363 + },
3345 "node_modules/tailwindcss": { 3364 "node_modules/tailwindcss": {
3346 "version": "3.4.17", 3365 "version": "3.4.17",
3347 "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", 3366 "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
11 "dependencies": { 11 "dependencies": {
12 "@vant/use": "^1.6.0", 12 "@vant/use": "^1.6.0",
13 "dayjs": "^1.11.13", 13 "dayjs": "^1.11.13",
14 + "swiper": "^11.2.6",
14 "vant": "^4.9.18", 15 "vant": "^4.9.18",
15 "vconsole": "^3.15.1", 16 "vconsole": "^3.15.1",
16 "vue": "^3.5.13", 17 "vue": "^3.5.13",
......
1 <template> 1 <template>
2 <div class="relative overflow-hidden rounded-b-3xl shadow-lg"> 2 <div class="relative overflow-hidden rounded-b-3xl shadow-lg">
3 - <!-- Background image with overlay --> 3 + <div class="swiper-container relative" ref="swiperRef">
4 - <div 4 + <div class="swiper-wrapper">
5 - class="absolute inset-0 z-0 bg-cover bg-center" 5 + <div v-for="(item, index) in items" :key="index" class="swiper-slide">
6 - :style="{ 6 + <!-- Background image with overlay -->
7 - backgroundImage: `url('https://cdn.ipadbiz.cn/mlaj/images/summer-camp.jpg')`, 7 + <div
8 - filter: 'brightness(0.4)' 8 + class="absolute inset-0 z-0 bg-cover bg-center"
9 - }" 9 + :style="{
10 - ></div> 10 + backgroundImage: `url(${item.imageUrl || 'https://cdn.ipadbiz.cn/mlaj/images/summer-camp.jpg'})`,
11 + filter: 'brightness(0.4)'
12 + }"
13 + ></div>
11 14
12 - <!-- Gradient overlay --> 15 + <!-- Gradient overlay -->
13 - <div class="absolute inset-0 z-1 bg-gradient-to-b from-red-500/70 to-red-600/90"></div> 16 + <div class="absolute inset-0 z-1 bg-gradient-to-b from-red-500/70 to-red-600/90"></div>
14 17
15 - <!-- Content --> 18 + <!-- Content -->
16 - <div class="relative z-10 p-4"> 19 + <div class="relative z-10 p-4">
17 - <div class="bg-white/10 backdrop-blur-sm rounded-lg p-3 mb-3 inline-block"> 20 + <div class="bg-white/10 backdrop-blur-sm rounded-lg p-3 mb-3 inline-block">
18 - <div class="text-white font-semibold">{{ badge }}</div> 21 + <div class="text-white font-semibold">{{ item.badge }}</div>
19 - </div> 22 + </div>
20 23
21 - <h1 class="text-2xl text-white font-bold mb-1">{{ title }}</h1> 24 + <h1 class="text-2xl text-white font-bold mb-1">{{ item.title }}</h1>
22 - <h2 class="text-lg text-white/90">{{ subtitle }}</h2> 25 + <h2 class="text-lg text-white/90">{{ item.subtitle }}</h2>
23 26
24 - <div class="mt-4 flex justify-between items-center"> 27 + <div class="mt-4 flex justify-between items-center">
25 - <div class="text-orange-300 font-bold text-2xl">{{ price }}</div> 28 + <div class="text-orange-300 font-bold text-2xl">{{ item.price }}</div>
26 - <div class="bg-orange-500/30 text-orange-100 text-xs px-3 py-1 rounded-full">{{ discount }}</div> 29 + <div class="bg-orange-500/30 text-orange-100 text-xs px-3 py-1 rounded-full">{{ item.discount }}</div>
27 - </div> 30 + </div>
28 31
29 - <div class="flex justify-between text-xs text-white/80 mt-3"> 32 + <div class="flex justify-between text-xs text-white/80 mt-3">
30 - <div>已更新{{ episodes }}期</div> 33 + <div>已更新{{ item.episodes }}期</div>
31 - <div>{{ subscribers }}人订阅</div> 34 + <div>{{ item.subscribers }}人订阅</div>
35 + </div>
36 + </div>
37 + </div>
32 </div> 38 </div>
39 + <!-- Pagination -->
40 + <div class="swiper-pagination"></div>
33 </div> 41 </div>
34 </div> 42 </div>
35 </template> 43 </template>
36 44
37 <script setup> 45 <script setup>
38 -import { defineProps } from 'vue' 46 +import { defineProps, onMounted, ref } from 'vue'
47 +import Swiper from 'swiper'
48 +import { Pagination } from 'swiper/modules'
49 +import 'swiper/css'
50 +import 'swiper/css/pagination'
39 51
40 -defineProps({ 52 +const props = defineProps({
41 - title: { 53 + items: {
42 - type: String, 54 + type: Array,
43 - default: '大国少年-世界正东方' 55 + default: () => [{
44 - }, 56 + title: '大国少年-世界正东方',
45 - subtitle: { 57 + subtitle: '亲子夏令营',
46 - type: String, 58 + badge: '亲子夏令营',
47 - default: '亲子夏令营' 59 + price: '¥1280',
48 - }, 60 + discount: '限时优惠',
49 - badge: { 61 + episodes: 16,
50 - type: String, 62 + subscribers: 1140,
51 - default: '亲子夏令营' 63 + imageUrl: 'https://cdn.ipadbiz.cn/mlaj/images/summer-camp.jpg'
52 - }, 64 + }]
53 - price: {
54 - type: String,
55 - default: '¥1280'
56 - },
57 - discount: {
58 - type: String,
59 - default: '限时优惠'
60 - },
61 - episodes: {
62 - type: Number,
63 - default: 16
64 - },
65 - subscribers: {
66 - type: Number,
67 - default: 1140
68 } 65 }
69 }) 66 })
67 +
68 +const swiperRef = ref(null)
69 +
70 +onMounted(() => {
71 + new Swiper(swiperRef.value, {
72 + modules: [Pagination],
73 + pagination: {
74 + el: '.swiper-pagination',
75 + clickable: true
76 + },
77 + loop: true
78 + })
79 +})
70 </script> 80 </script>
81 +
82 +<style scoped>
83 +.swiper-container {
84 + width: 100%;
85 + height: 100%;
86 +}
87 +
88 +.swiper-slide {
89 + height: auto;
90 +}
91 +
92 +.swiper-pagination {
93 + bottom: 10px !important;
94 +}
95 +
96 +.swiper-pagination-bullet {
97 + background: white !important;
98 + opacity: 0.5;
99 +}
100 +
101 +.swiper-pagination-bullet-active {
102 + opacity: 1;
103 +}
104 +</style>
......
1 <!-- 1 <!--
2 * @Date: 2025-03-20 19:55:21 2 * @Date: 2025-03-20 19:55:21
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2025-03-20 23:13:20 4 + * @LastEditTime: 2025-03-21 09:35:57
5 * @FilePath: /mlaj/src/views/HomePage.vue 5 * @FilePath: /mlaj/src/views/HomePage.vue
6 * @Description: 文件描述 6 * @Description: 文件描述
7 --> 7 -->
...@@ -25,13 +25,13 @@ ...@@ -25,13 +25,13 @@
25 <p class="text-sm text-gray-500">{{ formatToday() }}</p> 25 <p class="text-sm text-gray-500">{{ formatToday() }}</p>
26 </div> 26 </div>
27 </div> 27 </div>
28 - <div class="flex items-center text-sm"> 28 + <!-- <div class="flex items-center text-sm">
29 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> 29 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
30 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" /> 30 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
31 </svg> 31 </svg>
32 <span class="ml-1 font-medium">23°C</span> 32 <span class="ml-1 font-medium">23°C</span>
33 <span class="ml-1 text-gray-500">晴朗</span> 33 <span class="ml-1 text-gray-500">晴朗</span>
34 - </div> 34 + </div> -->
35 </div> 35 </div>
36 36
37 <!-- User Stats --> 37 <!-- User Stats -->
...@@ -40,14 +40,14 @@ ...@@ -40,14 +40,14 @@
40 <div class="text-lg font-bold">3</div> 40 <div class="text-lg font-bold">3</div>
41 <div class="text-xs text-gray-500">连续打卡</div> 41 <div class="text-xs text-gray-500">连续打卡</div>
42 </div> 42 </div>
43 - <div class="border-r border-gray-200 flex-1"> 43 + <div class="border-gray-200 flex-1">
44 <div class="text-lg font-bold">12</div> 44 <div class="text-lg font-bold">12</div>
45 <div class="text-xs text-gray-500">已学课程</div> 45 <div class="text-xs text-gray-500">已学课程</div>
46 </div> 46 </div>
47 - <div class="flex-1"> 47 + <!-- <div class="flex-1">
48 <div class="text-lg font-bold">25</div> 48 <div class="text-lg font-bold">25</div>
49 <div class="text-xs text-gray-500">积分</div> 49 <div class="text-xs text-gray-500">积分</div>
50 - </div> 50 + </div> -->
51 </div> 51 </div>
52 </FrostedGlass> 52 </FrostedGlass>
53 53
...@@ -127,7 +127,38 @@ ...@@ -127,7 +127,38 @@
127 127
128 <!-- Summer Camp Promotion --> 128 <!-- Summer Camp Promotion -->
129 <div class="px-4 mb-6"> 129 <div class="px-4 mb-6">
130 - <SummerCampCard /> 130 + <SummerCampCard :items="[
131 + {
132 + title: '大国少年-世界正东方',
133 + subtitle: '亲子夏令营',
134 + badge: '亲子夏令营',
135 + price: '¥1280',
136 + discount: '限时优惠',
137 + episodes: 16,
138 + subscribers: 1140,
139 + imageUrl: 'https://cdn.ipadbiz.cn/mlaj/images/summer-camp.jpg'
140 + },
141 + {
142 + title: '暑期特训营',
143 + subtitle: '提升学习能力',
144 + badge: '特训营',
145 + price: '¥1580',
146 + discount: '早鸟优惠',
147 + episodes: 20,
148 + subscribers: 980,
149 + imageUrl: 'https://cdn.ipadbiz.cn/mlaj/images/summer-camp-2.jpg'
150 + },
151 + {
152 + title: '艺术创想营',
153 + subtitle: '激发创造力',
154 + badge: '艺术营',
155 + price: '¥1380',
156 + discount: '新课优惠',
157 + episodes: 12,
158 + subscribers: 760,
159 + imageUrl: 'https://cdn.ipadbiz.cn/mlaj/images/summer-camp-3.jpg'
160 + }
161 + ]" />
131 </div> 162 </div>
132 163
133 <!-- Featured Courses Carousel --> 164 <!-- Featured Courses Carousel -->
......