feat(home): 添加轮播图组件并重构首页banner逻辑
重构首页banner展示逻辑,将轮播图抽离为独立组件BannerSwiper 新增支持多种类型的banner点击处理(图片预览、文章弹窗、车辆详情) 添加mock数据作为banner备用数据源 移除不再使用的console.warn调试代码
Showing
4 changed files
with
180 additions
and
29 deletions
| ... | @@ -7,6 +7,7 @@ export {} | ... | @@ -7,6 +7,7 @@ export {} |
| 7 | 7 | ||
| 8 | declare module 'vue' { | 8 | declare module 'vue' { |
| 9 | export interface GlobalComponents { | 9 | export interface GlobalComponents { |
| 10 | + BannerSwiper: typeof import('./src/components/BannerSwiper.vue')['default'] | ||
| 10 | BrandModelPicker: typeof import('./src/components/BrandModelPicker.vue')['default'] | 11 | BrandModelPicker: typeof import('./src/components/BrandModelPicker.vue')['default'] |
| 11 | FeaturedRecommendations: typeof import('./src/components/FeaturedRecommendations.vue')['default'] | 12 | FeaturedRecommendations: typeof import('./src/components/FeaturedRecommendations.vue')['default'] |
| 12 | LatestScooters: typeof import('./src/components/LatestScooters.vue')['default'] | 13 | LatestScooters: typeof import('./src/components/LatestScooters.vue')['default'] | ... | ... |
src/components/BannerSwiper.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2025-01-20 10:00:00 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-07-24 14:20:22 | ||
| 5 | + * @FilePath: /jgdl/src/components/BannerSwiper.vue | ||
| 6 | + * @Description: 轮播图组件 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <view class="px-4 pt-4" style="background: linear-gradient( 180deg, #fb923c 0%, rgba(255,203,53,0) 61%);"> | ||
| 10 | + <nut-swiper :init-page="0" :pagination-visible="true" pagination-color="#ffffff" auto-play="3000" | ||
| 11 | + class="rounded-lg overflow-hidden" height="160"> | ||
| 12 | + <nut-swiper-item v-for="(item, index) in bannerList" :key="index" @click="onBannerClick(item)"> | ||
| 13 | + <image :src="item.image" mode="aspectFill" class="w-full h-40 object-cover" /> | ||
| 14 | + </nut-swiper-item> | ||
| 15 | + </nut-swiper> | ||
| 16 | + </view> | ||
| 17 | + | ||
| 18 | + <!-- 图片预览组件 --> | ||
| 19 | + <nut-image-preview | ||
| 20 | + v-model:show="showImagePreview" | ||
| 21 | + :images="previewImages" | ||
| 22 | + :init-no="currentImageIndex" | ||
| 23 | + /> | ||
| 24 | + | ||
| 25 | + <!-- 文章弹框组件 --> | ||
| 26 | + <nut-popup | ||
| 27 | + v-model:visible="showArticleModal" | ||
| 28 | + position="bottom" | ||
| 29 | + :style="{ height: '100vh' }" | ||
| 30 | + close-icon-position="bottom-right" | ||
| 31 | + :safe-area-inset-bottom="true" | ||
| 32 | + > | ||
| 33 | + <view class="article-modal"> | ||
| 34 | + <!-- <view class="article-header p-4 border-b"> | ||
| 35 | + <text class="text-lg font-bold">{{ currentArticle.title }}</text> | ||
| 36 | + </view> --> | ||
| 37 | + <scroll-view :scroll-y="true" :catch-move="true" class="article-content" style="height: calc(100vh - 140rpx);"> | ||
| 38 | + <view class="p-4"> | ||
| 39 | + <rich-text :nodes="currentArticle.content"></rich-text> | ||
| 40 | + </view> | ||
| 41 | + </scroll-view> | ||
| 42 | + <view class="article-footer p-4"> | ||
| 43 | + <nut-button type="primary" block color="#fb923c" @click="closeArticleModal"> | ||
| 44 | + 关闭 | ||
| 45 | + </nut-button> | ||
| 46 | + </view> | ||
| 47 | + </view> | ||
| 48 | + </nut-popup> | ||
| 49 | +</template> | ||
| 50 | + | ||
| 51 | +<script setup> | ||
| 52 | +import Taro from '@tarojs/taro' | ||
| 53 | +import { ref } from 'vue' | ||
| 54 | + | ||
| 55 | +// 定义props | ||
| 56 | +defineProps({ | ||
| 57 | + bannerList: { | ||
| 58 | + type: Array, | ||
| 59 | + default: () => [] | ||
| 60 | + } | ||
| 61 | +}) | ||
| 62 | + | ||
| 63 | +// 定义emits | ||
| 64 | +const emit = defineEmits(['bannerClick']) | ||
| 65 | + | ||
| 66 | +// 响应式数据 | ||
| 67 | +const showImagePreview = ref(false) | ||
| 68 | +const previewImages = ref([]) | ||
| 69 | +const currentImageIndex = ref(0) | ||
| 70 | +const showArticleModal = ref(false) | ||
| 71 | +const currentArticle = ref({ | ||
| 72 | + title: '', | ||
| 73 | + content: '' | ||
| 74 | +}) | ||
| 75 | + | ||
| 76 | +/** | ||
| 77 | + * 点击轮播图处理函数 | ||
| 78 | + */ | ||
| 79 | +const onBannerClick = (item) => { | ||
| 80 | + switch (item.type) { | ||
| 81 | + case 'poster': | ||
| 82 | + // 海报图 - 显示图片预览 | ||
| 83 | + previewImages.value = [item.image] | ||
| 84 | + currentImageIndex.value = 0 | ||
| 85 | + showImagePreview.value = true | ||
| 86 | + break | ||
| 87 | + case 'article': | ||
| 88 | + // 文章封面图 - 显示文章弹框 | ||
| 89 | + currentArticle.value = { | ||
| 90 | + title: item.title, | ||
| 91 | + content: item.content | ||
| 92 | + } | ||
| 93 | + showArticleModal.value = true | ||
| 94 | + break | ||
| 95 | + case 'vehicle': | ||
| 96 | + // 车辆封面图 - 跳转到车辆详情页 | ||
| 97 | + Taro.navigateTo({ | ||
| 98 | + url: `/pages/productDetail/index?id=${item.id}` | ||
| 99 | + }) | ||
| 100 | + break | ||
| 101 | + default: | ||
| 102 | + console.warn('未知的轮播图类型:', item.type) | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + // 触发父组件事件 | ||
| 106 | + emit('bannerClick', item) | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +/** | ||
| 110 | + * 关闭文章弹框 | ||
| 111 | + */ | ||
| 112 | +const closeArticleModal = () => { | ||
| 113 | + showArticleModal.value = false | ||
| 114 | +} | ||
| 115 | +</script> | ||
| 116 | + | ||
| 117 | +<style scoped> | ||
| 118 | +.article-modal { | ||
| 119 | + height: 100vh; | ||
| 120 | + display: flex; | ||
| 121 | + flex-direction: column; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +.article-content { | ||
| 125 | + overflow-y: auto; | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +.article-footer { | ||
| 129 | + position: sticky; | ||
| 130 | + bottom: 0; | ||
| 131 | + left: 0; | ||
| 132 | + right: 0; | ||
| 133 | + background: white; | ||
| 134 | + border-top: 1px solid #eee; | ||
| 135 | + padding: 16px; | ||
| 136 | + z-index: 1000; | ||
| 137 | + margin-top: auto; | ||
| 138 | +} | ||
| 139 | +</style> |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-06-28 10:33:00 | 2 | * @Date: 2025-06-28 10:33:00 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-07-18 17:11:10 | 4 | + * @LastEditTime: 2025-07-24 14:21:19 |
| 5 | * @FilePath: /jgdl/src/pages/index/index.vue | 5 | * @FilePath: /jgdl/src/pages/index/index.vue |
| 6 | * @Description: 捡个电驴首页 | 6 | * @Description: 捡个电驴首页 |
| 7 | --> | 7 | --> |
| ... | @@ -28,14 +28,7 @@ | ... | @@ -28,14 +28,7 @@ |
| 28 | </nut-sticky> | 28 | </nut-sticky> |
| 29 | 29 | ||
| 30 | <!-- Banner --> | 30 | <!-- Banner --> |
| 31 | - <view class="px-4 pt-4" style="background: linear-gradient( 180deg, #fb923c 0%, rgba(255,203,53,0) 61%);"> | 31 | + <BannerSwiper :banner-list="bannerList" /> |
| 32 | - <nut-swiper :init-page="0" :pagination-visible="true" pagination-color="#ffffff" auto-play="3000" | ||
| 33 | - class="rounded-lg overflow-hidden" height="160"> | ||
| 34 | - <nut-swiper-item v-for="(image, index) in bannerImages" :key="index" @click="onBannerClick(image)"> | ||
| 35 | - <image :src="image.front_photo" mode="aspectFill" class="w-full h-40 object-cover" /> | ||
| 36 | - </nut-swiper-item> | ||
| 37 | - </nut-swiper> | ||
| 38 | - </view> | ||
| 39 | 32 | ||
| 40 | <!-- Category Icons --> | 33 | <!-- Category Icons --> |
| 41 | <view class="px-4 mt-2"> | 34 | <view class="px-4 mt-2"> |
| ... | @@ -87,10 +80,10 @@ import TabBar from '@/components/TabBar.vue' | ... | @@ -87,10 +80,10 @@ import TabBar from '@/components/TabBar.vue' |
| 87 | import SearchPopup from '@/components/SearchPopup.vue' | 80 | import SearchPopup from '@/components/SearchPopup.vue' |
| 88 | import FeaturedRecommendations from '@/components/FeaturedRecommendations.vue' | 81 | import FeaturedRecommendations from '@/components/FeaturedRecommendations.vue' |
| 89 | import LatestScooters from '@/components/LatestScooters.vue' | 82 | import LatestScooters from '@/components/LatestScooters.vue' |
| 83 | +import BannerSwiper from '@/components/BannerSwiper.vue' | ||
| 90 | import "./index.less"; | 84 | import "./index.less"; |
| 91 | // 导入接口 | 85 | // 导入接口 |
| 92 | import { getRecommendVehicleAPI } from '@/api/car'; | 86 | import { getRecommendVehicleAPI } from '@/api/car'; |
| 93 | -import { DEFAULT_COVER_IMG } from '@/utils/config' | ||
| 94 | // 响应式数据 | 87 | // 响应式数据 |
| 95 | const searchValue = ref('') | 88 | const searchValue = ref('') |
| 96 | const showSearchPopup = ref(false) | 89 | const showSearchPopup = ref(false) |
| ... | @@ -100,8 +93,31 @@ const onSearchHandle = () => { | ... | @@ -100,8 +93,31 @@ const onSearchHandle = () => { |
| 100 | showSearchPopup.value = true | 93 | showSearchPopup.value = true |
| 101 | } | 94 | } |
| 102 | 95 | ||
| 103 | -// Banner图片 | 96 | +// Banner数据 |
| 104 | -const bannerImages = ref([]) | 97 | +const bannerList = ref([]) |
| 98 | + | ||
| 99 | +// Mock轮播图数据 | ||
| 100 | +const mockBannerData = [ | ||
| 101 | + { | ||
| 102 | + id: 1, | ||
| 103 | + type: 'poster', | ||
| 104 | + image: 'https://picsum.photos/400/160?random=1', | ||
| 105 | + title: '海报图片' | ||
| 106 | + }, | ||
| 107 | + { | ||
| 108 | + id: 2, | ||
| 109 | + type: 'article', | ||
| 110 | + image: 'https://picsum.photos/400/160?random=2', | ||
| 111 | + title: '电动车保养指南', | ||
| 112 | + content: '<h2>电动车保养指南</h2><p>电动车作为现代出行的重要工具,正确的保养方式能够延长其使用寿命。</p><h3>1. 电池保养</h3><p>• 避免过度充电和过度放电</p><p>• 定期检查电池连接线是否松动</p><p>• 保持电池清洁干燥</p><h3>2. 轮胎保养</h3><p>• 定期检查轮胎气压</p><p>• 检查轮胎磨损情况</p><p>• 及时更换磨损严重的轮胎</p><h3>3. 刹车系统</h3><p>• 定期检查刹车片厚度</p><p>• 检查刹车线是否正常</p><p>• 保持刹车系统清洁</p><p>通过以上保养措施,您的电动车将为您提供更安全、更持久的服务。</p><p>电动车作为现代出行的重要工具,正确的保养方式能够延长其使用寿命。</p><h3>1. 电池保养</h3><p>• 避免过度充电和过度放电</p><p>• 定期检查电池连接线是否松动</p><p>• 保持电池清洁干燥</p><h3>2. 轮胎保养</h3><p>• 定期检查轮胎气压</p><p>• 检查轮胎磨损情况</p><p>• 及时更换磨损严重的轮胎</p><h3>3. 刹车系统</h3><p>• 定期检查刹车片厚度</p><p>• 检查刹车线是否正常</p><p>• 保持刹车系统清洁</p><p>通过以上保养措施,您的电动车将为您提供更安全、更持久的服务。</p>' | ||
| 113 | + }, | ||
| 114 | + { | ||
| 115 | + id: 3, | ||
| 116 | + type: 'vehicle', | ||
| 117 | + image: 'https://picsum.photos/400/160?random=3', | ||
| 118 | + title: '精品二手车' | ||
| 119 | + } | ||
| 120 | +] | ||
| 105 | 121 | ||
| 106 | /** | 122 | /** |
| 107 | * 点击认证车源 | 123 | * 点击认证车源 |
| ... | @@ -179,17 +195,18 @@ useReady(async () => { | ... | @@ -179,17 +195,18 @@ useReady(async () => { |
| 179 | onMounted(async () => { | 195 | onMounted(async () => { |
| 180 | // 获取首页轮播 | 196 | // 获取首页轮播 |
| 181 | const res1 = await getRecommendVehicleAPI({ section: 1 }) | 197 | const res1 = await getRecommendVehicleAPI({ section: 1 }) |
| 182 | - if (res1.code) { | 198 | + if (res1.code && res1.data.list.length) { |
| 183 | - bannerImages.value = res1.data.list.map(item => ({ | 199 | + // 将API数据转换为车辆类型的轮播图 |
| 200 | + const vehicleBanners = res1.data.list.map(item => ({ | ||
| 184 | id: item.id, | 201 | id: item.id, |
| 185 | - front_photo: item.front_photo | 202 | + type: 'vehicle', |
| 186 | - })); | 203 | + image: item.front_photo, |
| 187 | - if (!bannerImages.value.length) { | 204 | + title: '精品二手车' |
| 188 | - bannerImages.value = [{ | 205 | + })) |
| 189 | - id: 0, | 206 | + bannerList.value = vehicleBanners |
| 190 | - front_photo: DEFAULT_COVER_IMG | 207 | + } else { |
| 191 | - }] | 208 | + // 使用mock数据 |
| 192 | - } | 209 | + bannerList.value = mockBannerData |
| 193 | } | 210 | } |
| 194 | }) | 211 | }) |
| 195 | 212 | ||
| ... | @@ -217,10 +234,5 @@ useShareAppMessage(() => { | ... | @@ -217,10 +234,5 @@ useShareAppMessage(() => { |
| 217 | return shareObj; | 234 | return shareObj; |
| 218 | }) | 235 | }) |
| 219 | 236 | ||
| 220 | -// 点击banner图片跳转到详情页 | 237 | + |
| 221 | -const onBannerClick = ({id}) => { | ||
| 222 | - Taro.navigateTo({ | ||
| 223 | - url: `/pages/productDetail/index?id=${id}` | ||
| 224 | - }) | ||
| 225 | -} | ||
| 226 | </script> | 238 | </script> | ... | ... |
| ... | @@ -119,7 +119,6 @@ const scrollTop = ref(0) | ... | @@ -119,7 +119,6 @@ const scrollTop = ref(0) |
| 119 | // 搜索值 | 119 | // 搜索值 |
| 120 | const searchValue = ref('') | 120 | const searchValue = ref('') |
| 121 | const onBlurSearch = async () => { | 121 | const onBlurSearch = async () => { |
| 122 | - console.warn(searchValue.value) | ||
| 123 | // 重置分页状态 | 122 | // 重置分页状态 |
| 124 | page.value = 1 | 123 | page.value = 1 |
| 125 | hasMore.value = true | 124 | hasMore.value = true | ... | ... |
-
Please register or login to post a comment