zhangzf 2 днів тому
батько
коміт
96b26fc495
23 змінених файлів з 2031 додано та 680 видалено
  1. BIN
      public/image/account/birthday_happy.png
  2. BIN
      public/image/account/birthday_waiting.png
  3. 71 0
      src/app/(public)/customer/(has-dashboard)/account/birthday/page.tsx
  4. 33 29
      src/app/(public)/customer/(has-dashboard)/account/edit/page.tsx
  5. 210 0
      src/app/(public)/customer/(has-dashboard)/account/mypoints/_components/PointList.tsx
  6. 323 0
      src/app/(public)/customer/(has-dashboard)/account/mypoints/earn/page.tsx
  7. 158 58
      src/app/(public)/customer/(has-dashboard)/account/mypoints/page.tsx
  8. 2 2
      src/app/(public)/customer/(has-dashboard)/account/page.tsx
  9. 2 2
      src/app/(public)/customer/(has-dashboard)/address/_components/DefaultAddress.tsx
  10. 77 40
      src/app/(public)/customer/(has-dashboard)/address/eidt/[id]/page.tsx
  11. 1 1
      src/app/(public)/customer/(has-dashboard)/address/new/_components/CustomerAddressPhone.tsx
  12. 84 50
      src/app/(public)/customer/(has-dashboard)/address/new/_components/NewAddressDefault.tsx
  13. 12 38
      src/app/(public)/customer/(has-dashboard)/address/page.tsx
  14. 15 15
      src/app/(public)/customer/(has-dashboard)/order/history/_components/OrderHistory.tsx
  15. 1 1
      src/app/(public)/customer/(has-dashboard)/order/track/order_id/[id]/page.tsx
  16. 134 0
      src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/OrderAddressCard.tsx
  17. 3 23
      src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/OrderHeader.tsx
  18. 65 33
      src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/OrderInfoBlock.tsx
  19. 149 19
      src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/ProductList.tsx
  20. 14 12
      src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/page.tsx
  21. 334 24
      src/app/(public)/customer/_components/CustomerSidebar.tsx
  22. 340 332
      src/components/customer/accountSwiper.tsx
  23. 3 1
      src/types/api/customer/order/vieworder.ts

BIN
public/image/account/birthday_happy.png


BIN
public/image/account/birthday_waiting.png


+ 71 - 0
src/app/(public)/customer/(has-dashboard)/account/birthday/page.tsx

@@ -0,0 +1,71 @@
+"use client";
+// import { useState, useEffect } from "react";
+// import { clientFetch } from "@/lib/restApiClient";
+import Image from "next/image";
+
+const BIRTH_MONTH = 3; 
+const BIRTH_DAY = 22;
+
+
+function isTodayBirthday(): boolean {
+  const now = new Date();
+  const currentMonth = now.getMonth();
+  const currentDate = now.getDate();
+  return currentMonth === BIRTH_MONTH && currentDate === BIRTH_DAY;
+}
+export default function GetPoints() {
+  const isBirthdayToday = isTodayBirthday();
+
+  return (
+    <div className="w-full min-h-screen bg-white  h-full">
+      <h1 className="font-normal text-2xl leading-ly-28 mb-6">
+        Birthday Rewards
+      </h1>
+      <div className="relative w-full max-w-[1220px] mx-auto aspect-[1217/960] overflow-hidden rounded-xl">
+        {/* 底层背景大图,整张底图 */}
+        <Image
+          src={
+            isBirthdayToday
+              ? "/image/account/birthday_happy.png"
+              : "/image/account/birthday_waiting.png"
+          }
+          alt={
+            isBirthdayToday
+              ? "Birthday active gift"
+              : "Birthday upcoming notice"
+          }
+          fill
+          className="object‑contain"
+          priority
+        />
+
+        {isBirthdayToday ? (
+        
+          <>
+            <div className="absolute top-[44%] left-1/2 -translate-x-1/2 w-[62%] text-center text-white text-md md:text-lg font-medium">
+              A Little Birthday Gift, Just for You
+            </div>
+            <div className="absolute top-[51%] left-1/2 -translate-x-1/2 text-white text-ly-24 font-bold">
+              Enjoy $15 OFF
+            </div>
+            <div className="absolute top-[60%] left-1/2 -translate-x-1/2 w-[64%] text-center text-white text-md md:text-lg font-medium">
+              with your Birthday Gift Card.Treat Yourself & Shine.
+            </div>
+          </>
+        ) : (
+       
+          <>
+            <div className="absolute top-[51%] left-1/2 -translate-x-1/2 w-[60%] text-center text-white text-md md:text-lg leading-relaxed">
+              <p>Your special day is coming!</p>
+              <p>We&apos;ll be celebrating you all month long.</p>
+              <p>Your $15 Birthday Gift Card will be ready soon.</p>
+            </div>
+            <div className="absolute top-[75%] left-1/2 -translate-x-1/2 text-white text-xl font-bold">
+              [Apr. 22]
+            </div>
+          </>
+        )}
+      </div>
+    </div>
+  );
+}

+ 33 - 29
src/app/(public)/customer/(has-dashboard)/account/edit/page.tsx

@@ -174,8 +174,8 @@ const AccountEditPage = () => {
   return (
     <div className="w-full h-full">
       <FormProvider {...addressForm}>
-      <form className="overflow-hidden"  onSubmit={handleSubmit(addressFormOnSubmit)} >
-        <div className=" text-black py-4 px-4 flex items-center border-b border-[#F9F9F9FF]">
+      <form className="w-full min-h-screen bg-white  h-full"  onSubmit={handleSubmit(addressFormOnSubmit)} >
+        {/* <div className=" text-black py-4 px-4 flex items-center border-b border-[#F9F9F9FF]">
           <Link
             className="mr-4"
             href="/customer/account"
@@ -200,11 +200,12 @@ const AccountEditPage = () => {
           <h1 className="text-lg w-full text-center  font-normal">
             Information
           </h1>
-        </div>
+        </div> */}
+         <h1 className="font-normal text-2xl leading-ly-28 mb-6">My Profile</h1>
         <input name="form_key" type="hidden" value="kW7wucXHATeeem75"></input>
-        <div className=" p-[20px_10px]">
+        <div className="rounded-md bg-white shadow-[0_2px_12px_rgba(0,0,0,0.1)] p-6">
           <div className="text-start mb-3">*Name</div>
-          <div className="flex justify-between items-center">
+          <div className="flex justify-between items-center gap-6">
             <input
               // name="firstname" // 必须和state字段对应
              
@@ -212,7 +213,7 @@ const AccountEditPage = () => {
                   required: "First name is required",
                 })}
               placeholder="firstname"
-              className="w-43 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
+              className="flex-1 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
             />
             <input
               // name="lastname" // 必须和state字段对应
@@ -221,10 +222,22 @@ const AccountEditPage = () => {
                   required: "Last name is required",
                 })}
               placeholder="lastname"
-              className="w-43 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+              className="flex-1 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
             />
           </div>
-          <div className="mt-7.5">
+           <div className="mt-6">
+            <label >Email Address*</label>
+            <input
+              // name="email" // 必须和state字段对应
+            
+               {...register("email", {
+                  required: "email name is required",
+                })}
+              placeholder="email"
+              className="w-full mt-3 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+            />
+          </div>
+          <div className="mt-6">
             <Controller
         name="date"
         control={control}
@@ -255,20 +268,9 @@ const AccountEditPage = () => {
             {/* label={"Date of Birth"} */}
           </div>
           <CustomerAddressPhone />
-          <div className="mt-7.5">
-            <label >Email Address*</label>
-            <input
-              // name="email" // 必须和state字段对应
-            
-               {...register("email", {
-                  required: "email name is required",
-                })}
-              placeholder="email"
-              className="w-full mt-3 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
-            />
-          </div>
+         
           {/* 选择框(开关)*/}
-          <label className="flex items-center gap-2 cursor-pointer mt-7.5">
+          <label className="flex items-center gap-2 cursor-pointer mt-4">
             <input
               type="checkbox"
               checked={showContent}
@@ -287,7 +289,7 @@ const AccountEditPage = () => {
                   required: "Password is required",
                 })}
                 placeholder="Old password"
-                className="mt-7.5 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+                className="mt-6 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
               />
               <input
                 type="password"
@@ -297,16 +299,18 @@ const AccountEditPage = () => {
                   required: "Password is required",
                 })}
                 placeholder="* New Password"
-                className=" mt-7.5 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+                className=" mt-6 w-full h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
               />
             </div>
           )}
-          <button
-            className="w-full h-12 leading-12  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-7.5"
-            type="submit"
-          >
-            Save
-          </button>
+          <div className="w-89.5 ml-auto">
+            <button
+              className="w-full h-12 leading-12  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-6"
+              type="submit"
+            >
+              Save
+            </button>
+          </div>
         </div>
       </form>
       </FormProvider>

+ 210 - 0
src/app/(public)/customer/(has-dashboard)/account/mypoints/_components/PointList.tsx

