Files
YGChatCS/components/ImageSwiper/index.vue
2025-08-03 18:06:06 +08:00

171 lines
4.2 KiB
Vue

<template>
<view class="image-swiper">
<swiper
class="swiper-box"
:style="swiperStyle"
:autoplay="false"
:interval="3000"
:duration="1000"
:current="active"
@change="handleSwiperChange"
>
<swiper-item
class="swiper-item"
v-for="(item, index) in thumbnails"
:key="index"
>
<image :src="item.photoUrl" mode="aspectFill"></image>
</swiper-item>
</swiper>
<view class="custom-indicator">
图片{{ active + 1 }}/{{ thumbnails.length }}
</view>
<!-- 缩略图部分 -->
<view v-if="showThumbnails" class="thumbnail-box">
<scroll-view
class="thumbnail-scroll"
scroll-x="true"
:scroll-left="scrollLeft"
:scroll-with-animation="true"
show-scrollbar="false"
>
<view class="thumbnail-list">
<view
v-for="(thumb, index) in thumbnails"
:key="index"
:class="['thumbnail-item', { active: index === active }]"
:id="`thumbnail-${index}`"
@click="handleThumbnailClick(index)"
>
<image :src="thumb.photoUrl" mode="aspectFill"></image>
<text>{{ thumb.photoName }}</text>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup>
import { ref, computed, nextTick } from "vue";
// Props定义
const props = defineProps({
// 轮播图圆角大小,支持数字(px)或字符串
borderRadius: {
type: [Number, String],
default: 8,
},
// 轮播图数据
images: {
type: Array,
default: () => [],
},
// 轮播图高度,支持数字(px)或字符串
height: {
type: [Number, String],
default: 200,
},
// 是否显示缩略图
showThumbnails: {
type: Boolean,
default: true,
},
});
const active = ref(0);
const scrollLeft = ref(0);
// 计算圆角样式
const borderRadiusStyle = computed(() => {
const radius =
typeof props.borderRadius === "number"
? `${props.borderRadius}px`
: props.borderRadius;
return {
borderRadius: radius,
};
});
// 计算轮播图样式(包含高度和圆角)
const swiperStyle = computed(() => {
const radius =
typeof props.borderRadius === "number"
? `${props.borderRadius}px`
: props.borderRadius;
const swiperHeight =
typeof props.height === "number" ? `${props.height}px` : props.height;
return {
borderRadius: radius,
height: swiperHeight,
};
});
// 默认图片数据
const defaultImages = [
{
photoUrl:
"https://fastly.picsum.photos/id/866/654/400.jpg?hmac=z3vI4CYrpnXEgimSlJCDwXRxEa-UDHiRwzGEyB8V-po",
photoName: "瑶山古寨",
},
{
photoUrl:
"https://fastly.picsum.photos/id/284/654/400.jpg?hmac=89XRCJxYTblKIFGLOp6hJ9U0GC8BQrcnJwE5pG21NAk",
photoName: "民俗表演",
},
{
photoUrl:
"https://fastly.picsum.photos/id/281/654/400.jpg?hmac=hcAJB7y2Xz3DVuz6S4XeQZgzaTJ_QWnxtbnaagZL6Fs",
photoName: "特色美食",
},
{
photoUrl:
"https://fastly.picsum.photos/id/435/654/400.jpg?hmac=TSVDxfo-zXbunxNQK0erSG_nmKcS20xfhbQsCAXLlHo",
photoName: "传统服饰",
},
{
photoUrl:
"https://fastly.picsum.photos/id/737/654/400.jpg?hmac=VED05oEK3XB0Aa_DUVoZjTAf0bHjAmNYyJky4lq5vVo",
photoName: "其他",
},
];
// 使用传入的图片数据或默认数据
const thumbnails = computed(() => {
return props.images.length ? props.images : defaultImages;
});
const handleThumbnailClick = (index) => {
active.value = index;
scrollToActiveItem(index);
};
// 滚动到选中项
const scrollToActiveItem = async (index) => {
await nextTick();
// 计算每个缩略图项的宽度(包括间距)
const itemWidth = 58; // 48px宽度 + 10px间距
const containerWidth = 300; // 大概的容器宽度
const targetScrollLeft = Math.max(
0,
index * itemWidth - containerWidth / 2 + itemWidth / 2
);
scrollLeft.value = targetScrollLeft;
};
// 监听主轮播图变化,同步缩略图滚动
const handleSwiperChange = (e) => {
const currentIndex = e.detail.current;
active.value = currentIndex;
scrollToActiveItem(currentIndex);
};
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>