hookehuyr

feat(home): 添加轮播图组件并重构首页banner逻辑

重构首页banner展示逻辑,将轮播图抽离为独立组件BannerSwiper
新增支持多种类型的banner点击处理(图片预览、文章弹窗、车辆详情)
添加mock数据作为banner备用数据源
移除不再使用的console.warn调试代码
......@@ -7,6 +7,7 @@ export {}
declare module 'vue' {
export interface GlobalComponents {
BannerSwiper: typeof import('./src/components/BannerSwiper.vue')['default']
BrandModelPicker: typeof import('./src/components/BrandModelPicker.vue')['default']
FeaturedRecommendations: typeof import('./src/components/FeaturedRecommendations.vue')['default']
LatestScooters: typeof import('./src/components/LatestScooters.vue')['default']
......
<!--
* @Date: 2025-01-20 10:00:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-24 14:20:22
* @FilePath: /jgdl/src/components/BannerSwiper.vue
* @Description: 轮播图组件
-->
<template>
<view class="px-4 pt-4" style="background: linear-gradient( 180deg, #fb923c 0%, rgba(255,203,53,0) 61%);">
<nut-swiper :init-page="0" :pagination-visible="true" pagination-color="#ffffff" auto-play="3000"
class="rounded-lg overflow-hidden" height="160">
<nut-swiper-item v-for="(item, index) in bannerList" :key="index" @click="onBannerClick(item)">
<image :src="item.image" mode="aspectFill" class="w-full h-40 object-cover" />
</nut-swiper-item>
</nut-swiper>
</view>
<!-- 图片预览组件 -->
<nut-image-preview
v-model:show="showImagePreview"
:images="previewImages"
:init-no="currentImageIndex"
/>
<!-- 文章弹框组件 -->
<nut-popup
v-model:visible="showArticleModal"
position="bottom"
:style="{ height: '100vh' }"
close-icon-position="bottom-right"
:safe-area-inset-bottom="true"
>
<view class="article-modal">
<!-- <view class="article-header p-4 border-b">
<text class="text-lg font-bold">{{ currentArticle.title }}</text>
</view> -->
<scroll-view :scroll-y="true" :catch-move="true" class="article-content" style="height: calc(100vh - 140rpx);">
<view class="p-4">
<rich-text :nodes="currentArticle.content"></rich-text>
</view>
</scroll-view>
<view class="article-footer p-4">
<nut-button type="primary" block color="#fb923c" @click="closeArticleModal">
关闭
</nut-button>
</view>
</view>
</nut-popup>
</template>
<script setup>
import Taro from '@tarojs/taro'
import { ref } from 'vue'
// 定义props
defineProps({
bannerList: {
type: Array,
default: () => []
}
})
// 定义emits
const emit = defineEmits(['bannerClick'])
// 响应式数据
const showImagePreview = ref(false)
const previewImages = ref([])
const currentImageIndex = ref(0)
const showArticleModal = ref(false)
const currentArticle = ref({
title: '',
content: ''
})
/**
* 点击轮播图处理函数
*/
const onBannerClick = (item) => {
switch (item.type) {
case 'poster':
// 海报图 - 显示图片预览
previewImages.value = [item.image]
currentImageIndex.value = 0
showImagePreview.value = true
break
case 'article':
// 文章封面图 - 显示文章弹框
currentArticle.value = {
title: item.title,
content: item.content
}
showArticleModal.value = true
break
case 'vehicle':
// 车辆封面图 - 跳转到车辆详情页
Taro.navigateTo({
url: `/pages/productDetail/index?id=${item.id}`
})
break
default:
console.warn('未知的轮播图类型:', item.type)
}
// 触发父组件事件
emit('bannerClick', item)
}
/**
* 关闭文章弹框
*/
const closeArticleModal = () => {
showArticleModal.value = false
}
</script>
<style scoped>
.article-modal {
height: 100vh;
display: flex;
flex-direction: column;
}
.article-content {
overflow-y: auto;
}
.article-footer {
position: sticky;
bottom: 0;
left: 0;
right: 0;
background: white;
border-top: 1px solid #eee;
padding: 16px;
z-index: 1000;
margin-top: auto;
}
</style>
<!--
* @Date: 2025-06-28 10:33:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-07-18 17:11:10
* @LastEditTime: 2025-07-24 14:21:19
* @FilePath: /jgdl/src/pages/index/index.vue
* @Description: 捡个电驴首页
-->
......@@ -28,14 +28,7 @@
</nut-sticky>
<!-- Banner -->
<view class="px-4 pt-4" style="background: linear-gradient( 180deg, #fb923c 0%, rgba(255,203,53,0) 61%);">
<nut-swiper :init-page="0" :pagination-visible="true" pagination-color="#ffffff" auto-play="3000"
class="rounded-lg overflow-hidden" height="160">
<nut-swiper-item v-for="(image, index) in bannerImages" :key="index" @click="onBannerClick(image)">
<image :src="image.front_photo" mode="aspectFill" class="w-full h-40 object-cover" />
</nut-swiper-item>
</nut-swiper>
</view>
<BannerSwiper :banner-list="bannerList" />
<!-- Category Icons -->
<view class="px-4 mt-2">
......@@ -87,10 +80,10 @@ import TabBar from '@/components/TabBar.vue'
import SearchPopup from '@/components/SearchPopup.vue'
import FeaturedRecommendations from '@/components/FeaturedRecommendations.vue'
import LatestScooters from '@/components/LatestScooters.vue'
import BannerSwiper from '@/components/BannerSwiper.vue'
import "./index.less";
// 导入接口
import { getRecommendVehicleAPI } from '@/api/car';
import { DEFAULT_COVER_IMG } from '@/utils/config'
// 响应式数据
const searchValue = ref('')
const showSearchPopup = ref(false)
......@@ -100,8 +93,31 @@ const onSearchHandle = () => {
showSearchPopup.value = true
}
// Banner图片
const bannerImages = ref([])
// Banner数据
const bannerList = ref([])
// Mock轮播图数据
const mockBannerData = [
{
id: 1,
type: 'poster',
image: 'https://picsum.photos/400/160?random=1',
title: '海报图片'
},
{
id: 2,
type: 'article',
image: 'https://picsum.photos/400/160?random=2',
title: '电动车保养指南',
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>'
},
{
id: 3,
type: 'vehicle',
image: 'https://picsum.photos/400/160?random=3',
title: '精品二手车'
}
]
/**
* 点击认证车源
......@@ -179,17 +195,18 @@ useReady(async () => {
onMounted(async () => {
// 获取首页轮播
const res1 = await getRecommendVehicleAPI({ section: 1 })
if (res1.code) {
bannerImages.value = res1.data.list.map(item => ({
if (res1.code && res1.data.list.length) {
// 将API数据转换为车辆类型的轮播图
const vehicleBanners = res1.data.list.map(item => ({
id: item.id,
front_photo: item.front_photo
}));
if (!bannerImages.value.length) {
bannerImages.value = [{
id: 0,
front_photo: DEFAULT_COVER_IMG
}]
}
type: 'vehicle',
image: item.front_photo,
title: '精品二手车'
}))
bannerList.value = vehicleBanners
} else {
// 使用mock数据
bannerList.value = mockBannerData
}
})
......@@ -217,10 +234,5 @@ useShareAppMessage(() => {
return shareObj;
})
// 点击banner图片跳转到详情页
const onBannerClick = ({id}) => {
Taro.navigateTo({
url: `/pages/productDetail/index?id=${id}`
})
}
</script>
......
......@@ -119,7 +119,6 @@ const scrollTop = ref(0)
// 搜索值
const searchValue = ref('')
const onBlurSearch = async () => {
console.warn(searchValue.value)
// 重置分页状态
page.value = 1
hasMore.value = true
......