import { View, Swiper, SwiperItem, Text } from '@tarojs/components' import { useState, useEffect, useRef } from 'react' import { cn } from '@/utils/cn' import { Image } from './image' import Taro from '@tarojs/taro' export interface CarouselItem { /** 图片地址 */ src: string /** 图片标题 */ title?: string /** 图片描述 */ description?: string /** 点击跳转链接 */ link?: string /** 自定义样式 */ className?: string } export interface CarouselProps { /** 轮播图数据 */ items: CarouselItem[] /** 是否自动播放 */ autoplay?: boolean /** 自动播放间隔时间(毫秒) */ interval?: number /** 是否显示指示器 */ showIndicators?: boolean /** 指示器位置 */ indicatorPosition?: 'bottom' | 'top' | 'left' | 'right' /** 是否循环播放 */ circular?: boolean /** 图片填充模式 */ imageMode?: 'scaleToFill' | 'aspectFit' | 'aspectFill' | 'widthFix' | 'top' | 'bottom' | 'center' | 'left' | 'right' /** 图片高度 */ height?: number | string /** 圆角大小 */ rounded?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full' /** 点击轮播图回调 */ onItemClick?: (item: CarouselItem, index: number) => void /** 轮播切换回调 */ onChange?: (current: number) => void /** 自定义样式类 */ className?: string } const roundedMap = { none: '', sm: 'rounded-sm', md: 'rounded-md', lg: 'rounded-lg', xl: 'rounded-xl', full: 'rounded-full' } const indicatorPositionMap = { bottom: 'bottom-4 left-1/2 -translate-x-1/2', top: 'top-4 left-1/2 -translate-x-1/2', left: 'left-4 top-1/2 -translate-y-1/2 flex-col', right: 'right-4 top-1/2 -translate-y-1/2 flex-col' } export function Carousel({ items, autoplay = true, interval = 3000, showIndicators = true, indicatorPosition = 'bottom', circular = true, imageMode = 'aspectFill', height = 200, rounded = 'md', onItemClick, onChange, className }: CarouselProps) { const [currentIndex, setCurrentIndex] = useState(0) const swiperRef = useRef(null) const autoplayTimerRef = useRef(null) // 处理轮播切换 const handleChange = (e: any) => { const index = e.detail.current setCurrentIndex(index) onChange?.(index) } // 处理图片点击 const handleItemClick = (item: CarouselItem, index: number) => { onItemClick?.(item, index) } // 处理指示器点击 const handleIndicatorClick = (index: number) => { setCurrentIndex(index) swiperRef.current?.swiperTo?.(index) } // 自动播放 useEffect(() => { if (autoplay && items.length > 1) { autoplayTimerRef.current = setInterval(() => { const nextIndex = (currentIndex + 1) % items.length setCurrentIndex(nextIndex) swiperRef.current?.swiperTo?.(nextIndex) }, interval) } return () => { if (autoplayTimerRef.current) { clearInterval(autoplayTimerRef.current) } } }, [autoplay, currentIndex, interval, items.length]) // 页面隐藏时清除定时器 useEffect(() => { const handleHide = () => { if (autoplayTimerRef.current) { clearInterval(autoplayTimerRef.current) } } const handleShow = () => { if (autoplay && items.length > 1) { autoplayTimerRef.current = setInterval(() => { const nextIndex = (currentIndex + 1) % items.length setCurrentIndex(nextIndex) swiperRef.current?.swiperTo?.(nextIndex) }, interval) } } // Taro 事件监听 if (typeof Taro !== 'undefined') { Taro.eventCenter.on('onHide', handleHide) Taro.eventCenter.on('onShow', handleShow) } return () => { if (typeof Taro !== 'undefined') { Taro.eventCenter.off('onHide', handleHide) Taro.eventCenter.off('onShow', handleShow) } } }, [autoplay, currentIndex, interval, items.length]) if (!items || items.length === 0) { return ( 暂无图片 ) } return ( {items.map((item, index) => ( handleItemClick(item, index)} > {item.title && ( {item.title && ( {item.title} )} {item.description && ( {item.description} )} )} ))} {/* 指示器 */} {showIndicators && items.length > 1 && ( {items.map((_, index) => ( handleIndicatorClick(index)} /> ))} )} {/* 左右箭头 - 可选 */} {items.length > 1 && ( <> { const prevIndex = currentIndex === 0 ? items.length - 1 : currentIndex - 1 setCurrentIndex(prevIndex) swiperRef.current?.swiperTo?.(prevIndex) }} > { const nextIndex = (currentIndex + 1) % items.length setCurrentIndex(nextIndex) swiperRef.current?.swiperTo?.(nextIndex) }} > )} ) }