|
@@ -1,14 +1,16 @@
|
|
|
import { useState, useRef, useEffect } from "react";
|
|
import { useState, useRef, useEffect } from "react";
|
|
|
import { Swiper, SwiperSlide } from "swiper/react";
|
|
import { Swiper, SwiperSlide } from "swiper/react";
|
|
|
-import { Scrollbar, Navigation } from "swiper/modules";
|
|
|
|
|
|
|
+import type { Swiper as SwiperType } from "swiper/types";
|
|
|
|
|
+import { Navigation } from "swiper/modules";
|
|
|
import "swiper/css";
|
|
import "swiper/css";
|
|
|
-import "swiper/css/scrollbar";
|
|
|
|
|
import "swiper/css/navigation";
|
|
import "swiper/css/navigation";
|
|
|
|
|
+import "swiper/css";
|
|
|
import Image from "next/image";
|
|
import Image from "next/image";
|
|
|
|
|
+import Link from "next/link";
|
|
|
import { OpenAddToCartModalButton } from "@/components/common/AddToCartModal/OpenAddToCartModalButton";
|
|
import { OpenAddToCartModalButton } from "@/components/common/AddToCartModal/OpenAddToCartModalButton";
|
|
|
-import type {HomeProduct} from "@/types/home/type"
|
|
|
|
|
-interface trendingProps {
|
|
|
|
|
- trending: HomeProduct[];
|
|
|
|
|
|
|
+import type { HomeProduct,HomeSection } from "@/types/home/type";
|
|
|
|
|
+interface newArrivalSectionProps{
|
|
|
|
|
+ trendingSection:HomeSection |undefined;
|
|
|
}
|
|
}
|
|
|
// // 商品数据,每个卡片携带videoUrl视频地址
|
|
// // 商品数据,每个卡片携带videoUrl视频地址
|
|
|
// type ProductItem = {
|
|
// type ProductItem = {
|
|
@@ -49,23 +51,24 @@ interface trendingProps {
|
|
|
// videoUrl: "/video/hair-demo-4.mp4",
|
|
// videoUrl: "/video/hair-demo-4.mp4",
|
|
|
// },
|
|
// },
|
|
|
// ];
|
|
// ];
|
|
|
-
|
|
|
|
|
-const Trending = ({ trending }: trendingProps) => {
|
|
|
|
|
- // 视频弹窗状态
|
|
|
|
|
|
|
+const SLIDES_PER_VIEW = 6.5;
|
|
|
|
|
+const SLIDES_PER_GROUP = 6;
|
|
|
|
|
+const SPACE_BETWEEN = 24;
|
|
|
|
|
+const Trending = ({ trendingSection }: newArrivalSectionProps) => {
|
|
|
|
|
+ const trending: HomeProduct[] = trendingSection?.products ?? [];
|
|
|
|
|
+ // 视频弹窗状态
|
|
|
const [openVideoModal, setOpenVideoModal] = useState(false);
|
|
const [openVideoModal, setOpenVideoModal] = useState(false);
|
|
|
const [currentVideoSrc, setCurrentVideoSrc] = useState("");
|
|
const [currentVideoSrc, setCurrentVideoSrc] = useState("");
|
|
|
- const videoRef = useRef<HTMLVideoElement>(null);
|
|
|
|
|
- // 弹窗打开自动播放
|
|
|
|
|
|
|
+ const videoRef = useRef<HTMLVideoElement>(null);
|
|
|
|
|
+ // 弹窗打开自动播放
|
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
|
if (openVideoModal && videoRef.current) {
|
|
if (openVideoModal && videoRef.current) {
|
|
|
videoRef.current.play().catch((err) => console.log("播放拦截", err));
|
|
videoRef.current.play().catch((err) => console.log("播放拦截", err));
|
|
|
}
|
|
}
|
|
|
}, [openVideoModal]);
|
|
}, [openVideoModal]);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (!trending || trending.length === 0) return null;
|
|
if (!trending || trending.length === 0) return null;
|
|
|
- const productList = trending
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const productList = trending;
|
|
|
|
|
|
|
|
// 打开弹窗并赋值视频地址
|
|
// 打开弹窗并赋值视频地址
|
|
|
const handlePlayClick = (videoUrl: string) => {
|
|
const handlePlayClick = (videoUrl: string) => {
|
|
@@ -81,204 +84,172 @@ const Trending = ({ trending }: trendingProps) => {
|
|
|
videoRef.current.currentTime = 0;
|
|
videoRef.current.currentTime = 0;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
+ const swiperRef = useRef<SwiperType | null>(null);
|
|
|
|
|
+ const [activePage, setActivePage] = useState(0);
|
|
|
|
|
+ const [isBeginning, setIsBeginning] = useState(true);
|
|
|
|
|
+ const [isEnd, setIsEnd] = useState(trending.length <= SLIDES_PER_VIEW);
|
|
|
|
|
+ const pageCount = Math.ceil(trending.length / SLIDES_PER_VIEW);
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
+ const syncSwiperState = (swiper: SwiperType) => {
|
|
|
|
|
+ const pageIndex = Math.min(swiper.snapIndex, Math.max(pageCount - 1, 0));
|
|
|
|
|
|
|
|
- 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>
|
|
|
|
|
|
|
+ setActivePage(pageIndex);
|
|
|
|
|
+ setIsBeginning(swiper.isBeginning);
|
|
|
|
|
+ setIsEnd(swiper.isEnd);
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
- {/* 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
|
|
|
|
|
- "
|
|
|
|
|
|
|
+ const handlePrev = () => {
|
|
|
|
|
+ swiperRef.current?.slidePrev();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleNext = () => {
|
|
|
|
|
+ swiperRef.current?.slideNext();
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handlePaginationClick = (pageIndex: number) => {
|
|
|
|
|
+ const swiper = swiperRef.current;
|
|
|
|
|
+
|
|
|
|
|
+ if (!swiper) return;
|
|
|
|
|
+
|
|
|
|
|
+ swiper.slideTo(pageIndex * SLIDES_PER_GROUP);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div className=" relative w-full mx-auto">
|
|
|
|
|
+ <h2 className="text-4xl text-center font-bold leading-ly-46 mt-12 mb-8">{trendingSection?.title}</h2>
|
|
|
|
|
+ <div className="pl-12">
|
|
|
|
|
+ <Swiper
|
|
|
|
|
+ slidesPerView={SLIDES_PER_VIEW}
|
|
|
|
|
+ slidesPerGroup={SLIDES_PER_GROUP}
|
|
|
|
|
+ spaceBetween={SPACE_BETWEEN}
|
|
|
|
|
+ speed={300}
|
|
|
|
|
+ watchOverflow
|
|
|
|
|
+ onSwiper={(swiper) => {
|
|
|
|
|
+ swiperRef.current = swiper;
|
|
|
|
|
+ syncSwiperState(swiper);
|
|
|
|
|
+ }}
|
|
|
|
|
+ onSlideChange={syncSwiperState}
|
|
|
|
|
+ onReachBeginning={syncSwiperState}
|
|
|
|
|
+ onReachEnd={syncSwiperState}
|
|
|
|
|
+ onFromEdge={syncSwiperState}
|
|
|
|
|
+ className="w-full"
|
|
|
>
|
|
>
|
|
|
- <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.image_url}
|
|
|
|
|
- alt={item.name}
|
|
|
|
|
- className="w-full h-full object-cover"
|
|
|
|
|
- />
|
|
|
|
|
- {/* 居中圆形播放按钮 */}
|
|
|
|
|
- <button
|
|
|
|
|
- onClick={() => handlePlayClick(item.videoUrl||"/video/hair-demo-1.mp4" )}
|
|
|
|
|
- className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2
|
|
|
|
|
|
|
+ {trending.map((item, index) => (
|
|
|
|
|
+ <SwiperSlide key={`${item.id}-${index}`}>
|
|
|
|
|
+ <div className=" overflow-hidden flex flex-col">
|
|
|
|
|
+ {/* 图片区域 + 中间播放按钮 */}
|
|
|
|
|
+ <div className="relative rounded-xl w-full aspect-[4/5] overflow-hidden rounded-xl">
|
|
|
|
|
+ <img
|
|
|
|
|
+ src={item.image_url}
|
|
|
|
|
+ alt={item.name}
|
|
|
|
|
+ className="w-full h-full object-cover"
|
|
|
|
|
+ />
|
|
|
|
|
+ {/* 居中圆形播放按钮 */}
|
|
|
|
|
+ <button
|
|
|
|
|
+ onClick={() =>
|
|
|
|
|
+ handlePlayClick(item.videoUrl || "/video/hair-demo-1.mp4")
|
|
|
|
|
+ }
|
|
|
|
|
+ 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
|
|
w-24 h-24 rounded-full bg-white/60 backdrop-blur-sm
|
|
|
flex items-center justify-center transition hover:bg-white/80"
|
|
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"
|
|
|
>
|
|
>
|
|
|
- <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>
|
|
|
|
|
|
|
+ <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.name}
|
|
|
|
|
- </p>
|
|
|
|
|
- {/* 价格 + 购物车图标 */}
|
|
|
|
|
- <div className="flex items-center justify-between">
|
|
|
|
|
- <div className="flex items-center gap-1">
|
|
|
|
|
- <span className="font-semibold">${item.min_price}</span>
|
|
|
|
|
- <span className="text-xs text-gray-500 line-through">
|
|
|
|
|
- ${item.price}
|
|
|
|
|
- </span>
|
|
|
|
|
- </div>
|
|
|
|
|
- {/* 购物车图标 */}
|
|
|
|
|
- <OpenAddToCartModalButton
|
|
|
|
|
- productId={item.id}
|
|
|
|
|
- icon={
|
|
|
|
|
- <svg
|
|
|
|
|
- xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
|
|
+ {/* 底部文字价格区域 */}
|
|
|
|
|
+ <div className="p-3 flex flex-col rounded-xl mt-3 bg-[#f0f8f5] gap-3">
|
|
|
|
|
+ <p className="text-base text-gray-900 truncate">
|
|
|
|
|
+ {item.name}
|
|
|
|
|
+ </p>
|
|
|
|
|
+ {/* 价格 + 购物车图标 */}
|
|
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
|
|
+ <div className="flex items-center gap-1">
|
|
|
|
|
+ <span className="font-semibold">${item.min_price}</span>
|
|
|
|
|
+ <span className="text-xs text-gray-500 line-through">
|
|
|
|
|
+ ${item.price}
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {/* 购物车图标 */}
|
|
|
|
|
+ <OpenAddToCartModalButton
|
|
|
|
|
+ productId={item.id}
|
|
|
|
|
+ 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"
|
|
width="32"
|
|
|
height="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>
|
|
|
|
|
|
|
+ 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>
|
|
</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>
|
|
|
|
|
-
|
|
|
|
|
- {/* ====================== 全屏视频遮罩弹窗 ====================== */}
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </SwiperSlide>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </Swiper>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {/* ====================== 全屏视频遮罩弹窗 ====================== */}
|
|
|
{openVideoModal && (
|
|
{openVideoModal && (
|
|
|
<div
|
|
<div
|
|
|
className="fixed inset-0 z-[9999] bg-black/95 flex items-center justify-center p-4"
|
|
className="fixed inset-0 z-[9999] bg-black/95 flex items-center justify-center p-4"
|
|
@@ -305,9 +276,117 @@ const Trending = ({ trending }: trendingProps) => {
|
|
|
/>
|
|
/>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- )}
|
|
|
|
|
- </div>
|
|
|
|
|
- <div className="relative w-full aspect-[3/4] mt-8">
|
|
|
|
|
|
|
+ )}
|
|
|
|
|
+ {!isBeginning && (
|
|
|
|
|
+ <button
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ aria-label="Previous"
|
|
|
|
|
+ onClick={handlePrev}
|
|
|
|
|
+ className="
|
|
|
|
|
+ absolute
|
|
|
|
|
+ left-18
|
|
|
|
|
+ top-1/2
|
|
|
|
|
+ z-10
|
|
|
|
|
+ flex
|
|
|
|
|
+ h-10
|
|
|
|
|
+ w-10
|
|
|
|
|
+ -translate-y-1/2
|
|
|
|
|
+ items-center
|
|
|
|
|
+ justify-center
|
|
|
|
|
+ "
|
|
|
|
|
+ >
|
|
|
|
|
+ <svg
|
|
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
+ width="40"
|
|
|
|
|
+ height="40"
|
|
|
|
|
+ viewBox="0 0 40 40"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ className="rotate-180"
|
|
|
|
|
+ >
|
|
|
|
|
+ <circle cx="20" cy="20" r="20" fill="#000000" fillOpacity="0.5" />
|
|
|
|
|
+
|
|
|
|
|
+ <path
|
|
|
|
|
+ d="M17 26L23 20L17 14"
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ strokeWidth="1.5"
|
|
|
|
|
+ strokeLinejoin="round"
|
|
|
|
|
+ strokeLinecap="square"
|
|
|
|
|
+ />
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ {/* Next - 最后一页不显示 */}
|
|
|
|
|
+ {!isEnd && (
|
|
|
|
|
+ <button
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ aria-label="Next"
|
|
|
|
|
+ onClick={handleNext}
|
|
|
|
|
+ className="
|
|
|
|
|
+ absolute
|
|
|
|
|
+ right-6
|
|
|
|
|
+ top-1/2
|
|
|
|
|
+ z-10
|
|
|
|
|
+ flex
|
|
|
|
|
+ h-10
|
|
|
|
|
+ w-10
|
|
|
|
|
+ -translate-y-1/2
|
|
|
|
|
+ items-center
|
|
|
|
|
+ justify-center
|
|
|
|
|
+ "
|
|
|
|
|
+ >
|
|
|
|
|
+ <svg
|
|
|
|
|
+ xmlns="http://www.w3.org/2000/svg"
|
|
|
|
|
+ width="40"
|
|
|
|
|
+ height="40"
|
|
|
|
|
+ viewBox="0 0 40 40"
|
|
|
|
|
+ fill="none"
|
|
|
|
|
+ >
|
|
|
|
|
+ <circle cx="20" cy="20" r="20" fill="#000000" fillOpacity="0.5" />
|
|
|
|
|
+
|
|
|
|
|
+ <path
|
|
|
|
|
+ d="M17 26L23 20L17 14"
|
|
|
|
|
+ stroke="rgba(255, 255, 255, 1)"
|
|
|
|
|
+ strokeWidth="1.5"
|
|
|
|
|
+ strokeLinejoin="round"
|
|
|
|
|
+ strokeLinecap="square"
|
|
|
|
|
+ />
|
|
|
|
|
+ </svg>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ )}
|
|
|
|
|
+
|
|
|
|
|
+ {/* Pagination */}
|
|
|
|
|
+ {pageCount > 1 && (
|
|
|
|
|
+ <div
|
|
|
|
|
+ className="
|
|
|
|
|
+ mt-4
|
|
|
|
|
+ flex
|
|
|
|
|
+ items-center
|
|
|
|
|
+ justify-center
|
|
|
|
|
+ gap-2
|
|
|
|
|
+ "
|
|
|
|
|
+ >
|
|
|
|
|
+ {Array.from({
|
|
|
|
|
+ length: pageCount,
|
|
|
|
|
+ }).map((_, index) => (
|
|
|
|
|
+ <button
|
|
|
|
|
+ key={index}
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ aria-label={`Go to page ${index + 1}`}
|
|
|
|
|
+ onClick={() => handlePaginationClick(index)}
|
|
|
|
|
+ className={`
|
|
|
|
|
+ h-2
|
|
|
|
|
+ w-10
|
|
|
|
|
+ rounded-full
|
|
|
|
|
+ transition-all
|
|
|
|
|
+ duration-300
|
|
|
|
|
+ ${activePage === index ? "bg-black" : "bg-[#DBDBDB]!"}
|
|
|
|
|
+ `}
|
|
|
|
|
+ />
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ <div className="relative w-full aspect-[4/3] mt-8">
|
|
|
<Image
|
|
<Image
|
|
|
src={
|
|
src={
|
|
|
"https://cdn.asteriahair.com/uploads/202607/01/20260701165110_3fff435adf276b89.jpg"
|
|
"https://cdn.asteriahair.com/uploads/202607/01/20260701165110_3fff435adf276b89.jpg"
|