diff --git a/app/products/[...slug]/page.jsx b/app/products/[...slug]/page.jsx
index 0ad6e9f..b2f7d79 100644
--- a/app/products/[...slug]/page.jsx
+++ b/app/products/[...slug]/page.jsx
@@ -136,7 +136,7 @@ export default async function Page({ params, searchParams }) {
)}
{/* 产品副标题 */}
{data.subtitle && (
-
+
{data.subtitle}
)}
diff --git a/components/products/ProductImageGallery.jsx b/components/products/ProductImageGallery.jsx
index 3a77523..d99abc9 100644
--- a/components/products/ProductImageGallery.jsx
+++ b/components/products/ProductImageGallery.jsx
@@ -1,10 +1,20 @@
'use client';
import { useState, useEffect, useRef } from 'react';
+import { Swiper, SwiperSlide } from "swiper/react";
+import { Navigation, Pagination, Thumbs, FreeMode } from "swiper/modules";
+
+// 导入Swiper样式
+import "swiper/css";
+import "swiper/css/navigation";
+import "swiper/css/pagination";
+import "swiper/css/thumbs";
+import "swiper/css/free-mode";
const ProductImageGallery = ({ data }) => {
const [currentImageIndex, setCurrentImageIndex] = useState(0);
- const thumbnailContainerRef = useRef(null);
+ const [thumbsSwiper, setThumbsSwiper] = useState(null);
+ const thumbsSwiperRef = useRef(null);
// 如果没有attachments,不显示组件
if (!data?.attachments || data.attachments.length === 0) {
@@ -30,6 +40,41 @@ const ProductImageGallery = ({ data }) => {
setCurrentImageIndex(mainImageIndex);
}, [mainImageIndex]);
+ // 键盘导航支持
+ useEffect(() => {
+ const handleKeyDown = (event) => {
+ if (data.attachments.length <= 1) return;
+
+ switch (event.key) {
+ case 'ArrowLeft':
+ event.preventDefault();
+ changeMainImage('prev');
+ break;
+ case 'ArrowRight':
+ event.preventDefault();
+ changeMainImage('next');
+ break;
+ case 'Home':
+ event.preventDefault();
+ setCurrentImageIndex(0);
+ break;
+ case 'End':
+ event.preventDefault();
+ setCurrentImageIndex(data.attachments.length - 1);
+ break;
+ default:
+ break;
+ }
+ };
+
+ // 添加键盘事件监听器
+ document.addEventListener('keydown', handleKeyDown);
+
+ return () => {
+ document.removeEventListener('keydown', handleKeyDown);
+ };
+ }, [data.attachments.length]);
+
const changeMainImage = (direction) => {
if (data.attachments.length <= 1) return;
@@ -42,149 +87,201 @@ const ProductImageGallery = ({ data }) => {
const changeMainImageByIndex = (index) => {
setCurrentImageIndex(index);
+
+ // 智能滚动缩略图到可见区域
+ if (thumbsSwiperRef.current && thumbsSwiperRef.current.swiper) {
+ const swiper = thumbsSwiperRef.current.swiper;
+ const slideIndex = index;
+
+ // 计算目标滚动位置
+ const slideWidth = 64; // 64px 缩略图宽度
+ const spaceBetween = 16; // 16px 间距
+ const totalSlideWidth = slideWidth + spaceBetween;
+
+ // 获取当前可见的slides数量
+ const visibleSlides = swiper.params.slidesPerView;
+
+ // 计算当前滚动位置
+ const currentTranslate = swiper.translate;
+ const currentSlideIndex = Math.round(Math.abs(currentTranslate) / totalSlideWidth);
+
+ // 判断需要向左还是向右滚动
+ let targetSlideIndex;
+
+ if (slideIndex < currentSlideIndex) {
+ // 向左滚动:让选中的缩略图显示在左侧
+ targetSlideIndex = Math.max(0, slideIndex - 1);
+ } else if (slideIndex >= currentSlideIndex + visibleSlides) {
+ // 向右滚动:让选中的缩略图显示在右侧
+ targetSlideIndex = slideIndex - visibleSlides + 1;
+ } else {
+ // 当前缩略图已经在可见区域内,不需要滚动
+ return;
+ }
+
+ // 计算目标滚动位置
+ const targetTranslate = targetSlideIndex * totalSlideWidth;
+
+ // 确保不超出边界
+ const maxTranslate = swiper.maxTranslate();
+ const finalTranslate = Math.max(0, Math.min(targetTranslate, maxTranslate));
+
+ // 平滑滚动到目标位置
+ swiper.slideTo(Math.round(finalTranslate / totalSlideWidth), 300);
+ }
};
- // 智能滚动到缩略图 - 业内最佳实践
- const scrollToThumbnail = (index) => {
- if (!thumbnailContainerRef.current) return;
-
- const container = thumbnailContainerRef.current;
- const containerWidth = container.clientWidth;
- const thumbnailWidth = 64; // 64px 缩略图宽度
- const gap = 16; // 16px 间距
- const totalThumbnailWidth = thumbnailWidth + gap;
-
- // 计算一次能显示多少个缩略图(类似Swiper的slidesPerView)
- const visibleCount = Math.floor(containerWidth / totalThumbnailWidth);
-
- // 计算目标滚动位置
- let scrollLeft;
-
- if (index < visibleCount / 2) {
- // 如果是前几个,滚动到开头
- scrollLeft = 0;
- } else if (index >= data.attachments.length - visibleCount / 2) {
- // 如果是后几个,滚动到末尾
- scrollLeft = container.scrollWidth - containerWidth;
- } else {
- // 居中显示
- scrollLeft = (index - Math.floor(visibleCount / 2)) * totalThumbnailWidth;
- }
-
- // 确保滚动位置在有效范围内
- scrollLeft = Math.max(0, Math.min(scrollLeft, container.scrollWidth - containerWidth));
-
- container.scrollTo({
- left: scrollLeft,
- behavior: 'smooth'
- });
- };
+ // 缩略图导航控制 - 移除这些函数,不再需要
+ // const goToPrevThumbs = () => {
+ // if (thumbsSwiperRef.current && thumbsSwiperRef.current.swiper) {
+ // thumbsSwiperRef.current.swiper.slidePrev();
+ // }
+ // };
+
+ // const goToNextThumbs = () => {
+ // if (thumbsSwiperRef.current && thumbsSwiperRef.current.swiper) {
+ // if (thumbsSwiperRef.current && thumbsSwiperRef.current.swiper) {
+ // thumbsSwiperRef.current.swiper.slideNext();
+ // }
+ // }
+ // };
return (
{/* 主图显示区域 */}
-
+

- {/* 导航箭头 - 简约大气设计 */}
+ {/* 图片计数器 */}
+ {data.attachments.length > 1 && (
+
+ {currentImageIndex + 1} / {data.attachments.length}
+
+ )}
+
+ {/* 导航箭头 - 悬浮时显示 */}
{data.attachments.length > 1 && (
<>
>
)}
- {/* 缩略图列表 - 业内最佳实践,确保完整显示 */}
-
-
- {data.attachments.map((attachment, index) => (
-
{
- changeMainImageByIndex(index);
- scrollToThumbnail(index);
- }}
- >
-

{
- console.error('Image load error:', attachment.file_url);
- e.target.style.display = 'none';
- }}
- />
-
- ))}
+ {/* 缩略图列表 - 使用Swiper实现最佳滚动效果 */}
+ {data.attachments.length > 1 && (
+
+
+ {data.attachments.map((attachment, index) => (
+ changeMainImageByIndex(index)}
+ >
+ {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault();
+ changeMainImageByIndex(index);
+ }
+ }}
+ >
+

{
+ console.error('Image load error:', attachment.file_url);
+ e.target.style.display = 'none';
+ }}
+ />
+ {/* 缩略图加载状态指示 */}
+
+
+
+ ))}
+
-
+ )}
{/* 内联样式 */}