|
@@ -0,0 +1,317 @@
|
|
|
|
|
+import React, { useState, useRef, useEffect } from "react";
|
|
|
|
|
+import { Swiper, SwiperSlide } from "swiper/react";
|
|
|
|
|
+import { Scrollbar, Navigation } from "swiper/modules";
|
|
|
|
|
+import "swiper/css";
|
|
|
|
|
+import "swiper/css/scrollbar";
|
|
|
|
|
+import "swiper/css/navigation";
|
|
|
|
|
+import Image from "next/image";
|
|
|
|
|
+import { OpenAddToCartModalButton } from "@/components/common/AddToCartModal/OpenAddToCartModalButton";
|
|
|
|
|
+
|
|
|
|
|
+// 商品数据,每个卡片携带videoUrl视频地址
|
|
|
|
|
+type ProductItem = {
|
|
|
|
|
+ id: number;
|
|
|
|
|
+ img: string;
|
|
|
|
|
+ title: string;
|
|
|
|
|
+ price: number;
|
|
|
|
|
+ videoUrl: string;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+const productList: ProductItem[] = [
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ img: "/img/hair-video-1.jpg",
|
|
|
|
|
+ title: "14-36 In Water Wave Human Hair HD Inv...",
|
|
|
|
|
+ price: 146.1,
|
|
|
|
|
+ videoUrl: "/video/hair-demo-1.mp4",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 2,
|
|
|
|
|
+ img: "/img/hair-video-2.jpg",
|
|
|
|
|
+ title: "14-36 In Water Wave Human Hair HD Inv...",
|
|
|
|
|
+ price: 146.1,
|
|
|
|
|
+ videoUrl: "/video/hair-demo-2.mp4",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 3,
|
|
|
|
|
+ img: "/img/hair-video-3.jpg",
|
|
|
|
|
+ title: "14-36 In Water Wave Human Hair HD Inv...",
|
|
|
|
|
+ price: 146.1,
|
|
|
|
|
+ videoUrl: "/video/hair-demo-3.mp4",
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 4,
|
|
|
|
|
+ img: "/img/hair-video-4.jpg",
|
|
|
|
|
+ title: "14-36 In Water Wave Human Hair HD Inv...",
|
|
|
|
|
+ price: 146.1,
|
|
|
|
|
+ videoUrl: "/video/hair-demo-4.mp4",
|
|
|
|
|
+ },
|
|
|
|
|
+];
|
|
|
|
|
+
|
|
|
|
|
+const Trending = () => {
|
|
|
|
|
+ // 视频弹窗状态
|
|
|
|
|
+ const [openVideoModal, setOpenVideoModal] = useState(false);
|
|
|
|
|
+ const [currentVideoSrc, setCurrentVideoSrc] = useState("");
|
|
|
|
|
+ const videoRef = useRef<HTMLVideoElement>(null);
|
|
|
|
|
+
|
|
|
|
|
+ // 打开弹窗并赋值视频地址
|
|
|
|
|
+ const handlePlayClick = (videoUrl: string) => {
|
|
|
|
|
+ setCurrentVideoSrc(videoUrl);
|
|
|
|
|
+ setOpenVideoModal(true);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 关闭弹窗,暂停视频
|
|
|
|
|
+ const closeModal = () => {
|
|
|
|
|
+ setOpenVideoModal(false);
|
|
|
|
|
+ if (videoRef.current) {
|
|
|
|
|
+ videoRef.current.pause();
|
|
|
|
|
+ videoRef.current.currentTime = 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 弹窗打开自动播放
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (openVideoModal && videoRef.current) {
|
|
|
|
|
+ videoRef.current.play().catch((err) => console.log("播放拦截", err));
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [openVideoModal]);
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className="w-full max-w-[960px] mx-auto">
|
|
|
|
|
+ <div className="w-full max-w-[960px] mx-auto px-4 py-6">
|
|
|
|
|
+ {/* 顶部标题 */}
|
|
|
|
|
+ <h2 className="text-[clamp(1.5rem,4vw,2.2rem)] font-bold mb-6 text-black">
|
|
|
|
|
+ Real looks,Trending Now
|
|
|
|
|
+ </h2>
|
|
|
|
|
+
|
|
|
|
|
+ {/* Swiper外层容器:进度条深层Tailwind样式 */}
|
|
|
|
|
+ <div
|
|
|
|
|
+ className="
|
|
|
|
|
+ [&_.custom-scrollbar]:h-[3px]
|
|
|
|
|
+ [&_.custom-scrollbar]:bg-gray-200
|
|
|
|
|
+ [&_.custom-scrollbar]:rounded-full
|
|
|
|
|
+ [&_.swiper-scrollbar-drag]:h-full
|
|
|
|
|
+ [&_.swiper-scrollbar-drag]:bg-black
|
|
|
|
|
+ [&_.swiper-scrollbar-drag]:rounded-full
|
|
|
|
|
+ [&_.swiper-scrollbar-drag]:shadow-none
|
|
|
|
|
+ [&_.swiper-scrollbar]:bg-transparent
|
|
|
|
|
+ /* 自定义左右箭头样式 匹配截图灰色圆形按钮 */
|
|
|
|
|
+ [&_.swiper-button-next]:w-11
|
|
|
|
|
+ [&_.swiper-button-next]:h-11
|
|
|
|
|
+ [&_.swiper-button-next]:rounded-full
|
|
|
|
|
+ [&_.swiper-button-next]:bg-black/40
|
|
|
|
|
+ [&_.swiper-button-next]:text-white
|
|
|
|
|
+ [&_.swiper-button-next]:after:text-base
|
|
|
|
|
+ [&_.swiper-button-next]:after:font-bold
|
|
|
|
|
+ [&_.swiper-button-prev]:w-11
|
|
|
|
|
+ [&_.swiper-button-prev]:h-11
|
|
|
|
|
+ [&_.swiper-button-prev]:rounded-full
|
|
|
|
|
+ [&_.swiper-button-prev]:bg-black/40
|
|
|
|
|
+ [&_.swiper-button-prev]:text-white
|
|
|
|
|
+ [&_.swiper-button-prev]:after:text-base
|
|
|
|
|
+ [&_.swiper-button-prev]:after:font-bold
|
|
|
|
|
+ [&_.swiper-button-next]:right-3
|
|
|
|
|
+ [&_.swiper-button-prev]:left-3
|
|
|
|
|
+ "
|
|
|
|
|
+ >
|
|
|
|
|
+ <Swiper
|
|
|
|
|
+ modules={[Scrollbar, Navigation]}
|
|
|
|
|
+ spaceBetween={16}
|
|
|
|
|
+ slidesPerView={1.2} // 露出右侧下一张,和截图一致
|
|
|
|
|
+ navigation={{
|
|
|
|
|
+ prevEl: ".btn-prev", // 左箭头class
|
|
|
|
|
+ nextEl: ".btn-next", // 右箭头class
|
|
|
|
|
+ disabledClass: "opacity-20", // 滑到首尾自动加这个类,Tailwind直接用
|
|
|
|
|
+ }}
|
|
|
|
|
+ breakpoints={{
|
|
|
|
|
+ 640: { slidesPerView: 2.2 },
|
|
|
|
|
+ 1024: { slidesPerView: 2.5 },
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ {productList.map((item) => (
|
|
|
|
|
+ <SwiperSlide key={item.id} className="!h-auto">
|
|
|
|
|
+ {/* 单个商品卡片 */}
|
|
|
|
|
+ <div className=" overflow-hidden flex flex-col">
|
|
|
|
|
+ {/* 图片区域 + 中间播放按钮 */}
|
|
|
|
|
+ <div className="relative rounded-xl w-full aspect-[4/5] overflow-hidden rounded-xl">
|
|
|
|
|
+ <img
|
|
|
|
|
+ src={item.img}
|
|
|
|
|
+ alt={item.title}
|
|
|
|
|
+ className="w-full h-full object-cover"
|
|
|
|
|
+ />
|
|
|
|
|
+ {/* 居中圆形播放按钮 */}
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={() => handlePlayClick(item.videoUrl)}
|
|
|
|
|
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
|
|
|
|
|
+ w-24 h-24 rounded-full bg-white/60 backdrop-blur-sm
|
|
|
|
|
+ flex items-center justify-center transition hover:bg-white/80"
|
|
|
|
|
+ >
|
|
|
|
|
+ <svg
|
|
|
|
|
+ width="36"
|
|
|
|
|
+ height="36"
|
|
|
|
|
+ viewBox="0 0 24 24"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ stroke="#222"
|
|
|
|
|
+ strokeWidth="2"
|
|
|
|
|
+ >
|
|
|
|
|
+ <polygon points="5 3 19 12 5 21 5 3" />
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 底部文字价格区域 */}
|
|
|
|
|
+ <div className="p-3 flex flex-col rounded-xl mt-3 bg-[#f0f8f5] gap-3">
|
|
|
|
|
+ <p className="text-base text-gray-900 truncate">
|
|
|
|
|
+ {item.title}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ {/* 价格 + 购物车图标 */}
|
|
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
|
|
+ <div className="flex items-center gap-1">
|
|
|
|
|
+ <span className="font-semibold">${item.price}</span>
|
|
|
|
|
+ <span className="text-xs text-gray-500 line-through">
|
|
|
|
|
+ ${item.price}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {/* 购物车图标 */}
|
|
|
|
|
+ <OpenAddToCartModalButton
|
|
|
|
|
+ productUrlKey={
|
|
|
|
|
+ "http://nshop.test/hd-transparent-deep-wave-&-curly-lace-frontal-closure-wig"
|
|
|
|
|
+ }
|
|
|
|
|
+ icon={
|
|
|
|
|
+ <svg
|
|
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
+ width="32"
|
|
|
|
|
+ height="32"
|
|
|
|
|
+ viewBox="0 0 32 32"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ >
|
|
|
|
|
+ <rect
|
|
|
|
|
+ x="0"
|
|
|
|
|
+ y="0"
|
|
|
|
|
+ width="32"
|
|
|
|
|
+ height="32"
|
|
|
|
|
+ rx="8"
|
|
|
|
|
+ fill="#036141"
|
|
|
|
|
+ ></rect>
|
|
|
|
|
+ <path
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ stroke-width="1.3333333333333333"
|
|
|
|
|
+ stroke-linejoin="round"
|
|
|
|
|
+ stroke-linecap="round"
|
|
|
|
|
+ d="M6.40039 7.79993L8.03372 7.79993L8.73372 10.5999M8.73372 10.5999L11.0671 19.9333L23.2004 19.9333L25.5337 10.5999L8.73372 10.5999Z"
|
|
|
|
|
+ ></path>
|
|
|
|
|
+ <ellipse
|
|
|
|
|
+ cx="11.066040515899658"
|
|
|
|
|
+ cy="23.199928283691406"
|
|
|
|
|
+ rx="1.4000248908996582"
|
|
|
|
|
+ ry="1.4000015258789062"
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ stroke-width="1.3333333333333333"
|
|
|
|
|
+ stroke-linejoin="round"
|
|
|
|
|
+ stroke-linecap="round"
|
|
|
|
|
+ ></ellipse>
|
|
|
|
|
+ <ellipse
|
|
|
|
|
+ cx="23.1998291015625"
|
|
|
|
|
+ cy="23.199928283691406"
|
|
|
|
|
+ rx="1.4000244140625"
|
|
|
|
|
+ ry="1.4000015258789062"
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ stroke-width="1.3333333333333333"
|
|
|
|
|
+ stroke-linejoin="round"
|
|
|
|
|
+ stroke-linecap="round"
|
|
|
|
|
+ ></ellipse>
|
|
|
|
|
+ <path
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ stroke-width="1.3333333333333333"
|
|
|
|
|
+ stroke-linejoin="round"
|
|
|
|
|
+ stroke-linecap="round"
|
|
|
|
|
+ d="M15.2656 15.2665L18.999 15.2665"
|
|
|
|
|
+ ></path>
|
|
|
|
|
+ <path
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ stroke-width="1.3333333333333333"
|
|
|
|
|
+ stroke-linejoin="round"
|
|
|
|
|
+ stroke-linecap="round"
|
|
|
|
|
+ d="M17.1348 17.1332L17.1348 13.3999"
|
|
|
|
|
+ ></path>
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ }
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </SwiperSlide>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ {/* 自定义左右按钮,放在Swiper内部,同级slide */}
|
|
|
|
|
+ <button className="btn-prev absolute left-2 top-1/2 -translate-y-1/2 z-10 w-9 h-9 rounded-full bg-black/50 shadow flex items-center justify-center">
|
|
|
|
|
+ {/* 自定义左箭头SVG */}
|
|
|
|
|
+ <svg
|
|
|
|
|
+ width="16"
|
|
|
|
|
+ height="16"
|
|
|
|
|
+ viewBox="0 0 24 24"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ stroke="#fff"
|
|
|
|
|
+ stroke-width="2"
|
|
|
|
|
+ >
|
|
|
|
|
+ <path d="M15 18l-6-6 6-6" />
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button className="btn-next absolute right-2 top-1/2 -translate-y-1/2 z-10 w-9 h-9 rounded-full bg-black/50 shadow flex items-center justify-center">
|
|
|
|
|
+ {/* 自定义右箭头SVG */}
|
|
|
|
|
+ <svg
|
|
|
|
|
+ width="16"
|
|
|
|
|
+ height="16"
|
|
|
|
|
+ viewBox="0 0 24 24"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ stroke="#fff"
|
|
|
|
|
+ stroke-width="2"
|
|
|
|
|
+ >
|
|
|
|
|
+ <path d="M9 18l6-6-6-6" />
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </Swiper>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ {/* ====================== 全屏视频遮罩弹窗 ====================== */}
|
|
|
|
|
+ {openVideoModal && (
|
|
|
|
|
+ <div
|
|
|
|
|
+ className="fixed inset-0 z-[9999] bg-black/95 flex items-center justify-center p-4"
|
|
|
|
|
+ onClick={closeModal} // 点击遮罩空白关闭
|
|
|
|
|
+ >
|
|
|
|
|
+ {/* 弹窗内层,阻止冒泡 */}
|
|
|
|
|
+ <div
|
|
|
|
|
+ className="relative w-full max-w-4xl"
|
|
|
|
|
+ onClick={(e) => e.stopPropagation()}
|
|
|
|
|
+ >
|
|
|
|
|
+ {/* 右上角关闭按钮 */}
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={closeModal}
|
|
|
|
|
+ className="absolute -top-12 right-0 text-white text-3xl hover:text-gray-300"
|
|
|
|
|
+ >
|
|
|
|
|
+ ✕
|
|
|
|
|
+ </button>
|
|
|
|
|
+ {/* 视频播放器 */}
|
|
|
|
|
+ <video
|
|
|
|
|
+ ref={videoRef}
|
|
|
|
|
+ src={currentVideoSrc}
|
|
|
|
|
+ controls
|
|
|
|
|
+ className="w-full rounded-lg"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="relative w-full aspect-[3/4] mt-8">
|
|
|
|
|
+ <Image
|
|
|
|
|
+ src={
|
|
|
|
|
+ "https://cdn.asteriahair.com/uploads/202607/01/20260701165110_3fff435adf276b89.jpg"
|
|
|
|
|
+ }
|
|
|
|
|
+ alt={"cscscs"}
|
|
|
|
|
+ fill
|
|
|
|
|
+ className="object-cover rounded-md"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ );
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+export default Trending;
|