Explorar el Código

产品详情页调整

zhangzf hace 2 días
padre
commit
b32f2abb50

+ 2 - 2
src/app/(public)/product/_components/ProductReviewSection.tsx

@@ -2,7 +2,7 @@
 import ReviewModal from "@/app/(public)/product/_components/review/ReviewModal";
 import { NoReview } from "@/app/(public)/product/_components/review/NoReview";
 // import { getProductReviews } from "@utils/hooks/getProductReviews";
-import type { ProductReviewList } from "@/types/products/review";
+import type { ProductReviewList,ProductReviewNode } from "@/types/products/review";
 import type {
   GalleryItem,
 } from "@/types/products/review";
@@ -27,7 +27,7 @@ interface ProductReviewSectionProps {
   onOpenGallery: (
     clickReviewIndex: number,
     clickImgIndex: number,
-    reviewList: ProductReviewList
+    reviewList: ProductReviewNode[]
   ) => void;
 }
 

+ 27 - 18
src/app/(public)/product/_components/SwitchButton.tsx

@@ -11,6 +11,7 @@ import { ReviewButton } from "@components/common/button/ReviewButton";
 import AddProductReviewForm from "../_components/review/AddProductReviewForm";
 import type {
   ProductReviewList,
+  ProductReviewNode,
   GalleryItem,
 } from "@/types/products/review";
 interface ReviewQuestionTabProps {
@@ -73,7 +74,7 @@ export default function SwitchButton({
       try {
         const clientUuid = getUuId();
         const res = await clientFetch(
-          `/api/shop/products/${productId}/reviews?page=1&per_page=5&has_images=0&sort=all&client_id=${clientUuid}`,
+          `/api/shop/products/${productId}/reviews?page=1&per_page=5&has_images=1&sort=photos&client_id=${clientUuid}`,
           { signal }, // 绑定中断信号
         );
       console.log("res------------------------------ccc:", res);
@@ -125,12 +126,12 @@ export default function SwitchButton({
     setActiveModalTab("all");
     setReviewModalOpen(true);
   }, []);
-  const hairImages = [
-    "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
-    "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
-    "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
-    "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
-  ];
+  // const hairImages = [
+  //   "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
+  //   "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
+  //   "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
+  //   "https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png",
+  // ];
   const closeModal = useCallback(() => {
     console.log("【根】调用关闭弹窗");
     setReviewModalOpen(false);
@@ -151,14 +152,14 @@ export default function SwitchButton({
     initialSlide: number;
   }>({ galleryList: [], initialSlide: 0 });
   // ✅ buildGalleryList 移到父组件
-  const buildGalleryList = useCallback((reviewList: ProductReviewList): GalleryItem[] => {
+  const buildGalleryList = useCallback((datareviewList: ProductReviewNode[]): GalleryItem[] => {
     const allGallery: GalleryItem[] = [];
-    reviewList.forEach((edge, reviewIdx) => {
-      edge.node.attachments.forEach((imgUrl) => {
+    datareviewList.forEach((edge, reviewIdx) => {
+      edge.attachments.forEach((imgUrl) => {
         allGallery.push({
           reviewIndex: reviewIdx,
           imageUrl: imgUrl,
-          review: edge.node,
+          review: edge,
         });
       });
     });
@@ -168,12 +169,12 @@ export default function SwitchButton({
 const openGalleryModal = useCallback((
   clickReviewIndex: number,
   clickImgIndex: number,
-  reviewList: ProductReviewList // 重点:传入当前点击所属的评论数组
+  reviewList: ProductReviewNode[] // 重点:传入当前点击所属的评论数组
 ) => {
   const galleryAll = buildGalleryList(reviewList);
   const targetEdge = reviewList[clickReviewIndex];
-  const targetReview = targetEdge.node;
-  const targetImg = targetReview.attachments[clickImgIndex];
+  // const targetReview = targetEdge;
+  const targetImg = targetEdge.attachments[clickImgIndex];
 
   const targetSlide = galleryAll.findIndex(
     (g) => g.reviewIndex === clickReviewIndex && g.imageUrl === targetImg
@@ -274,10 +275,18 @@ const openGalleryModal = useCallback((
             <ReviewButton setShowForm={setShowForm} />
             </div>
           <div className="w-full relative ">
-               <HairPhotoGallery
-            imageList={hairImages}
-            onOpenModal={openPhotoTabModal}
-          />
+              {reviewList.map((edge) => (
+                <div key={edge.id}>
+                  {/*评论文字、评分等*/}
+                  {edge.attachments.length > 0 && (
+                    <HairPhotoGallery
+                      review={edge}
+                      fullReviewList={reviewList}
+                      onOpenModal={openGalleryModal}
+                    />
+                  )}
+                </div>
+              ))}
              {/* <ReviewAdd productId={productId} /> */}
           </div>
            {open && (

+ 25 - 24
src/app/(public)/product/_components/review/HairPhotoGallery.tsx

@@ -1,53 +1,54 @@
 "use client";
 import Image from "next/image";
+import type {ProductReviewNode} from "@/types/products/review";
 
 interface HairPhotoGalleryProps {
-  onOpenModal: () => void;
-  imageList: string[];
+  review: ProductReviewNode;
+  fullReviewList: ProductReviewNode[];
+  onOpenModal: (
+    clickReviewIndex: number,
+    clickImgIndex: number,
+    fullReviewList: ProductReviewNode[]
+  ) => void;
 }
 
 export default function HairPhotoGallery({
+  review,
+  fullReviewList,
   onOpenModal,
-  imageList,
 }: HairPhotoGalleryProps) {
-  const displayImages = imageList.slice(0, 4);
+  const allImages = review.attachments;
+  const displayImages = allImages.slice(0, 4);
+
+  const handleImageClick = (imgIdx: number) => {
+    const reviewIndex = fullReviewList.findIndex((r) => r.id === review.id);
+    if (reviewIndex === -1) return;
+    onOpenModal(reviewIndex, imgIdx, fullReviewList);
+  };
+
   return (
     <div className="w-full flex gap-2 overflow-hidden my-4">
-      {displayImages.map((src, idx) => (
+      {displayImages.map((src, imgIdx) => (
         <div
-          key={idx}
-          onClick={onOpenModal}
+          key={'https://cdn.alipearlhair.com/media/reviewimages/cache/6/image/150x/040ec09b1e35df139433887a97daa66f/reviewimages/7O_A6W92AW3D_87N5PR8CO.png'||src}
+          onClick={() => handleImageClick(imgIdx)}
           className="relative flex-1 aspect-3/4 cursor-pointer rounded overflow-hidden"
         >
           <Image
             src={src}
-            alt={`hair ${idx}`}
+            alt={`hair ${imgIdx}`}
             width={144}
             height={192}
             className="w-full h-full object-cover"
           />
-          {idx == 3 ? (
+          {imgIdx === 3 && allImages.length > 4 ? (
             <div className="absolute inset-0 bg-black/25 flex flex-col items-center justify-center text-white">
               <p className="text-sm font-normal leading-ly-22 underline">More</p>
               <p className="text-sm font-normal leading-ly-22 underline">Photos</p>
             </div>
-          ) : (
-            ""
-          )}
+          ) : null}
         </div>
       ))}
-
-      {/* More Photos 第四张卡片 */}
-      {/* <div
-        onClick={onOpenModal}
-        className="relative flex-1 aspect-4/3 cursor-pointer rounded overflow-hidden"
-      >
-        <Image
-          src="/img/hair4.jpg"
-          alt="more photos"
-          className="object-cover"
-        />
-      </div> */}
     </div>
   );
 }

+ 9 - 7
src/app/(public)/product/_components/review/ReviewModal.tsx

@@ -3,7 +3,7 @@ import React, { useState, useRef, useEffect, useCallback } from "react";
 import type {
   ReviewTabType,
   ProductReviewList,
-  ProductReviewEdge,
+  ProductReviewNode,
   GalleryItem,
 } from "@/types/products/review";
 import ReviewImageGalleryModal from "./ReviewImageGalleryModal";
@@ -32,7 +32,7 @@ interface ReviewModalProps {
   onOpenGallery: (
     clickReviewIndex: number,
     clickImgIndex: number,
-    reviewList: ProductReviewList
+    reviewList: ProductReviewNode[]
   ) => void;
 }
 // 模拟后端请求函数,替换为你的真实接口
@@ -42,7 +42,7 @@ async function fetchReviewApi(
   pageNum: number,
   guestId: string | null,
   signal?: AbortSignal
-): Promise<{ list: ProductReviewList; total: number }> {
+): Promise<{ list: ProductReviewNode[]; total: number }> {
   
   let has_images = 0;
   if (tab == "photos") {
@@ -82,7 +82,7 @@ const ReviewModal: React.FC<ReviewModalProps> = React.memo(
     onOpenGallery
   }) {
     // 全部原始评论数据(接口返回完整数据缓存)
-    const [fullReviewList, setFullReviewList] = useState<ProductReviewList>([]);
+    const [fullReviewList, setFullReviewList] = useState<ProductReviewNode[]>([]);
     // 分页状态
     const [page, setPage] = useState(1);
     const [loading, setLoading] = useState(false);
@@ -120,6 +120,7 @@ const ReviewModal: React.FC<ReviewModalProps> = React.memo(
           setLoading(true);
           try {
             const uuid = getUuId();
+            // debugger
             const res = await fetchReviewApi(productId, tab, pageNum, uuid, signal);
             if (signal.aborted) return;
             // 直接覆盖,不再追加
@@ -157,7 +158,7 @@ const ReviewModal: React.FC<ReviewModalProps> = React.memo(
 
     // 当前分页渲染数据
     const displayList = filterReviewList(); 
-    
+    // debugger;
     // 生成图库一维图片数组
   //  const buildGalleryList = useCallback((reviewList: any[]): GalleryItem[] => {
   //    const allGallery: GalleryItem[] = [];
@@ -416,8 +417,9 @@ const tabLabelMap: Record<ReviewTabType, string> = {
               ) : displayList.length === 0 ? (
                 <p className="text-center text-gray-500 py-10">No reviews</p>
               ) : (
-                displayList.map((edge: ProductReviewEdge, reviewIdx) => {
-                  const item = edge.node;
+                displayList.map((edge: ProductReviewNode, reviewIdx) => {
+                  const item = edge;
+                  // debugger;
                   const { liked, like_count } = getCurrentLike(item);
                   // const item = edge.node;
                   return (