소스 검색

添加购物车页

fogwind 1 주 전
부모
커밋
af37cfbc50

BIN
public/image/empty-cart.webp


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 0
public/image/payment/paypal-logo.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 0
public/image/plus-crown.svg


+ 33 - 0
src/app/(checkout)/checkout/cart/_components/BonusBuy.tsx

@@ -0,0 +1,33 @@
+"use client";
+
+import Image from "next/image";
+
+export function BonusBuy() { 
+
+
+    return (
+        <div className="box-border w-full">
+            <div className="text-ly-24 font-medium">Bonus Buy</div>
+            <div className="box-border w-full flex justify-between">
+                <div className="flex-none w-41.25 mt-4">
+                    <div className="relative flex-none w-41.25 h-42.5">
+                        <Image className="object-cover" fill
+                            src="https://cdn.asteriahair.com/media/catalog/product/1/_/1_1_5.png"
+                            alt="test" 
+                        />
+                    </div>
+                    <p className="mt-2 w-full line-clamp-2 text-ly-12 leading-5">
+                        USA Orders Only-DoublelayerSatin Silk Sleep Cap 
+                    </p>
+                    <button className="flex justify-between items-center box-border w-full mt-3 px-3 border-1 h-10 rounded-3xl">
+                        <span className="text-ly-14 font-bold">$9.99</span>
+                        <span className="flex items-center text-ly-12">
+                            <svg className="w-4 h-4" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><path    stroke="rgba(0, 0, 0, 1)" strokeWidth="1.3333333333333333" strokeLinejoin="round" strokeLinecap="square"  d="M8.02001 3.33301L8.00781 12.6663"></path><path    stroke="rgba(0, 0, 0, 1)" strokeWidth="1.3333333333333333" strokeLinejoin="round" strokeLinecap="square"  d="M3.3335 8L12.6668 8"></path></svg>
+                            ADD
+                        </span>
+                    </button>
+                </div>
+            </div>
+        </div>
+    );
+}

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 435 - 0
src/app/(checkout)/checkout/cart/_components/CartWrapper.tsx


+ 47 - 0
src/app/(checkout)/checkout/cart/_components/EmptyCart.tsx

@@ -0,0 +1,47 @@
+"use client";
+
+import Image from "next/image";
+import { useRouter } from 'next/navigation';
+
+
+export function EmptyCart({isGuest}:{isGuest: boolean;}) { 
+
+    const router = useRouter();
+    return (
+        <div className="w-full box-border">
+            <div className="w-37.5 h-37.5 mt-6 mx-auto">
+                <Image src="/image/empty-cart.webp"
+                    width={150}
+                    height={150}
+                    alt="empty cart"
+                />
+            </div>
+            <p className="text-center text-ly-16">Your Cart Is Empty</p>
+            {isGuest && <>
+                <p className="mt-2 text-center text-ly-12 leading-5 text-ly-placeholder">Have an account? Sign in to view your cart</p>
+                <div className="relative border-box w-full px-4 mt-15">
+                    <button className="flex items-center justify-center w-full bg-ly-middlegreen text-ly-16 text-white h-12 rounded-3xl"
+                        onClick={() => {router.replace('/customer/login')}}
+                    >
+                        Sign In
+                    </button>
+                    <button className="mt-3 flex items-center justify-center w-full  text-ly-16 h-12 rounded-3xl border-1"
+                        onClick={() => {router.replace('/')}}
+                    >
+                        Back To Shop
+                    </button>
+                    <div className="text-ly-12 absolute -top-4 left-1/2 -translate-x-1/2 -translate-y-full flex items-center bg-[#ffe033] w-max h-7 px-2 rounded-sm">
+                        <span className="absolute left-12.5 bottom-0 translate-y-2/5 bg-[#ffe033] w-3 h-3 rotate-z-45 rotate-y-30"></span>
+                        <span className="font-bold px-1 leading-5 rounded-xs bg-white mr-2">$200+</span>
+                        Log In To Unlock VIP CouponPack
+                        <button className="ml-1 w-6 h-6">
+                            <svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" fill="none"><path    stroke="rgba(0, 0, 0, 1)" strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="square"  d="M7 7L17 17"></path><path    stroke="rgba(0, 0, 0, 1)" strokeWidth="1.5" strokeLinejoin="round" strokeLinecap="square"  d="M7 17L17 7"></path></svg>
+                        </button>
+                        
+                    </div>
+                </div>
+            </>}
+            
+        </div>
+    );
+}

+ 23 - 0
src/app/(checkout)/checkout/cart/_components/PromiseInCart.tsx