@@ -0,0 +1,210 @@
+"use client";
+
+import { useState } from "react";
+import { clientFetch } from "@/lib/restApiClient";
+
+export interface PointListItem {
+  id: number;
+  type: number;
+  type_text: string;
+  amount: number;
+  balance: number;
+  detail: string;
+  created_at: string;
+}
+
+interface PointListProps {
+  initialData: PointListItem[];
+  initialPage?: number;
+  initialHasMore: boolean;
+}
+
+const PAGE_SIZE = 10;
+
+export default function PointList({
+  initialData,
+  initialPage = 1,
+  initialHasMore,
+}: PointListProps) {
+  // 第一页数据直接使用父组件传下来的
+  const [listData, setListData] = useState<PointListItem[]>(initialData);
+
+  // 父组件拿到的是 page=1
+  const [page, setPage] = useState(initialPage);
+
+  const [hasMore, setHasMore] = useState(initialHasMore);
+
+  const [loading, setLoading] = useState(false);
+
+  /**
+   * 这里只负责加载下一页
+   * 第一次点击就是 page=2
+   */
+  const handleMore = async () => {
+    if (loading || !hasMore) return;
+
+    const nextPage = page + 1;
+
+    setLoading(true);
+
+    try {
+      const resp = await clientFetch(
+        `/api/customer/reward-points/history?page=${nextPage}&per_page=${PAGE_SIZE}&amountType=`,
+      );
+
+      if (resp.message === "Success") {
+        const newList: PointListItem[] = resp?.data?.history ?? [];
+
+        const pagination = resp?.pagination;
+
+        // 下一页数据追加到原有列表
+        setListData((prev) => [...prev, ...newList]);
+
+        // 直接使用后端返回的 has_more
+        setHasMore(pagination?.has_more ?? false);
+
+        // 请求成功后才更新当前 page
+        setPage(pagination?.current_page ?? nextPage);
+      }
+    } catch (err) {
+      console.error("reward history load more error:", err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  function formatDate(date: string) {
+    const d = new Date(date.replace(" ", "T"));
+
+    return d.toLocaleString("en-US", {
+      month: "short",
+      day: "numeric",
+      year: "numeric",
+      hour: "numeric",
+      minute: "2-digit",
+    });
+  }
+
+  return (
+    <div
+      className="
+        mt-8
+        rounded-[8px]
+        bg-white
+        pb-8
+        shadow-[0_2px_12px_rgba(0,0,0,0.08)]
+      "
+    >
+      {/* Header */}
+      <div
+        className="
+          grid
+          h-[52px]
+          grid-cols-[1fr_200px_150px]
+          items-center
+          bg-[#FAFAFA]
+          px-8
+          text-ly-16
+          font-normal
+        "
+      >
+        <div>Date & Activity</div>
+
+        <div>Points</div>
+
+        <div>Balance</div>
+      </div>
+
+      {/* List */}
+      {listData.map((item) => (
+        <div
+          key={item.id}
+          className="
+            grid
+            min-h-[88px]
+            grid-cols-[1fr_200px_150px]
+            items-center
+            border-b
+            border-[#ACBDB8]
+            px-8
+          "
+        >
+          {/* Activity */}
+          <div>
+            <div
+              className="
+                text-ly-14
+                font-normal
+                text-black
+              "
+            >
+              {item.detail}
+            </div>
+
+            <div
+              className="
+                mt-1
+                text-ly-14
+                text-[#666]
+              "
+            >
+              {formatDate(item.created_at)}
+            </div>
+          </div>
+
+          {/* Points */}
+          <div
+            className={`
+              text-ly-14
+                font-normal
+              ${item.amount > 0 ? "text-[#FF6B35]" : "text-black"}
+            `}
+          >
+            {item.amount > 0 ? `+${item.amount}` : item.amount}
+          </div>
+
+          {/* Balance */}
+          <div
+            className="
+              text-ly-14
+              font-normal
+              text-black
+            "
+          >
+            {item.balance}
+          </div>
+        </div>
+      ))}
+
+      {/* MORE */}
+      <div
+        className="
+          flex
+          justify-center
+          pt-8
+        "
+      >
+        <button
+          type="button"
+          disabled={loading || !hasMore}
+          onClick={handleMore}
+          className={`
+            h-[46px]
+            min-w-[160px]
+            rounded-full
+            border
+            text-[16px]
+            transition-colors
+            ${
+              hasMore
+                ? "border-[#036141] text-[#036141] hover:bg-[#036141] hover:text-white"
+                : "cursor-default border-[#ccc] text-[#999]"
+            }
+          `}
+        >
+          {loading ? "Loading..." : hasMore ? "MORE" : "NO MORE"}
+        </button>
+      </div>
+    </div>
+  );
+}

+ 323 - 0
src/app/(public)/customer/(has-dashboard)/account/mypoints/earn/page.tsx

@@ -0,0 +1,323 @@
+"use client";
+// import Link from "next/link";
+import { useState, useEffect } from "react";
+import { clientFetch } from "@/lib/restApiClient";
+
+export default function GetPoints() {
+  const [taskData, setTaskData] = useState<any>(null);
+  const [loading, setLoading] = useState(true);
+  useEffect(() => {
+    const fetchRewardPointsHistory = async () => {
+      try {
+        setLoading(true);
+        const res = await clientFetch(
+          "/api/customer/reward-points/follow-status",
+        );
+
+        setTaskData(res.data);
+        console.log("积分任务数据:", res.data);
+      } catch (error) {
+        console.error("获取任务状态失败:", error);
+      } finally {
+        setLoading(false);
+      }
+    };
+    fetchRewardPointsHistory();
+  }, []);
+
+  function canTriggerTask(item: (typeof earnPointsItems)[0]) {
+    if (!taskData) return false;
+
+    const { type } = item;
+    const { browsed_product_ids, platforms } = taskData;
+
+    if (!type) return true;
+
+    if (browsed_product_ids.includes(type)) {
+      return false;
+    }
+
+    const platform = platforms.find((p: any) => p.code === type);
+    if (platform) {
+      return !platform.followed;
+    }
+
+    return true;
+  }
+
+  const handleClaimPoints = async (item: any) => {
+    if (!canTriggerTask(item)) return;
+
+    try {
+      console.log("触发领取积分,任务类型:", item.type);
+
+      // const getpointsres = await clientFetch("/api/customer/reward-points/follow", {
+      //   method: "POST",
+      //   body: JSON.stringify({ platform: item.type }),
+      // });
+      // 这个浏览获取积分接口后面放到列表页去调
+      const getpointsres = await clientFetch(
+        "/api/customer/reward-points/browse-product",
+        {
+          method: "POST",
+          body: JSON.stringify({ categorys: item.type }),
+        },
+      );
+      if (getpointsres.success) {
+        alert("Points claimed successfully!");
+      }
+
+      const res = await clientFetch(
+        "/api/customer/reward-points/follow-status",
+      );
+      setTaskData(res.data);
+    } catch (err) {
+      console.error("领取失败", err);
+      alert("Failed to claim points");
+    }
+  };
+
+  const earnPointsItems = [
+    {
+      title: "Share a plain text review",
+      desc: "To Get 50 points",
+      points: "",
+      type: "",
+      btnText: "GO",
+    },
+    {
+      title: "Share your wig review with photos/videos",
+      desc: "To Get 100 Points",
+      points: "",
+      type: "",
+      btnText: "GO",
+    },
+    {
+      title: "Log In Our Website",
+      desc: "Every Day To Get Points",
+      points: "",
+      type: "",
+      btnText: "GO",
+    },
+    {
+      title: "Browse New Arrival Page ≥ 15 Seconds",
+      desc: "To Get 10 Points",
+      points: "",
+      type: "aaa",
+      btnText: "Browse",
+    },
+    {
+      title: "Browse HD Lace Wigs ≥ 15 Seconds",
+      desc: "To Get 10 Points",
+      points: "",
+      type: "ccc",
+      btnText: "Browse",
+    },
+    {
+      title: "Browse Ready To Go Wigs ≥ 15 Seconds",
+      desc: "To Get 10 Points",
+      points: "",
+      type: "bbbb",
+      btnText: "Browse",
+    },
+    {
+      title: "Follow Our Instagram Account",
+      desc: "To Get 20 Points",
+      points: "",
+      type: "ig",
+      btnText: "GO",
+    },
+    {
+      title: "Follow Our Facebook Account",
+      desc: "To Get 20 Points",
+      points: "",
+      type: "fb",
+      btnText: "GO",
+    },
+    {
+      title: "Follow Our Youtube Channel",
+      desc: "To Get 20 Points",
+      points: "",
+      type: "ytb",
+      btnText: "GO",
+    },
+    {
+      title: "Follow Our Tiktok Account",
+      desc: "To Get 20 Points",
+      points: "",
+      type: "tt",
+      btnText: "GO",
+    },
+  ];
+
+  return (
+    <div className="w-full min-h-screen bg-white  h-full">
+      <h1 className="font-normal text-2xl leading-ly-28 mb-6">
+        How To Get More Points?
+      </h1>
+      {/* 固定 QA */}
+      <div className="mb-6">
+        {/* Question */}
+        <div className="flex items-center gap-3">
+          <div
+            className="
+          flex
+          h-9
+          w-9
+          shrink-0
+          items-center
+          justify-center
+          rounded-full
+          bg-[#036141]
+          text-ly-18
+          text-white
+        "
+          >
+            Q
+          </div>
+
+          <div
+            className="
+          text-ly-24
+          font-medium
+          leading-ly-34
+          text-[#036141]
+        "
+          >
+            Why do I need to earn points?
+          </div>
+        </div>
+
+        {/* Answer */}
+        <div className="mt-5 flex items-center gap-3">
+          <div
+            className="
+          flex
+          h-9
+          w-9
+          shrink-0
+          items-center
+          justify-center
+          rounded-full
+          border
+          border-[#222]
+          bg-white
+          text-ly-18
+          text-black
+        "
+          >
+            A
+          </div>
+
+          <div
+            className="
+          text-ly-18
+          leading-ly-26
+          text-black
+        "
+          >
+            Points can be redeemed for gift card, gift packs or wig to help you
+            save money.
+          </div>
+        </div>
+      </div>
+
+      {/* Task List */}
+      {loading ? (
+        <div className="py-10 text-center text-ly-16 text-[#666]">
+          Loading tasks...
+        </div>
+      ) : (
+        <div
+          className="
+        grid
+        grid-cols-2
+        gap-x-6
+        gap-y-6
+      "
+        >
+          {earnPointsItems.map((item, index) => {
+            const canClick = canTriggerTask(item);
+
+            return (
+              <div
+                key={index}
+                className="
+              flex
+              min-h-[85px]
+              items-center
+              justify-between
+              bg-[#F8F8F8]
+              p-4
+            "
+              >
+                {/* 左侧文案 */}
+                <div className="min-w-0 flex-1 pr-8">
+                  <h3
+                    className="
+                  text-ly-16
+                  font-normal
+                  leading-[24px]
+                  text-black
+                "
+                  >
+                    {item.title}
+                  </h3>
+
+                  {/* desc + points 按设计稿放到标题下面 */}
+                  {(item.desc || item.points) && (
+                    <div
+                      className="
+                    mt-3
+                    text-ly-13
+                    leading-5.25
+                    text-black
+                  "
+                    >
+                      {item.desc && (
+                        <span>
+                          {item.desc}
+                          {item.points ? " " : ""}
+                        </span>
+                      )}
+
+                      {item.points && <span>{item.points}</span>}
+                    </div>
+                  )}
+                </div>
+
+                {/* 右侧按钮 */}
+                <button
+                  type="button"
+                  onClick={() => handleClaimPoints(item)}
+                  disabled={!canClick}
+                  className={`
+                flex
+                h-[36px]
+                w-[84px]
+                shrink-0
+                items-center
+                justify-center
+                rounded-full
+                border
+                bg-white
+                text-ly-14
+                font-normal
+                transition-colors
+                ${
+                  canClick
+                    ? "border-[#222] text-black hover:bg-[#f2f2f2] cursor-pointer"
+                    : "border-[#D0D0D0] text-[#999] cursor-default"
+                }
+              `}
+                >
+                  {canClick ? item.btnText : "Done"}
+                </button>
+              </div>
+            );
+          })}
+        </div>
+      )}
+    </div>
+  );
+}

+ 158 - 58
src/app/(public)/customer/(has-dashboard)/account/mypoints/page.tsx

@@ -1,62 +1,59 @@
 import Image from "next/image";
 import Link from "next/link";
-import PointsRule from "./_components/PointsRule"
+// import PointsRule from "./_components/PointsRule"
 import GetPoints from "./_components/GetPoints"
-const AccountPointsPage = () => {
+import { restApiFetch } from "@utils/bagisto/index";
+import PointList, {
+  type PointListItem,
+} from "./_components/PointList";
+
+const AccountPointsPage = async() => {
+  const resp = await restApiFetch({
+    api: `/customer/reward-points/history?page=${1}&per_page=${10}&amountType=`,
+    method: "GET", 
+  });
+  console.log("resp--------------------------aaaaa",resp);
+  
+   const currentPoints =
+    resp?.body.data?.current_points ?? 0;
+
+  const history =
+    resp?.body.data?.history ?? [];
+
+  const currentPage =
+    resp?.body.pagination?.current_page ?? 1;
+      const hasMore =
+    resp?.body.pagination?.has_more ?? false;
   // 模拟用户积分数据
   const userPoints = 100009;
-  const hrefitems = [
-    {
-      title: "GiftCard",
-      href: "/customer/account/pointsreceivelist",
-      img: "/image/account/202306141502.png",
-    },
-    {
-      title: "Hair",
-      href: "/customer/account/redeemhair",
-      img: "/image/account/202306141505.png",
-    },
-    {
-      title: "Accessories",
-      href: "/customer/account/accessories",
-      img: "/image/account/202306141506.png",
-    },
-    {
-      title: "Cash",
-      href: "/customer/account/wheeloffortune",
-      img: "/image/account/202306141504.png",
-    },
-  ];
+  // const hrefitems = [
+  //   {
+  //     title: "GiftCard",
+  //     href: "/customer/account/pointsreceivelist",
+  //     img: "/image/account/202306141502.png",
+  //   },
+  //   {
+  //     title: "Hair",
+  //     href: "/customer/account/redeemhair",
+  //     img: "/image/account/202306141505.png",
+  //   },
+  //   {
+  //     title: "Accessories",
+  //     href: "/customer/account/accessories",
+  //     img: "/image/account/202306141506.png",
+  //   },
+  //   {
+  //     title: "Cash",
+  //     href: "/customer/account/wheeloffortune",
+  //     img: "/image/account/202306141504.png",
+  //   },
+  // ];
 
   return (
-    <div className="w-full h-full">
-      <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
-        <Link
-          href={"/customer/account"}
-          className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
-        >
-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            width="24"
-            height="24"
-            viewBox="0 0 24 24"
-            fill="none"
-          >
-            <path
-              stroke="rgba(0, 0, 0, 1)"
-              stroke-width="1.2"
-              stroke-linejoin="round"
-              stroke-linecap="round"
-              d="M15 18L9 12L15 6"
-            ></path>
-          </svg>
-        </Link>
-        <span className="vipReturnTitles">My Points</span>
-      </div>
-      <div className=" bg-[#FFF5F2] px-4 py-8 max-w-3xl mx-auto pt-6 pb-5">
-        {/* 顶部积分展示区 */}
+    <div className="w-full min-h-screen bg-white  h-full">
+      <h1 className="font-normal text-2xl leading-ly-28 mb-6">My Rewards Points</h1>
+      {/* <div className=" bg-[#FFF5F2] px-4 py-8 max-w-3xl mx-auto pt-6 pb-5">
         <div className="flex items-center justify-between mb-5">
-          {/* 积分图标 + 数值 */}
           <div className="flex items-center gap-2.5">
             <div className="w-17.5 h-17.5">
               <svg
@@ -113,7 +110,6 @@ const AccountPointsPage = () => {
               <p className="text-xs text-black">My Points</p>
             </div>
           </div>
-          {/* Details & Rules 链接 */}
           <div className="flex gap-4 text-sm font-medium">
             <Link
               href="/customer/account/pointlist"
@@ -124,8 +120,6 @@ const AccountPointsPage = () => {
             <PointsRule/>
           </div>
         </div>
-
-        {/* 功能按钮区 */}
         <div className="flex justify-between items-center ">
           {hrefitems.map((item, index) => (
             <a key={index} href={item.href} className="">
@@ -141,14 +135,120 @@ const AccountPointsPage = () => {
             </a>
           ))}
         </div>
+      </div> */}
+    <section className="w-full">
+      <div
+        className="
+          relative
+          min-h-[191px]
+          w-full
+          rounded-2xl
+         bg-[linear-gradient(180deg,rgba(217,255,242,1)_0%,rgba(255,255,255,1)_100%)] border border-[rgba(88,184,149,1)]
+          px-8
+          pt-6
+        "
+      >
+        <div className="flex items-start justify-between gap-8">
+          {/* Points */}
+          <div>
+            <h2
+              className="
+                text-ly-16
+                font-normal
+                leading-[20px]
+                text-black
+              "
+            >
+              My Reward Points
+            </h2>
+
+            <div
+              className="
+                mt-[7px]
+                text-[62px]
+                font-bold
+                leading-none
+                tracking-[-2px]
+                text-black
+              "
+            >
+              {userPoints}
+            </div>
+          </div>
+
+          {/* Action Buttons */}
+          <div className="flex items-center gap-[20px]">
+            <button
+              type="button"
+              className="
+                flex
+                h-[45px]
+                w-[159px]
+                items-center
+                justify-center
+                rounded-full
+                border
+                border-[#222]
+                bg-white
+                text-ly-14
+                font-medium
+                text-[#111]
+                transition-colors
+              "
+            >
+              Pointes Redeem
+            </button>
+
+            <Link
+            href={"/customer/account/mypoints/earn"}
+              type="button"
+              className="
+                flex
+                h-[45px]
+                w-[159px]
+                items-center
+                justify-center
+                rounded-full
+                border
+                border-[#222]
+                bg-white
+                text-ly-14
+                font-normal
+                text-[#111]
+                transition-colors
+              "
+            >
+              Earn Points
+            </Link>
+          </div>
+        </div>
+
+        {/*
+          Daily Check-In 先不做。
+
+          后面做的时候建议放在这里,并使用:
+
+          absolute
+          left-[46px]
+          right-[46px]
+          top-[163px]
+
+          这样就可以像设计稿一样压在浅绿色区域下面。
+        */}
       </div>
-      {/* 积分获取区 */}
-      <div className="space-y-6">
+    </section>
+    
+        {/* 积分历史 */}
+      <PointList
+        initialData={history}
+        initialPage={currentPage}
+        initialHasMore={hasMore}
+      />
+      {/* <div className="space-y-6">
         <h2 className="text-base mt-6 mb-8  font-bold text-black text-center">
           Earn Points
         </h2>
-        <GetPoints />
-      </div>
+      </div> */}
     
     </div>
   );

+ 2 - 2
src/app/(public)/customer/(has-dashboard)/account/page.tsx

@@ -140,8 +140,8 @@ console.log('accountres-----------------------------:',res);
       {/* <div className="h-2.5 bg-[#F0F0F0]"></div> */}
       {/* 商品推荐 */}
       <div className="mb-12.5">
-        <div className="flex  justify-between items-center pt-6 pb-5 pl-4 pr-4">
-          <h1 className="font-medium text-ly-18 leading-ly-26 text-base">Recommended For You</h1>
+        <div className="flex  justify-between items-center pt-9 pb-6 ">
+          <h1 className="font-medium leading-ly-26 text-ly-18">Recommended For You</h1>
           {/* <a className="text-xs text-[#666666]" href="/flash-sale.html">
             View More  
              &gt;

+ 2 - 2
src/app/(public)/customer/(has-dashboard)/address/_components/DefaultAddress.tsx

@@ -12,8 +12,8 @@ export default function DefaultAddress() {
         `/api/customer/token/address`, ///api/shop/customer-addresses?page=${page}
       );
       console.log("完整返回:", resp.data);
-      resp.data.data = Array.isArray(resp.data.data) ? resp.data.data : [];
-      const sortedList = [...resp.data.data].sort((a, b) => {
+      resp.data = Array.isArray(resp.data) ? resp.data : [];
+      const sortedList = [...resp.data].sort((a, b) => {
         // true(1) 排在 false(0) 前面
         if (a.default_address !== b.default_address) {
           return b.default_address ? 1 : -1;

+ 77 - 40
src/app/(public)/customer/(has-dashboard)/address/eidt/[id]/page.tsx

@@ -120,41 +120,18 @@ const CustomerAddressEditPage = () => {
     load();
   }, [id, reset]);
   return (
-    <div className="w-full h-full">
-      <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
-        <Link
-          href={"/customer/address"}
-          // onClick={() => window.history.back()}
-          className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
-        >
-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            width="24"
-            height="24"
-            viewBox="0 0 24 24"
-            fill="none"
-          >
-            <path
-              stroke="rgba(0, 0, 0, 1)"
-              stroke-width="1.2"
-              stroke-linejoin="round"
-              stroke-linecap="round"
-              d="M15 18L9 12L15 6"
-            ></path>
-          </svg>
-        </Link>
-        <span className="vipReturnTitles">ADDRESS EDIT</span>
-      </div>
-      <div className="max-w-md mx-auto bg-[#f8f8f8]">
+    <div className="w-full min-h-screen bg-white  h-full">
+       <h1 className="font-normal text-2xl leading-ly-28 mb-6">Edit Address</h1>
+      <div className="rounded-md bg-white shadow-[0_2px_12px_rgba(0,0,0,0.1)] p-6">
         <FormProvider {...addressForm}>
           <form
             className="overflow-hidden"
             onSubmit={handleSubmit(addressFormOnSubmit)}
           >
-            <div className="bg-[#f8f8f8] p-[20px_10px]">
+            <div className="">
               {/* 姓名 */}
               <div className="text-start">*Name</div>
-              <div className="flex justify-between items-center gap-3">
+              <div className="flex justify-between items-center gap-6">
                 {/* firstname */}
                 <input
                   type="text"
@@ -162,7 +139,7 @@ const CustomerAddressEditPage = () => {
                   {...register("first_name", {
                     required: "First name is required",
                   })}
-                  className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
+                  className="flex-1 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
                 />
                 {/* lastname */}
                 <input
@@ -171,7 +148,7 @@ const CustomerAddressEditPage = () => {
                   {...register("last_name", {
                     required: "Last name is required",
                   })}
-                  className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+                  className="flex-1 h-11 rounded-lg border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
                 />
               </div>
               {errors.first_name && (
@@ -187,6 +164,7 @@ const CustomerAddressEditPage = () => {
 
               {/* 手机号 */}
               <CustomerAddressPhone />
+              <div className="mt-6">
               <Controller
                 control={addressForm.control}
                 name="country"
@@ -201,15 +179,17 @@ const CustomerAddressEditPage = () => {
                       {...register("country", {
                         required: "Country/Region field is required",
                       })}
+                      
                       options={countriesOptions}
                       error={errors.country?.message as string}
                     />
                   );
                 }}
               />
+              </div>
               {/* <input {...register("country")} /> */}
               {/* 邮箱 */}
-              <div className="mt-4">
+              <div className="mt-6">
                 <label>Email Address*</label>
                 <input
                   type="email"
@@ -221,7 +201,7 @@ const CustomerAddressEditPage = () => {
                       message: "Invalid email",
                     },
                   })}
-                  className="w-full h-11 rounded-sm border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
+                  className="w-full h-11 rounded-lg border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
                 />
                 {errors.email && (
                   <p className="text-red-500 text-sm mt-1">
@@ -231,47 +211,104 @@ const CustomerAddressEditPage = () => {
               </div>
 
               {/* 邮编 */}
-              <div className="mt-4">
+              <div className="mt-6">
                 <label>Zip/Postal Code</label>
                 <input
                   type="text"
                   placeholder="Postcode"
                   {...register("postcode")}
-                  className="w-full h-11 rounded-sm border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
+                  className="w-full h-11 rounded-lg border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
                 />
               </div>
 
               {/* 省/州 */}
-              <div className="mt-4">
+              <div className="mt-6">
                 <label>State / Province</label>
                 <input
                   type="text"
                   placeholder="State"
                   {...register("state")}
-                  className="w-full h-11 rounded-sm border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
+                  className="w-full h-11 rounded-lg border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
                 />
               </div>
 
               {/* 城市 */}
-              <div className="mt-4">
+              <div className="mt-6">
                 <label>City</label>
                 <input
                   type="text"
                   placeholder="City"
                   {...register("city")}
-                  className="w-full h-11 rounded-sm border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
+                  className="w-full h-11 rounded-lg border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
                 />
               </div>
 
               {/* 默认地址 */}
-              <label className="flex items-center gap-2 cursor-pointer mt-4">
+              {/* <label className="flex items-center gap-2 cursor-pointer mt-4">
                 <input
                   type="checkbox"
                   {...register("default_address", { valueAsNumber: true })}
                   className="w-5 h-5"
                 />
                 <span>Use as my default address</span>
-              </label>
+              </label> */}
+                <label className="flex items-center justify-between cursor-pointer mt-6 border rounded-[12px] px-5 py-4">
+     
+                 <span className="text-base">
+                   Use as my default address
+                 </span>
+               
+               
+                 <div className="relative">
+               
+                   <input
+                     type="checkbox"
+                     {...register("default_address")}
+                     className="peer sr-only"
+                   />
+               
+               
+                   {/* switch 背景 */}
+                   <div
+                     className="
+                       w-11
+                       h-6
+                       rounded-full
+                       bg-[#aaa]
+                       border
+                       border-[#D9D9D9]
+                       transition-all
+               
+                       peer-checked:bg-[#01754A]
+                       peer-checked:border-[#01754A]
+                     "
+                   />
+               
+               
+                   {/* 小圆点 */}
+                   <div
+                     className="
+                       absolute
+                       top-[2px]
+                       left-[3px]
+               
+                       w-5
+                       h-5
+               
+                       bg-white
+                       rounded-full
+               
+                       shadow
+               
+                       transition-transform
+               
+                       peer-checked:translate-x-5
+                     "
+                   />
+               
+                 </div>
+               
+                </label>
 
               {/* 提交按钮 */}
               <button

+ 1 - 1
src/app/(public)/customer/(has-dashboard)/address/new/_components/CustomerAddressPhone.tsx

@@ -60,7 +60,7 @@ export default function CustomerAddressPhone() {
   // );
 
   return (
-    <div className="w-full mt-4">
+    <div className="w-full mt-6">
       <label className="text-ly-12 block mb-3 font-semibold">
         Phone number *
       </label>

+ 84 - 50
src/app/(public)/customer/(has-dashboard)/address/new/_components/NewAddressDefault.tsx

@@ -81,38 +81,15 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
     <div className="w-full h-full">
       <FormProvider {...addressForm}>
         <form
-          className="overflow-hidden"
+          className="w-full min-h-screen bg-white  h-full"
           onSubmit={handleSubmit(addressFormOnSubmit)}
         >
-          {/* 头部 */}
-          <div className="bg-[#fff] px-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
-            <div
-              onClick={() => window.history.back()}
-              className="absolute left-2.5 text-2xl inline-block top-1/2 -translate-y-1/2"
-            >
-              <svg
-                xmlns="http://www.w3.org/2000/svg"
-                width="24"
-                height="24"
-                viewBox="0 0 24 24"
-                fill="none"
-              >
-                <path
-                  stroke="rgba(0, 0, 0, 1)"
-                  strokeWidth="1.2"
-                  strokeLinejoin="round"
-                  strokeLinecap="round"
-                  d="M15 18L9 12L15 6"
-                ></path>
-              </svg>
-            </div>
-            <span>Add New Address</span>
-          </div>
+           <h1 className="font-normal text-2xl leading-ly-28 mb-6">New Address</h1>
 
-          <div className="bg-[#f8f8f8] p-[20px_10px]">
+          <div className="rounded-md bg-white shadow-[0_2px_12px_rgba(0,0,0,0.1)] p-6">
             {/* 姓名 */}
              <div className="text-start">*Name</div>
-            <div className="flex justify-between items-center gap-3">
+            <div className="flex justify-between items-center gap-6">
               {/* firstname */}
               <input
                 type="text"
@@ -120,7 +97,7 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
                 {...register("first_name", {
                   required: "First name is required",
                 })}
-                className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
+                className="flex-1 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal"
               />
               {/* lastname */}
               <input
@@ -129,27 +106,14 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
                 {...register("last_name", {
                   required: "Last name is required",
                 })}
-                className="w-43 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
+                className="flex-1 h-11 rounded-sm border border-solid border-[rgba(102,102,102,1)] indent-4 text-[#a6a6a6] text-base leading-11 font-normal box-[unset]"
               />
             </div>
             {errors.first_name && <p className="text-red-500 text-sm mt-1">{errors.first_name.message}</p>}
             {errors.last_name && <p className="text-red-500 text-sm mt-1">{errors.last_name.message}</p>}
 
-            {/* 手机号 */}
-            <CustomerAddressPhone />
-
-            {/* 国家 */}
-            <div className="mt-4">
-              <Select
-                placeholder="Country/Region"
-                {...register("country", { required: "Country is required" })}
-                options={countriesOptions}
-                error={errors.country?.message as string}
-              />
-            </div>
-
-            {/* 邮箱 */}
-            <div className="mt-4">
+           {/* 邮箱 */}
+            <div className="mt-6">
               <label>Email Address*</label>
               <input
                 type="email"
@@ -162,9 +126,20 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
               />
               {errors.email && <p className="text-red-500 text-sm mt-1">{errors.email.message}</p>}
             </div>
+            {/* 国家 */}
+            <div className="mt-6">
+              <Select
+                placeholder="Country/Region"
+                {...register("country", { required: "Country is required" })}
+                options={countriesOptions}
+                error={errors.country?.message as string}
+              />
+            </div>
+
+           
 
             {/* 邮编 */}
-            <div className="mt-4">
+            <div className="mt-6">
               <label>Zip/Postal Code</label>
               <input
                 type="text"
@@ -175,7 +150,7 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
             </div>
 
             {/* 省/州 */}
-            <div className="mt-4">
+            <div className="mt-6">
               <label>State / Province</label>
               <input
                 type="text"
@@ -186,7 +161,7 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
             </div>
 
             {/* 城市 */}
-            <div className="mt-4">
+            <div className="mt-6">
               <label>City</label>
               <input
                 type="text"
@@ -195,20 +170,79 @@ export default function NewAddressDefault({ countries }: { countries: any[] }) {
                 className="w-full h-11 rounded-sm border border-[rgba(102,102,102,1)] indent-4 text-base mt-1"
               />
             </div>
-
+            {/* 手机号 */}
+            <CustomerAddressPhone />
+    
             {/* 默认地址 */}
-            <label className="flex items-center gap-2 cursor-pointer mt-4">
+            {/* <label className="flex items-center gap-2 cursor-pointer mt-6">
               <input
                 type="checkbox"
                 {...register("default_address", { valueAsNumber: true })}
                 className="w-5 h-5"
               />
               <span>Use as my default address</span>
+            </label> */}
+            <label className="flex items-center justify-between cursor-pointer mt-6 border rounded-[12px] px-5 py-4">
+  
+              <span className="text-base">
+                Use as my default address
+              </span>
+            
+            
+              <div className="relative">
+            
+                <input
+                  type="checkbox"
+                  {...register("default_address")}
+                  className="peer sr-only"
+                />
+            
+            
+                {/* switch 背景 */}
+                <div
+                  className="
+                    w-11
+                    h-6
+                    rounded-full
+                    bg-[#aaa]
+                    border
+                    border-[#D9D9D9]
+                    transition-all
+            
+                    peer-checked:bg-[#01754A]
+                    peer-checked:border-[#01754A]
+                  "
+                />
+            
+            
+                {/* 小圆点 */}
+                <div
+                  className="
+                    absolute
+                    top-[2px]
+                    left-[3px]
+            
+                    w-5
+                    h-5
+            
+                    bg-white
+                    rounded-full
+            
+                    shadow
+            
+                    transition-transform
+            
+                    peer-checked:translate-x-5
+                  "
+                />
+            
+              </div>
+            
             </label>
 
             {/* 提交按钮 */}
             <button
-              className="w-full h-11 leading-11 bg-[#1bbc9b] text-white text-center mt-6 rounded"
+              className="w-89.5 ml-auto h-12 leading-12 block  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-6"
               type="submit"
             >
               Save Address

+ 12 - 38
src/app/(public)/customer/(has-dashboard)/address/page.tsx

@@ -33,42 +33,19 @@ const CustomerAddressPage = async () => {
   };
 
   return (
-    <div className="w-full h-full">
-      <div className="bg-[#fff] pr-2.5 pl-2.5 w-full text-center text-base h-11 leading-11 font-semibold relative text-[#0b0b0b]">
-        <Link
-          href={"/customer/account"}
-          // onClick={() => window.history.back()}
-          className="absolute left-2.5 text-2xl inline-block top-1/2  -translate-y-1/2"
-        >
-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            width="24"
-            height="24"
-            viewBox="0 0 24 24"
-            fill="none"
-          >
-            <path
-              stroke="rgba(0, 0, 0, 1)"
-              stroke-width="1.2"
-              stroke-linejoin="round"
-              stroke-linecap="round"
-              d="M15 18L9 12L15 6"
-            ></path>
-          </svg>
-        </Link>
-        <span className="vipReturnTitles">SHIPPING ADDRESS</span>
-      </div>
-      <div className="max-w-md mx-auto p-4 bg-[#f8f8f8]">
+    <div className="w-full min-h-screen bg-white  h-full">
+      <h1 className="font-normal text-2xl leading-ly-28 mb-6">Address Book</h1>
+      <div className="rounded-md bg-white shadow-[0_2px_12px_rgba(0,0,0,0.1)] p-6">
         <DefaultAddress />
         {/* 账单地址模块 */}
-        <div className="mb-6">
+        {/* <div className="mb-6">
           <h2 className="text-ly-14 leading-ly-22 font-normal mb-2">
             Default Billing Address
           </h2>
 
-          {/* 外层卡片容器,浅背景圆角 */}
+       
           <div className="border border-gray-200 rounded-xl bg-gray-50 px-4 py-3 bg-[url('/image/account/202609011640.png')] bg-no-repeat bg-size-[100%_100%]">
-            {/* 姓名 + 手机号行 */}
+          
             <div className="flex items-center text-ly-13 leading-ly-21 font-normal ">
               <span>
                 {addressData.billing.first_name} {addressData.billing.last_name}
@@ -77,12 +54,10 @@ const CustomerAddressPage = async () => {
               <span>+1 {addressData.billing.phone}</span>
             </div>
 
-            {/* 分割线 */}
             <hr className="border-black my-3" />
 
-            {/* SVG定位图标 + 地址行 */}
             <div className="flex items-center gap-3 ">
-              {/* 位置图标 SVG */}
+          
 
               <svg
                 xmlns="http://www.w3.org/2000/svg"
@@ -118,15 +93,14 @@ const CustomerAddressPage = async () => {
             </div>
           </div>
 
-          {/* 复选框 + Change Address */}
-          <div className="flex items-center gap-3 mt-5">
+          <div className="flex items-center gap-3 mt-4.5">
             <input
               type="checkbox"
-              className="w-10 h-10 border-2 border-black rounded bg-white"
+              className="w-5 h-5 border-2 border-black rounded bg-white"
             />
             <span className="text-2xl text-gray-500">Change Address</span>
           </div>
-        </div>
+        </div> */}
 
         {/* 收货地址模块 */}
         {/* <div>
@@ -150,10 +124,10 @@ const CustomerAddressPage = async () => {
             </div>
           </div>
         </div> */}
-        <div className="w-89.5">
+        <div className="w-89.5 ml-auto">
           <Link
             href={"/customer/address/new"}
-            className="w-full h-12 leading-12 block  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-7.5"
+            className="w-full h-12 leading-12 block  rounded-[26px] bg-[rgba(3,97,65,1)] text-base text-white font-bold text-center mt-6"
           >
             Add New Adress
           </Link>

+ 15 - 15
src/app/(public)/customer/(has-dashboard)/order/history/_components/OrderHistory.tsx

@@ -82,7 +82,7 @@ const mockOrders = [
 
 // 标签列表
 const tabs = [
-  { key: "all", label: "All" },
+  { key: "", label: "All" },
   { key: "processing", label: "Processing" },
   { key: "complete", label: "Complete" },
   { key: "pending_payment", label: "Payment Review" }, // 补充模拟标签,测试滚动
@@ -101,7 +101,7 @@ export default function OrderHistory() {
   const sentinelRef = useRef<HTMLDivElement>(null);
   const listWrapRef = useRef<HTMLDivElement>(null);
   const router = useRouter();
-  const [activeTab, setActiveTab] = useState("all");
+  const [activeTab, setActiveTab] = useState("");
   const [filteredOrders, setFilteredOrders] = useState<any[]>([]);
   const lastHashRef = useRef<string>("");
   const fetchOrders = async (tabKey: string, isLoadMore = false) => {
@@ -135,7 +135,7 @@ export default function OrderHistory() {
     (hashKey: string) => {
       console.log("执行切换tab逻辑", hashKey);
       const validTab = tabs.find((t) => t.key === hashKey.toLowerCase());
-      const targetKey = validTab ? validTab.key : "all";
+      const targetKey = validTab ? validTab.key : "";
       // setActiveTab(targetKey);
       fetchOrders(targetKey, false);
     },
@@ -143,7 +143,7 @@ export default function OrderHistory() {
   );
   useEffect(() => {
     const getHash = () =>
-      window.location.hash.replace("#", "").toLowerCase() || "all";
+      window.location.hash.replace("#", "").toLowerCase() || "";
     const hash = getHash();
     if (lastHashRef.current !== hash) {
       lastHashRef.current = hash;
@@ -202,9 +202,9 @@ export default function OrderHistory() {
 
   return (
     <>
-      <div className="min-h-screen bg-gray-50">
+      <div className="min-h-screen bg-white ">
         {/* 顶部返回栏 + 标题 */}
-        <Link
+        {/* <Link
           href={"/customer/account"}
           className="sticky top-0 z-20 bg-white px-4 py-3 border-b flex justify-between items-center w-full"
         >
@@ -226,8 +226,8 @@ export default function OrderHistory() {
           </button>
           <h1 className="text-xl font-bold mx-auto">Orders</h1>
           <div className="w-6"></div>
-        </Link>
-
+        </Link> */}
+        <h1 className="font-normal text-2xl leading-ly-28 mb-6">My Orders</h1>
         {/* 标签栏(固定顶部 + 横向滚动) */}
         <div className="sticky top-14 z-10 bg-white px-4 py-2 border-b">
           <div className="flex items-center space-x-6 overflow-x-auto pb-1 scrollbar-hide">
@@ -249,7 +249,7 @@ export default function OrderHistory() {
         </div>
 
         {/* 订单列表 */}
-        <div className="px-4 py-3  h-[72vh] overflow-y-auto" ref={listWrapRef}>
+        <div className="rounded-md mt-4 bg-white shadow-[0_2px_12px_rgba(0,0,0,0.1)] p-6 h-[72vh] overflow-y-auto" ref={listWrapRef}>
           {filteredOrders.length === 0 && !loadMoreLoading ? (
             <div className="text-center py-8 text-gray-500">
               No orders found
@@ -261,10 +261,10 @@ export default function OrderHistory() {
                 className="bg-white rounded-lg p-4 mb-4 border"
               >
                 {/* 订单状态 + 编号 + 日期 */}
-                <div className="mb-3">
+                <div className="mb-3 border-b-1  border-[#ACBDB8] pb-3">
                   <p className="font-medium text-gray-800">• {order.status}</p>
                   <div className="flex justify-between text-sm text-gray-600 mt-1">
-                    <span>Order Number: {order.id}</span>
+                    <span>Order : {order.id}</span>
                     <span>Date: {order.date}</span>
                   </div>
                 </div>
@@ -289,9 +289,9 @@ export default function OrderHistory() {
                           {item.name}
                         </p>
                         {item.specs && (
-                          <p className="text-xs text-gray-500 mt-1">
+                          <span className="text-xs text-gray-500 mt-3 min-w-[460px] px-3 py-1.5 bg-[#F0FAF7]">
                             {item.specs}
-                          </p>
+                          </span>
                         )}
                         <p className="text-xs text-gray-600 mt-1">
                           {item.price}
@@ -302,7 +302,7 @@ export default function OrderHistory() {
                 </div>
 
                 {/* 订单进度条 */}
-                <Link
+                {/* <Link
                   href={`/customer/order/track/order_id/${order.id}`}
                   className="mb-4  bg-gray-100 rounded-full h-8 flex items-center px-2"
                 >
@@ -325,7 +325,7 @@ export default function OrderHistory() {
                       </svg>
                     </span>
                   </span>
-                </Link>
+                </Link> */}
 
                 {/* 折扣 + 总计 + 操作按钮 */}
                 <div className="text-right text-sm">

+ 1 - 1
src/app/(public)/customer/(has-dashboard)/order/track/order_id/[id]/page.tsx

@@ -49,7 +49,7 @@ export default async function TrackOrderDetail({ params }: { params: Params }) {
   // 解包拿到真实订单ID
   const realParams = await params;
   let orderId = realParams.id;
-  orderId = "9";
+  orderId = "881900125";
   console.log("orderId:", orderId);
 
   // 2. 服务端 fetch 请求我们自己的 Next.js API

+ 134 - 0
src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/OrderAddressCard.tsx

@@ -0,0 +1,134 @@
+import type {
+  AddressInfo,
+  OrderData,
+} from "@/types/api/customer/order/vieworder";
+
+interface Props {
+  orderData: OrderData;
+}
+
+const getFullAddress = (
+  address: AddressInfo
+) => {
+  return [
+    address.address1,
+    address.address2,
+    address.city,
+    address.state,
+    address.postcode,
+    address.country,
+  ]
+    .filter(Boolean)
+    .join(", ");
+};
+
+function AddressBlock({
+  address,
+}: {
+  address: AddressInfo;
+}) {
+  return (
+    <div
+      className="
+        mt-4
+        overflow-hidden
+        bg-[url('/image/account/202609011640.png')] bg-no-repeat bg-size-[100%_100%]
+      "
+    >
+      {/* Name + Phone */}
+      <div
+        className="
+          px-4
+          py-4
+          text-[14px]
+        "
+      >
+        {address.first_name}{" "}
+        {address.last_name}
+
+        <span className="mx-2 text-[#999]">
+          |
+        </span>
+
+        {address.phone}
+      </div>
+
+      {/* address */}
+      <div
+        className="
+          flex
+          items-start
+          gap-3
+          border-t
+          border-[#F7EBE6]
+          px-4
+          py-4
+          text-[14px]
+        "
+      >
+        <svg
+          width="20"
+          height="20"
+          viewBox="0 0 24 24"
+          fill="none"
+          className="mt-[1px] shrink-0"
+        >
+          <path
+            d="M12 21C12 21 19 15.5 19 9.5C19 5.36 15.87 2 12 2C8.13 2 5 5.36 5 9.5C5 15.5 12 21 12 21Z"
+            stroke="currentColor"
+            strokeWidth="1.5"
+          />
+
+          <circle
+            cx="12"
+            cy="9.5"
+            r="2.5"
+            stroke="currentColor"
+            strokeWidth="1.5"
+          />
+        </svg>
+
+        <span>
+          {getFullAddress(address)}
+        </span>
+      </div>
+    </div>
+  );
+}
+
+export default function OrderAddressCard({
+  orderData,
+}: Props) {
+  return (
+    <section
+      className="
+        mt-6
+        rounded-[6px]
+        bg-white
+        px-5
+        py-6
+        shadow-[0_2px_12px_rgba(0,0,0,0.06)]
+      "
+    >
+      <div>
+        <h2 className="text-[17px]">
+          Ship to:
+        </h2>
+
+        <AddressBlock
+          address={orderData.shipping_address}
+        />
+      </div>
+
+      <div className="mt-6">
+        <h2 className="text-[17px]">
+          Billing to:
+        </h2>
+
+        <AddressBlock
+          address={orderData.billing_address}
+        />
+      </div>
+    </section>
+  );
+}

+ 3 - 23
src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/OrderHeader.tsx

@@ -1,30 +1,10 @@
 "use client";
 
-import { useRouter } from "next/navigation";
+// import { useRouter } from "next/navigation";
 
 export default function OrderHeader({ title }: { title: string }) {
-  const router = useRouter();
+  // const router = useRouter();
   return (
-    <div className="sticky top-0 z-50 bg-white px-5 py-4 border-b">
-      <div className="flex items-center w-full">
-        <button onClick={() => router.back()} className="text-xl">
-          <svg
-            className="w-6 h-6"
-            viewBox="0 0 1024 1024"
-            version="1.1"
-            xmlns="http://www.w3.org/2000/svg"
-            p-id="111033"
-            width="200"
-            height="200"
-          >
-            <path
-              d="M391.466667 512l339.882666-339.882667a46.592 46.592 0 1 0-65.877333-65.877333L292.693333 479.061333a46.592 46.592 0 0 0 0 65.877334l372.821334 372.821333a46.592 46.592 0 1 0 65.877333-65.877333L391.466667 512z"
-              p-id="111034"
-            ></path>
-          </svg>
-        </button>
-        <h1 className="flex-grow text-center text-lg font-bold">{title}</h1>
-      </div>
-    </div>
+    <h1 className="font-normal text-2xl leading-ly-28 mb-6">{title}</h1>
   );
 }

+ 65 - 33
src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/OrderInfoBlock.tsx

@@ -1,51 +1,83 @@
-// OrderInfoBlock.tsx
 import type { OrderData } from "@/types/api/customer/order/vieworder";
 
 interface Props {
-  orderData: OrderData; // 直接传入完整订单对象
+  orderData: OrderData;
 }
 
-export default function OrderInfoBlock({ orderData }: Props) {
-  console.log("orderData:::",orderData);
-  
-  // 拼接收货信息
-  const shipToText = `${orderData.shipping_address.first_name} ${orderData.shipping_address.last_name}, 
-Phone: ${orderData.shipping_address.phone}, 
-${orderData.shipping_address.city}, ${orderData.shipping_address.state}, ${orderData.shipping_address.country} ${orderData.shipping_address.postcode}`;
+export default function PromotionDetails({ orderData }: Props) {
+  const money = (value: number) =>
+    new Intl.NumberFormat("en-US", {
+      style: "currency",
+      currency: orderData.order_currency_code,
+    }).format(value);
+
+  const discount = Math.abs(orderData.discount_amount);
 
   return (
-    <>
-      <h2 className="text-xl font-bold mb-6 border-b pb-3">Order Information</h2>
+    <section
+      className="
+        mt-6
+        rounded-[6px]
+        bg-white
+        px-5
+        py-6
+        shadow-[0_2px_12px_rgba(0,0,0,0.06)]
+      "
+    >
+      <h2 className="text-[18px]">Promotion Details</h2>
+
+      <div className="mt-6 border-t border-[#EEEEEE] pt-4">
+        {/* Subtotal */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Subtotal</span>
 
-      {/* 两列网格布局 */}
-      <div className="grid grid-cols-[auto_1fr] gap-x-4 gap-y-5">
-        <div className="font-medium text-gray-700">Ship To:</div>
-        <div>{shipToText}</div>
+          <span>{money(orderData.sub_total)}</span>
+        </div>
 
-        <div className="font-medium text-gray-700">Order Number:</div>
-        <div>{orderData.increment_id}</div>
+        {/* Discount */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Discount</span>
 
-        <div className="font-medium text-gray-700">Status:</div>
-        <div>{orderData.status}</div>
+          <span className={discount > 0 ? "bg-[#FFD600] px-2 py-1" : ""}>
+            {discount > 0 ? `-${money(discount)}` : money(0)}
+          </span>
+        </div>
 
-        <div className="font-medium text-gray-700">Order Date:</div>
-        <div>{orderData.created_at}</div>
+        {/* Shipping */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Shipping</span>
 
-        <div className="font-medium text-gray-700">Customer Email:</div>
-        <div>{orderData.customer_email}</div>
+          <span>
+            {orderData.shipping_amount === 0
+              ? "Free"
+              : money(orderData.shipping_amount)}
+          </span>
+        </div>
 
-        <div className="font-medium text-gray-700">Shipping Method:</div>
-        <div>{orderData.shipping_title}</div>
+        {/* Tax */}
+        <div className="flex justify-between py-3 text-[14px]">
+          <span>Tax</span>
 
-        <div className="font-medium text-gray-700">Payment Method:</div>
-        <div className="bg-pink-300 px-3 py-1 rounded w-fit">
-          {orderData.payment.method_title}
+          <span>{money(orderData.tax_amount)}</span>
         </div>
 
-        {/* 可选:显示总价信息 */}
-        <div className="font-medium text-gray-700">Total Amount:</div>
-        <div>{orderData.order_currency_code} {orderData.grand_total}</div>
+        {/* Total */}
+        <div
+          className="
+            mt-2
+            flex
+            justify-between
+            border-t
+            border-[#EEEEEE]
+            pt-5
+            text-[15px]
+          "
+        >
+          <span>Total:</span>
+
+          <span className="font-medium">{money(orderData.grand_total)}</span>
+        </div>
       </div>
-    </>
+    </section>
   );
-}
+}

+ 149 - 19
src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/_components/ProductList.tsx

@@ -1,29 +1,159 @@
-// ProductList.tsx
 import type { OrderData } from "@/types/api/customer/order/vieworder";
 
 interface Props {
-  orderItems: OrderData["items"];
-  currencyCode: string;
+  orderData: OrderData;
 }
 
-export default function ProductList({ orderItems, currencyCode }: Props) {
+const formatDate = (date: string) => {
+  if (!date) return "";
+
+  const datePart = date.split(" ")[0];
+  const [year, month, day] = datePart.split("-");
+
+  return `${Number(month)}/${Number(day)}/${year}`;
+};
+
+const formatMoney = (
+  value: number,
+  currency: string
+) => {
+  return new Intl.NumberFormat("en-US", {
+    style: "currency",
+    currency,
+  }).format(value);
+};
+
+const formatStatus = (status: string) => {
+  return status
+    .replace(/_/g, " ")
+    .replace(/\b\w/g, (char) => char.toUpperCase());
+};
+
+export default function ProductList({
+  orderData,
+}: Props) {
   return (
-    <div className="mb-6 space-y-4">
-      {orderItems.map((item, index) => (
-        <div className="flex items-center gap-3" key={item.id ?? index}>
-          {/* 暂无商品原图:用占位框 */}
-          <div className="w-24 h-24 bg-gray-100 rounded shrink-0"></div>
-
-          <div className="flex-1">
-            <p className="font-medium">{item.name}</p>
-            <p className="text-sm text-gray-600 my-1">SKU: {item.sku ?? "-"}</p>
-            <div className="flex justify-between">
-              <span>{currencyCode} {item.price}</span>
-              <span>× {item.qty_ordered}</span>
+    <section
+      className="
+        rounded-[6px]
+        bg-white
+        px-6
+        py-6
+        shadow-[0_2px_12px_rgba(0,0,0,0.06)]
+      "
+    >
+      {/* status */}
+      <div className="flex items-center gap-2 text-[16px]">
+        <span className="h-2 w-2 rounded-full bg-black" />
+
+        <span>
+          {formatStatus(orderData.status)}
+        </span>
+      </div>
+
+      {/* Order / Date */}
+      <div
+        className="
+          mt-4
+          flex
+          items-center
+          justify-between
+          border-b
+          border-[#F1E5DF]
+          pb-4
+          text-[14px]
+          text-[#8A8A8A]
+        "
+      >
+        <span>
+          Order #{orderData.increment_id}
+        </span>
+
+        <span>
+          Date: {formatDate(orderData.created_at)}
+        </span>
+      </div>
+
+      {/* Products */}
+      <div>
+        {orderData.items.map((item, index) => (
+          <div
+            key={item.id}
+            className={`
+              flex
+              gap-4
+              py-4
+              ${
+                index !== orderData.items.length - 1
+                  ? "border-b border-[#EEEEEE]"
+                  : ""
+              }
+            `}
+          >
+            {/* 商品图:
+                当前接口没有 image,所以做兼容
+            */}
+            <div
+              className="
+                h-[174px]
+                w-[130px]
+                shrink-0
+                overflow-hidden
+                bg-[#F5F5F5]
+              "
+            >
+              {item.image ? (
+                <img
+                  src={item.image}
+                  alt={item.name}
+                  className="h-full w-full object-cover"
+                />
+              ) : null}
+            </div>
+
+            <div className="flex min-w-0 flex-1 flex-col">
+              <h3
+                className="
+                  text-[14px]
+                  leading-[20px]
+                  text-black
+                "
+              >
+                {item.name}
+              </h3>
+
+              {/* 当前 API 没有 options,有了以后直接显示 */}
+              {item.options && (
+                <div
+                  className="
+                    mt-3
+                    bg-[#EFF9F6]
+                    px-3
+                    py-2
+                    text-[13px]
+                    text-[#333]
+                  "
+                >
+                  {item.options}
+                </div>
+              )}
+
+              <div className="mt-auto text-[15px]">
+                <span>
+                  {formatMoney(
+                    item.price,
+                    orderData.order_currency_code
+                  )}
+                </span>
+
+                <span className="ml-1">
+                  x {item.qty_ordered}
+                </span>
+              </div>
             </div>
           </div>
-        </div>
-      ))}
-    </div>
+        ))}
+      </div>
+    </section>
   );
 }

+ 14 - 12
src/app/(public)/customer/(has-dashboard)/order/view/order_id/[id]/page.tsx

@@ -1,7 +1,8 @@
 import OrderHeader from "./_components/OrderHeader";
 import OrderInfoBlock from "./_components/OrderInfoBlock";
 import ProductList from "./_components/ProductList";
-import PriceSummary from "./_components/PriceSummary";
+import OrderAddressCard from "./_components/OrderAddressCard";
+// import PriceSummary from "./_components/PriceSummary";
 import {restApiFetch} from "@utils/bagisto/index";
 import type {  OrderData,OrderApiResponse } from "@/types/api/customer/order/vieworder";
 type Params = {
@@ -11,7 +12,7 @@ export default async function OrderDetail({ params }: { params: Params }) {
   // 解包拿到真实订单ID
   const realParams = await params;
   let orderId = realParams.id;
-  orderId = '9';
+  orderId = '881900125';
   console.log("orderId:", orderId);
 
   // 2. 服务端 fetch 请求我们自己的 Next.js API
@@ -25,14 +26,19 @@ export default async function OrderDetail({ params }: { params: Params }) {
   const orderData: OrderData = res.body.data;
   console.log("父组件 orderData:", orderData, !!orderData); 
   return (
-    <div className="bg-gray-50 min-h-screen">
+    <div className="min-h-screen bg-white">
       {/* 顶部固定导航栏(客户端组件:返回按钮) */}
-      <OrderHeader title="Order Information" />
+      <OrderHeader title="Orders Information" />
 
-      <div className="px-5 py-4">
+      {/* 商品列表:循环渲染,自动兼容1件或多件商品 */}
+          <ProductList
+        orderData={orderData}
+      />
+
+      <div className="">
         {/* 收货信息、支付信息区块 */}
         <OrderInfoBlock orderData={orderData} />
-
+         <OrderAddressCard orderData={orderData} />
         {/* 分享按钮区域 */}
         <div className="my-6">
           <h3 className="text-lg font-bold mb-4">Share order:</h3>
@@ -44,14 +50,10 @@ export default async function OrderDetail({ params }: { params: Params }) {
           </div>
         </div>
 
-        {/* 商品列表:循环渲染,自动兼容1件或多件商品 */}
-            <ProductList
-          orderItems={orderData.items}
-          currencyCode={orderData.order_currency_code}
-        />
+      
 
         {/* 金额汇总 */}
-        <PriceSummary orderData={orderData} />
+        {/* <PriceSummary orderData={orderData} /> */}
       </div>
     </div>
   );

+ 334 - 24
src/app/(public)/customer/_components/CustomerSidebar.tsx

@@ -1,9 +1,28 @@
 "use client";
 
 import Link from "next/link";
+import { useEffect, useState } from "react";
 import { usePathname } from "next/navigation";
+import { logoutAction } from "@/actions";
+import { signOut } from "next-auth/react";
+import { useRouter } from "next/navigation";
+import { useAppDispatch } from "@/store/hooks";
+import { clearCart } from "@/store/slices/cart-slice";
+import { useCustomToast } from '@/utils/hooks/useToast';
+import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
+interface MenuChild {
+  name: string;
+  path: string;
+}
+
+interface MenuItem {
+  name: string;
+  path?: string;
+  children?: MenuChild[];
+  activePrefix?: string;
+}
 
-const menus = [
+const menus: MenuItem[] = [
   {
     name: "My Account",
     path: "/customer/account",
@@ -14,24 +33,59 @@ const menus = [
   },
   {
     name: "My Orders",
-    path: "/customer/order",
+    path: "/customer/order/history#all",
+    activePrefix: "/customer/order",
   },
   {
     name: "My Profile",
-    path: "/customer/profile",
+    path: "/customer/account/edit",
   },
+
+  // VIP 二级菜单
   {
     name: "VIP Exclusive",
+    children: [
+      {
+        name: "Level-Up Coupon",
+        path: "/customer/account/level-up-coupon",
+      },
+      {
+        name: "Birthday Reward",
+        path: "/customer/account/birthday",
+      },
+    ],
   },
+
   {
     name: "My Product Reviews",
+    path: "/customer/account/reviews",
   },
+
   {
     name: "Service",
+    children: [
+      {
+        name: "Track Order",
+        path: "/track_order",
+      },
+      {
+        name: "Customization Service",
+        path: "/customer/customization-service",
+      },
+    ],
   },
+
+  // 你原来的 My Points 继续保留
+  {
+    name: "My Points",
+    path: "/customer/account/mypoints",
+  },
+
   {
     name: "Wishlist",
+    path: "/customer/account/wishlist",
   },
+
   {
     name: "Sign Out",
   },
@@ -39,46 +93,302 @@ const menus = [
 
 export default function CustomerSidebar() {
   const pathname = usePathname();
+  const [openMenus, setOpenMenus] = useState<Record<string, boolean>>({});
+
+  const getPathname = (path: string) => {
+    return path.split("#")[0].split("?")[0];
+  };
+
+  const isItemActive = (item: MenuItem | MenuChild) => {
+    if ("activePrefix" in item && item.activePrefix) {
+      return (
+        pathname === item.activePrefix ||
+        pathname.startsWith(`${item.activePrefix}/`)
+      );
+    }
+
+    if (!item.path) {
+      return false;
+    }
+
+    return pathname === getPathname(item.path);
+  };
 
+  /**
+   * 判断父级下面有没有 active 子菜单
+   */
+  const hasActiveChild = (item: MenuItem) => {
+    return item.children?.some((child) => isItemActive(child)) ?? false;
+  };
+
+  /**
+   * 进入二级页面时,自动展开对应父菜单
+   */
+  useEffect(() => {
+    setOpenMenus((prev) => {
+      const next = { ...prev };
+
+      menus.forEach((item) => {
+        if (item.children && hasActiveChild(item)) {
+          next[item.name] = true;
+        }
+      });
+
+      return next;
+    });
+  }, [pathname]);
+
+  const toggleMenu = (name: string) => {
+    setOpenMenus((prev) => ({
+      ...prev,
+      [name]: !prev[name],
+    }));
+  };
+  const router = useRouter();
+  const { showToast } = useCustomToast();
+  const dispatch = useAppDispatch();
+   const handleSignOut = async () => {
+      try {
+        overlayLoading.start();
+        const res = await logoutAction();
+  
+        if (!res.success) {
+          showToast(res.message, "danger");
+          return;
+        }
+  
+        await signOut({
+          callbackUrl: "/customer/login",
+          redirect: false,
+        });
+  
+        dispatch(clearCart());
+        showToast("You are logged out successfully!", "success");
+        overlayLoading.stop();
+        setTimeout(() => {
+          router.push("/customer/login");
+          router.refresh();
+        }, 100);
+      } catch (err: unknown) {
+        overlayLoading.stop();
+        const message = err instanceof Error ? err.message : "Logout failed";
+        showToast(message, "danger");
+      }
+    };
   return (
     <div
       className="
- bg-white
- shadow-[0_2px_12px_rgba(0,0,0,.08)]
- py-3
- "
+        w-full
+        bg-white
+        py-3
+        shadow-[0_2px_12px_rgba(0,0,0,.08)]
+      "
     >
       {menus.map((item) => {
-        const active = pathname === item.path;
+        /**
+         * =========================
+         * 有子菜单
+         * =========================
+         */
+        if (item.children?.length) {
+          const open = !!openMenus[item.name];
+          const childActive = hasActiveChild(item);
+
+          return (
+            <div key={item.name}>
+              {/* 父菜单 */}
+              <button
+                type="button"
+                onClick={() => toggleMenu(item.name)}
+                className={`
+                  flex
+                  h-[55px]
+                  w-full
+                  items-center
+                  justify-between
+                  px-8
+                  text-left
+                  text-[16px]
+                  transition-colors
+
+                  ${
+                    childActive
+                      ? "font-semibold text-[#036141]"
+                      : "font-normal text-[#111]"
+                  }
+                `}
+              >
+                <span>{item.name}</span>
+
+                {/* Arrow */}
+                <svg
+                  xmlns="http://www.w3.org/2000/svg"
+                  width="18"
+                  height="18"
+                  viewBox="0 0 24 24"
+                  fill="none"
+                  className={`
+                    transition-transform
+                    duration-200
+                    ${open ? "rotate-180" : ""}
+                  `}
+                >
+                  <path
+                    d="M6 9L12 15L18 9"
+                    stroke="currentColor"
+                    strokeWidth="1.5"
+                    strokeLinecap="round"
+                    strokeLinejoin="round"
+                  />
+                </svg>
+              </button>
+
+              {/* 二级菜单 */}
+              <div
+                className={`
+                  grid
+                  transition-[grid-template-rows]
+                  duration-200
+                  ease-in-out
+
+                  ${open ? "grid-rows-[1fr]" : "grid-rows-[0fr]"}
+                `}
+              >
+                <div className="overflow-hidden">
+                  {item.children.map((child) => {
+                    const active = isItemActive(child);
+
+                    return (
+                      <Link
+                        key={child.name}
+                        href={child.path}
+                        className={`
+                          relative
+                          flex
+                          min-h-[64px]
+                          items-center
+                          pl-[50px]
+                          pr-8
+                          text-[16px]
+                          transition-colors
+
+                          ${
+                            active
+                              ? `
+                                bg-[#EEFAF6]
+                                font-semibold
+                                text-[#036141]
+                              `
+                              : `
+                                bg-white
+                                font-normal
+                                text-[#111]
+                                hover:bg-[#fafafa]
+                              `
+                          }
+                        `}
+                      >
+                        {child.name}
+
+                        {/* active 右侧绿色竖线 */}
+                        {active && (
+                          <span
+                            className="
+                              absolute
+                              right-0
+                              top-0
+                              h-full
+                              w-[3px]
+                              bg-[#036141]
+                            "
+                          />
+                        )}
+                      </Link>
+                    );
+                  })}
+                </div>
+              </div>
+            </div>
+          );
+        }
+
+        /**
+         * =========================
+         * Sign Out
+         * =========================
+         */
+        if (item.name === "Sign Out") {
+          return (
+            <button
+              key={item.name}
+              type="button"
+              onClick={handleSignOut}
+              className="
+                flex
+                h-[55px]
+                w-full
+                items-center
+                px-8
+                text-left
+                text-[16px]
+                font-normal
+                text-[#111]
+                transition-colors
+                hover:bg-[#fafafa]
+              "
+            >
+              {item.name}
+            </button>
+          );
+        }
+
+        /**
+         * =========================
+         * 普通一级菜单
+         * =========================
+         */
+        const active = isItemActive(item);
 
         return (
           <Link
             key={item.name}
             href={item.path || "#"}
             className={`
- relative
- flex
- h-[55px]
- items-center
- px-8
- text-[16px]
-
- ${active ? "bg-[#EEFAF6] text-[#036141] font-semibold" : "text-[#111]"}
+              relative
+              flex
+              h-[55px]
+              items-center
+              px-8
+              text-[16px]
+              transition-colors
 
- `}
+              ${
+                active
+                  ? `
+                    bg-[#EEFAF6]
+                    font-semibold
+                    text-[#036141]
+                  `
+                  : `
+                    font-normal
+                    text-[#111]
+                    hover:bg-[#fafafa]
+                  `
+              }
+            `}
           >
             {item.name}
 
             {active && (
               <span
                 className="
- absolute
- right-0
- top-0
- h-full
- w-[3px]
- bg-[#036141]
- "
+                  absolute
+                  right-0
+                  top-0
+                  h-full
+                  w-[3px]
+                  bg-[#036141]
+                "
               />
             )}
           </Link>

+ 340 - 332
src/components/customer/accountSwiper.tsx

@@ -1,7 +1,9 @@
 "use client";
-// import { Swiper, SwiperSlide } from 'swiper/react';
+import { Swiper, SwiperSlide } from "swiper/react";
+import { useRef, useState } from "react";
+import type { Swiper as SwiperType } from "swiper";
 import { OpenAddToCartModalButton } from "@/components/common/AddToCartModal/OpenAddToCartModalButton";
-import Image from 'next/image';
+import Image from "next/image";
 interface ProductItem {
   id: number;
   sku: string;
@@ -20,341 +22,347 @@ interface ProductItem {
 }
 
 // 模拟商品数据(和图片内容匹配)
-const productList:ProductItem[] = [
-                    {
-                        "id": 3792,
-                        "sku": "BW--Closure",
-                        "name": "100% Brazilian Virgin Hair Body Wave HD Transparent Lace Closure ",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 94.71,
-                        "min_price": 94.71,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/b/o/body_13__1_1.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Asteria Hair 100% Brazilian Virgin Hair Body Wave Lace Closure, Free Part 4x4 Closure Can be Parted anywhere you like, transparent lace color and HD invisible lace to choose from, both color body wave hair lace closure fits all skin tone perfectly.",
-                        "rating": 5,
-                        "review_count": 10
-                    },
-                    {
-                        "id": 3843,
-                        "sku": "#613--STW--3+C--10A613",
-                        "name": "100% Human Hair 613 Blonde Brazilian Straight Hair 3 Bundles With Closure In Stock",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 168.18,
-                        "min_price": 168.18,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/6/1/613_3_.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Asteria Hair 613 Blonde Color Straight Human Hair Weave 3 Bundles With 4x4 Lace Closure Transparent Lace Color",
-                        "rating": 0,
-                        "review_count": 0
-                    },
-                    {
-                        "id": 3850,
-                        "sku": "#613--BW--4--10A613",
-                        "name": "100% Human Hair Body Wave 4 Bundles 613 Blonde Hair",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 156.73,
-                        "min_price": 156.73,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/b/l/blonde_hair.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "100% Brazilian Virgin Human Hair Weaves Body Wave 4 Bundles 613 Blonde Hair For Sale",
-                        "rating": 0,
-                        "review_count": 0
-                    },
-                    {
-                        "id": 3790,
-                        "sku": "BW--3",
-                        "name": "100% Human Hair Natural Black Brazilian Body Wave Hair 8-30 Inch",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 183.29,
-                        "min_price": 183.29,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/b/o/body_wave_human_hair_bundles.png",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Unprocessed Brazilian Body Wave 3 Bundles Virgin Hair Body Wave Natural Black Hot Sale Asteria Hair Product",
-                        "rating": 0,
-                        "review_count": 0
-                    },
-                    {
-                        "id": 3868,
-                        "sku": "P--LD--3",
-                        "name": "100% Unprocessed Virgin Peruvian Human Hair Weave Loose Deep 3 PCS In Stock",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 193.24,
-                        "min_price": 193.24,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/3/_/3__1_9.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Asteria Hair 100% Unprocessed Peruvian Virgin Human Hair Weave 3 Bundles Deal Loose Deep Wave Weaves 3 PCs Hair Bundles",
-                        "rating": 5,
-                        "review_count": 17
-                    },
-                    {
-                        "id": 3835,
-                        "sku": "P--BW--4",
-                        "name": "100% Virgin Hair Natural Black Peruvian Body Wave Hair 8-30 Inch For Selling",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 222.27,
-                        "min_price": 222.27,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/x/y/xys_3917_2.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Unprocessed Peruvian Body Wave 4 Bundles Virgin Hair Body Wave Natural Black Top Selling Asteria Hair Bundles",
-                        "rating": 5,
-                        "review_count": 10
-                    },
-                    {
-                        "id": 3786,
-                        "sku": "test11111",
-                        "name": "12312312",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 111,
-                        "min_price": 111,
-                        "special_price": null,
-                        "image_url": null,
-                        "is_new": true,
-                        "is_featured": true,
-                        "short_description": "<p>3324124124</p>",
-                        "rating": 0,
-                        "review_count": 0
-                    },
-                    {
-                        "id": 3807,
-                        "sku": "DW--Frontal",
-                        "name": "13*4 Ear to Ear Transparent HD Lace Frontal Human Brazilian Hair Deep Wave Hair",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 121.75,
-                        "min_price": 121.75,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/0/2/02_4_3.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Asteria 13*4 Lace Frontal Human Hair Brazilian Hair Sale Deep Wave Hair Cheap Brazilian Hair Ear To Ear Lace Frontal Human Hair",
-                        "rating": 5,
-                        "review_count": 3
-                    },
-                    {
-                        "id": 3860,
-                        "sku": "#T1 B613--BW--3+F--10A",
-                        "name": "1B 613 Ombre Human Virgin Hair Body Wave Hair 3 Bundles with 13*4 Lace Frontal",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 199.24,
-                        "min_price": 199.24,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalog/product/1/b/1b-613-blonde-bundles-with-frontal.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "Asteria Hair #1B\/613 Ombre Blonde Hair Color 100% Human Body Wave Hair 3 Bundles with Ear to Ear Lace Frontal Deal",
-                        "rating": 0,
-                        "review_count": 0
-                    },
-                    {
-                        "id": 3800,
-                        "sku": "STW--3+F",
-                        "name": "1pc Ear To Ear Lace Frontal with 3 Bundles Straight Virgin Brazilian Hair Weft",
-                        "slug": null,
-                        "type": "flexible_variant",
-                        "price": 281.86,
-                        "min_price": 281.86,
-                        "special_price": null,
-                        "image_url": "https:www.asteriahair.com/media/catalo/product/3/_/3_11_1.jpg",
-                        "is_new": true,
-                        "is_featured": false,
-                        "short_description": "High Quality Asteria Products 1pc Ear To Ear Lace Frontal Closure Totally Hand Made Pre-Plucked Hairline With Baby Hair 3 Bundles Human Straight Virgin Brazilian Hair Weft Natural Black Tangle Free No Splits",
-                        "rating": 0,
-                        "review_count": 0
-                    },
-                ]
+const productList: ProductItem[] = [
+  {
+    id: 3792,
+    sku: "BW--Closure",
+    name: "100% Brazilian Virgin Hair Body Wave HD Transparent Lace Closure ",
+    slug: null,
+    type: "flexible_variant",
+    price: 94.71,
+    min_price: 94.71,
+    special_price: null,
+    image_url:
+      "https:www.asteriahair.com/media/catalog/product/b/o/body_13__1_1.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Asteria Hair 100% Brazilian Virgin Hair Body Wave Lace Closure, Free Part 4x4 Closure Can be Parted anywhere you like, transparent lace color and HD invisible lace to choose from, both color body wave hair lace closure fits all skin tone perfectly.",
+    rating: 5,
+    review_count: 10,
+  },
+  {
+    id: 3843,
+    sku: "#613--STW--3+C--10A613",
+    name: "100% Human Hair 613 Blonde Brazilian Straight Hair 3 Bundles With Closure In Stock",
+    slug: null,
+    type: "flexible_variant",
+    price: 168.18,
+    min_price: 168.18,
+    special_price: null,
+    image_url: "https:www.asteriahair.com/media/catalog/product/6/1/613_3_.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Asteria Hair 613 Blonde Color Straight Human Hair Weave 3 Bundles With 4x4 Lace Closure Transparent Lace Color",
+    rating: 0,
+    review_count: 0,
+  },
+  {
+    id: 3850,
+    sku: "#613--BW--4--10A613",
+    name: "100% Human Hair Body Wave 4 Bundles 613 Blonde Hair",
+    slug: null,
+    type: "flexible_variant",
+    price: 156.73,
+    min_price: 156.73,
+    special_price: null,
+    image_url:
+      "https:www.asteriahair.com/media/catalog/product/b/l/blonde_hair.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "100% Brazilian Virgin Human Hair Weaves Body Wave 4 Bundles 613 Blonde Hair For Sale",
+    rating: 0,
+    review_count: 0,
+  },
+  {
+    id: 3790,
+    sku: "BW--3",
+    name: "100% Human Hair Natural Black Brazilian Body Wave Hair 8-30 Inch",
+    slug: null,
+    type: "flexible_variant",
+    price: 183.29,
+    min_price: 183.29,
+    special_price: null,
+    image_url:
+      "https:www.asteriahair.com/media/catalog/product/b/o/body_wave_human_hair_bundles.png",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Unprocessed Brazilian Body Wave 3 Bundles Virgin Hair Body Wave Natural Black Hot Sale Asteria Hair Product",
+    rating: 0,
+    review_count: 0,
+  },
+  {
+    id: 3868,
+    sku: "P--LD--3",
+    name: "100% Unprocessed Virgin Peruvian Human Hair Weave Loose Deep 3 PCS In Stock",
+    slug: null,
+    type: "flexible_variant",
+    price: 193.24,
+    min_price: 193.24,
+    special_price: null,
+    image_url: "https:www.asteriahair.com/media/catalog/product/3/_/3__1_9.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Asteria Hair 100% Unprocessed Peruvian Virgin Human Hair Weave 3 Bundles Deal Loose Deep Wave Weaves 3 PCs Hair Bundles",
+    rating: 5,
+    review_count: 17,
+  },
+  {
+    id: 3835,
+    sku: "P--BW--4",
+    name: "100% Virgin Hair Natural Black Peruvian Body Wave Hair 8-30 Inch For Selling",
+    slug: null,
+    type: "flexible_variant",
+    price: 222.27,
+    min_price: 222.27,
+    special_price: null,
+    image_url:
+      "https:www.asteriahair.com/media/catalog/product/x/y/xys_3917_2.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Unprocessed Peruvian Body Wave 4 Bundles Virgin Hair Body Wave Natural Black Top Selling Asteria Hair Bundles",
+    rating: 5,
+    review_count: 10,
+  },
+  {
+    id: 3786,
+    sku: "test11111",
+    name: "12312312",
+    slug: null,
+    type: "flexible_variant",
+    price: 111,
+    min_price: 111,
+    special_price: null,
+    image_url: null,
+    is_new: true,
+    is_featured: true,
+    short_description: "<p>3324124124</p>",
+    rating: 0,
+    review_count: 0,
+  },
+  {
+    id: 3807,
+    sku: "DW--Frontal",
+    name: "13*4 Ear to Ear Transparent HD Lace Frontal Human Brazilian Hair Deep Wave Hair",
+    slug: null,
+    type: "flexible_variant",
+    price: 121.75,
+    min_price: 121.75,
+    special_price: null,
+    image_url: "https:www.asteriahair.com/media/catalog/product/0/2/02_4_3.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Asteria 13*4 Lace Frontal Human Hair Brazilian Hair Sale Deep Wave Hair Cheap Brazilian Hair Ear To Ear Lace Frontal Human Hair",
+    rating: 5,
+    review_count: 3,
+  },
+  {
+    id: 3860,
+    sku: "#T1 B613--BW--3+F--10A",
+    name: "1B 613 Ombre Human Virgin Hair Body Wave Hair 3 Bundles with 13*4 Lace Frontal",
+    slug: null,
+    type: "flexible_variant",
+    price: 199.24,
+    min_price: 199.24,
+    special_price: null,
+    image_url:
+      "https:www.asteriahair.com/media/catalog/product/1/b/1b-613-blonde-bundles-with-frontal.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "Asteria Hair #1B\/613 Ombre Blonde Hair Color 100% Human Body Wave Hair 3 Bundles with Ear to Ear Lace Frontal Deal",
+    rating: 0,
+    review_count: 0,
+  },
+  {
+    id: 3800,
+    sku: "STW--3+F",
+    name: "1pc Ear To Ear Lace Frontal with 3 Bundles Straight Virgin Brazilian Hair Weft",
+    slug: null,
+    type: "flexible_variant",
+    price: 281.86,
+    min_price: 281.86,
+    special_price: null,
+    image_url: "https:www.asteriahair.com/media/catalo/product/3/_/3_11_1.jpg",
+    is_new: true,
+    is_featured: false,
+    short_description:
+      "High Quality Asteria Products 1pc Ear To Ear Lace Frontal Closure Totally Hand Made Pre-Plucked Hairline With Baby Hair 3 Bundles Human Straight Virgin Brazilian Hair Weft Natural Black Tangle Free No Splits",
+    rating: 0,
+    review_count: 0,
+  },
+];
 // 五星星星渲染
-  const renderStars = (count: number) => {
-    return Array.from({ length: count }).map((_, idx) => (
-      <svg
-        key={idx}
-        className="w-4 h-4 text-green-600 inline-block"
-        viewBox="0 0 24 24"
-        fill="currentColor"
-      >
-        <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
-      </svg>
-    ));
-  };
+const renderStars = (count: number) => {
+  return Array.from({ length: count }).map((_, idx) => (
+    <svg
+      key={idx}
+      className="w-4 h-4 text-green-600 inline-block"
+      viewBox="0 0 24 24"
+      fill="currentColor"
+    >
+      <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
+    </svg>
+  ));
+};
 const AccountSwiper = () => {
+  const swiperRef = useRef<SwiperType | null>(null);
+  const [progress, setProgress] = useState(0);
+  const updateProgress = (swiper: SwiperType) => {
+    const min = 20; // 初始占比
+
+    const max = 100;
+
+    const value = min + swiper.progress * (max - min);
+
+    setProgress(value);
+  };
   return (
-    <div className="w-full max-w-[700px] mx-auto px-4">
-       {/* 商品2列网格 */}
-            <div className="grid grid-cols-2 gap-4">
-              {productList.map((item) => (
-                <div key={item.id} className="flex flex-col gap-3">
-                  {/* Next.Image 商品图容器 */}
-                  <div className="relative w-full aspect-[3/4] overflow-hidden rounded-md">
-                    <Image
-                      src={item.image_url?item.image_url:''}
-                      alt={item.name}
-                      fill
-                      sizes="(max-width:768px) 50vw, 33vw"
-                      className="object-cover"
-                    />
-                  </div>
-      
-                  {/* 标题 */}
-                  <p className="text-sm text-gray-800 truncate">{item.name}</p>
-      
-                  {/* 星级 + 评论数 */}
-                  <div className="flex items-center gap-1">
-                    {renderStars(item.rating)}
-                    <span className="text-sm text-gray-600 ml-1 underline">
-                      {item.review_count}
-                    </span>
-                  </div>
-                  {/* 价格 + 购物车图标 */}
-                  <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"
-                            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 className="w-full  mx-auto">
+      <Swiper
+        slidesPerView={4.5}
+        spaceBetween={24}
+        freeMode={true}
+        watchOverflow={true}
+        onSwiper={(swiper) => {
+          swiperRef.current = swiper;
+          updateProgress(swiper);
+        }}
+        onProgress={(swiper) => {
+          updateProgress(swiper);
+        }}
+      >
+        {productList.map((item, index) => (
+          <SwiperSlide key={index}>
+            <div key={item.id} className="flex flex-col gap-3">
+              {/* Next.Image 商品图容器 */}
+              <div className="relative w-full aspect-[3/4] overflow-hidden rounded-md">
+                <Image
+                  src={item.image_url ? item.image_url : ""}
+                  alt={item.name}
+                  fill
+                  sizes="(max-width:768px) 50vw, 33vw"
+                  className="object-cover"
+                />
+              </div>
+
+              {/* 标题 */}
+              <p className="text-sm text-gray-800 truncate">{item.name}</p>
+
+              {/* 星级 + 评论数 */}
+              <div className="flex items-center gap-1">
+                {renderStars(item.rating)}
+                <span className="text-sm text-gray-600 ml-1 underline">
+                  {item.review_count}
+                </span>
+              </div>
+              {/* 价格 + 购物车图标 */}
+              <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"
+                        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>
-      {/* Swiper 核心容器 */}
-   
-
-      {/* 分页器容器(底部小圆点) */}
-      {/* <div className="swiper-pagination flex justify-center mt-4"></div> */}
+          </SwiperSlide>
+        ))}
+        <div
+          className="
+    mx-auto
+    mt-5
+    h-[4px]
+    w-[170px]
+    bg-[#DBDBDB]
+    rounded-full
+    overflow-hidden
+  "
+        >
+          <div
+            className="
+      h-full
+      bg-[#01754A]
+      rounded-full
+      transition-[width]
+      duration-300
+    "
+            style={{
+              width: `${progress}%`,
+            }}
+          />
+        </div>
+      </Swiper>
     </div>
   );
 };
-  //  <Swiper
-     
-  //       slidesPerView={2} // 一行显示2个商品(和图片一致)
-  //       spaceBetween={8} // 商品之间的间距
-  //       breakpoints={{
-  //         // 响应式适配(小屏显示1个)
-  //         390: { slidesPerView: 2 },
-  //         640: { slidesPerView: 2 },
-  //       }}
-  //       className="product-swiper"
-  //     >
-  //       {/* 循环渲染商品卡片 */}
-  //       {productList.map((product) => (
-  //         <SwiperSlide key={product.id} className="rounded-lg overflow-hidden">
-  //           <div className="flex flex-col h-full">
-  //             {/* 商品图片(带标签) */}
-  //             <div className="relative w-full aspect-[3/4]">
-  //               <Image
-  //                 src={product.imgUrl}
-  //                 alt={product.title}
-  //                 fill
-  //                 className="w-full h-auto object-cover rounded-t-lg"
-  //               />
-  //               {/* 促销标签(有内容才显示) */}
-  //               {product.tag && (
-  //                 <div className="absolute bottom-2 left-2 bg-black/80 text-white text-xs px-2 py-1 rounded">
-  //                   {product.tag}
-  //                 </div>
-  //               )}
-  //             </div>
-
-  //             {/* 商品标题 */}
-  //             <p className="mt-3 text-sm text-gray-800 font-medium line-clamp-2">
-  //               {product.title}
-  //             </p>
-
-  //             {/* 价格区域 */}
-  //             <div className="flex items-center gap-2 mt-2">
-  //               <span className="text-red-600 font-bold">{product.price}</span>
-  //               <span className="text-xs text-gray-500 line-through">
-  //                 {product.originalPrice}
-  //               </span>
-  //               {/* 购物车图标 */}
-  //               <span className="ml-auto text-gray-600">🛒</span>
-  //             </div>
-  //           </div>
-  //         </SwiperSlide>
-  //       ))}
-  //     </Swiper>
-export default AccountSwiper;
+export default AccountSwiper;

+ 3 - 1
src/types/api/customer/order/vieworder.ts

@@ -1,5 +1,5 @@
 // 订单详情整体类型
-interface AddressInfo {
+export interface AddressInfo {
   first_name: string;
   last_name: string;
   email: string;
@@ -27,6 +27,8 @@ interface OrderItem {
   total: number;
   weight: number;
   product_id: number;
+  image?: string;
+  options?: string;
 }
 
 interface InvoiceItem {