@@ -0,0 +1,23 @@
+"use client";
+
+
+
+export function PromiseInCart() { 
+
+
+    return (
+        <div className="flex items-center justify-between w-full box-border bg-[#EAE9E1] h-10 px-5">
+            <div className="flex-none w-17.5 text-center text-ly-12 leading-4">
+                30 Days <br/>Free Return
+            </div>
+            <div className="relative text-center text-ly-12 leading-4 px-8">
+                <span className=" absolute left-0 top-1/2 -translate-y-1/2 h-5 border-[#cccccc] border-l-1"></span>
+                24-48 Hrs <br/>Free Fast Shipping
+                <span className=" absolute right-0 top-1/2 -translate-y-1/2 h-5 border-[#cccccc] border-l-1"></span>
+            </div>
+            <div className="flex-none w-17.5 text-center text-ly-12 leading-4">
+                Buy Now <br/>& Pay Later
+            </div>
+        </div>
+    );
+}

+ 54 - 0
src/app/(checkout)/checkout/cart/page.tsx

@@ -0,0 +1,54 @@
+import { createHash } from "crypto";
+import {serverGraphqlFetch} from "@utils/bagisto/index";
+import { GET_CART_ITEM } from "@/graphql";
+import { auth } from "@/utils/auth/auth-helper";
+import { GetCartItemData } from "@/types/cart/type";
+import TitleHeader from "@components/layout/navbar/TitleHeader";
+import { EmptyCart } from "./_components/EmptyCart";
+import { PromiseInCart } from "./_components/PromiseInCart";
+import { CartWrapper } from "./_components/CartWrapper";
+/**
+ * 购物车页 
+ * 登录用户的话,获取用户地址列表 从auth session 中获取email
+ * 获取billing地址和shipping地址
+ * 获取购物车数据
+ * 获取运输方式
+ * 获取支付方式
+ *  
+ * 
+ */
+export const dynamic = "force-dynamic";
+
+export default async function CheckoutPage() {
+
+    const session = await auth(); // 游客是null
+    const isGuest = session? true : false;
+
+    const {data: cartDetailsRes} = await serverGraphqlFetch<GetCartItemData>({
+        query: GET_CART_ITEM
+    });
+
+    let cartDatahash = "";
+    const cartDetails = cartDetailsRes?.createReadCart?.readCart ?? null;
+    if(cartDetails) {
+        cartDatahash = createHash('sha256').update(JSON.stringify(cartDetails)).digest('hex');
+    }
+    
+    return (
+        <>
+            <TitleHeader title="My Cart" />
+            <PromiseInCart />
+            {!cartDetails && 
+                <EmptyCart 
+                    isGuest={isGuest}
+                />
+            }
+            {cartDetails &&
+                <CartWrapper key={cartDatahash}
+                    cartDetailData={cartDetails}
+                />
+            }
+        </>
+    );
+  
+}

+ 10 - 0
src/components/theme/ui/CommonModal.tsx

@@ -12,8 +12,10 @@ export default function CommonModal({
     isOpen,
     destroyOnClose = false,
     contentClassName,
+    contentStyle = {},
     clickOutsideClose,
     onClose,
+    animationEndCallback = () => {},
     header,
     body,
     footer
@@ -21,8 +23,10 @@ export default function CommonModal({
     isOpen: boolean;
     destroyOnClose?: boolean;
     contentClassName?: string;
+    contentStyle?: React.CSSProperties;
     clickOutsideClose?: boolean;
     onClose: (e: boolean) => void;
+    animationEndCallback?: (flag: string) => void; 
     header?: React.ReactNode;
     body: React.ReactNode;
     footer?: React.ReactNode;
@@ -118,12 +122,17 @@ export default function CommonModal({
     }
     // 如果是关闭,动画结束之后才隐藏
     const handlerAnimationEnd = (e: React.AnimationEvent<HTMLDivElement>) => {
+        // 如果子元素上也有自己的动画效果,也会触发父元素的动画事件
+        if (e.target !== e.currentTarget) return;
         if(e.animationName === "fade-out") {
             setRootVisible('hidden');
             if(destroyOnClose){
                 setMounted(false);
                 cssInit.current=false;
             }
+            animationEndCallback('close');
+        } else {
+            animationEndCallback('show');
         }
     }
     // 初始化时是隐藏状态
@@ -152,6 +161,7 @@ export default function CommonModal({
                         }
                     )}
                     style={{
+                        ...contentStyle,
                         paddingTop: rootPddingTop,
                         paddingBottom: rootPddingBottom
                     }}