|
@@ -1,289 +0,0 @@
|
|
|
-"use client";
|
|
|
|
|
-
|
|
|
|
|
-import { AnimatePresence, motion } from "framer-motion";
|
|
|
|
|
-// import Link from "next/link";
|
|
|
|
|
-
|
|
|
|
|
-// import { MobileSearchBar } from "./MobileSearch";
|
|
|
|
|
-import { useBodyScrollLock } from "@utils/hooks/useBodyScrollLock";
|
|
|
|
|
-import{useState} from "react";
|
|
|
|
|
-import {useLoginModal} from "@/providers/LoginModalProvider";
|
|
|
|
|
-import { useCustomToast } from "@/utils/hooks/useToast";
|
|
|
|
|
-import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
|
|
|
|
|
-import { useRouter } from 'next/navigation';
|
|
|
|
|
-import Image from "next/image";
|
|
|
|
|
-import type { TreefrontMenu } from "@/types/theme/menus";
|
|
|
|
|
-import {CurrencySwitch} from "@/components/common/CurrencySwitch/CurrencySwitch";
|
|
|
|
|
-interface MobileMenuProps {
|
|
|
|
|
- menu: TreefrontMenu[];
|
|
|
|
|
- isOpen: boolean;
|
|
|
|
|
- onClose: () => void; // 接收关闭函数
|
|
|
|
|
- isGuest:boolean;
|
|
|
|
|
-}
|
|
|
|
|
-export default function MobileMenu({ menu, isOpen, onClose,isGuest }: MobileMenuProps) {
|
|
|
|
|
- const {openLoginModal} = useLoginModal();
|
|
|
|
|
- const { showToast } = useCustomToast();
|
|
|
|
|
- const router = useRouter();
|
|
|
|
|
- // 当前选中一级菜单ID
|
|
|
|
|
- const [activeMainId, setActiveMainId] = useState(menu[0]._id);
|
|
|
|
|
- // 存储右侧二级菜单展开状态集合 key:二级id value:是否展开boolean
|
|
|
|
|
- const [expandedSubIds, setExpandedSubIds] = useState<Record<string, boolean>>({});
|
|
|
|
|
-
|
|
|
|
|
- // 获取选中的一级菜单
|
|
|
|
|
- const activeMainMenu = menu.find((item) => item._id === activeMainId);
|
|
|
|
|
- // 当前一级下面全部二级菜单列表
|
|
|
|
|
- const subMenuList = activeMainMenu?.children?.edges?.map((e) => e.node) ?? [];
|
|
|
|
|
-
|
|
|
|
|
- // 切换右侧二级菜单展开/收起
|
|
|
|
|
- const toggleSubExpand = (subId: number) => {
|
|
|
|
|
- setExpandedSubIds((prev) => ({
|
|
|
|
|
- ...prev,
|
|
|
|
|
- [subId]: !prev[subId],
|
|
|
|
|
- }));
|
|
|
|
|
- };
|
|
|
|
|
- useBodyScrollLock(isOpen);
|
|
|
|
|
- const guestUserClick = () => {
|
|
|
|
|
- openLoginModal({
|
|
|
|
|
- onFinalResult: async (res) => {
|
|
|
|
|
- if(res.mergeCartSuccess) { // 购物车合并成功
|
|
|
|
|
- // if(!res.cartAfterMerge?.isVip && res.cartAfterMerge?.vipPlusAmount === 0) {
|
|
|
|
|
- // await switchVipPlus(true);
|
|
|
|
|
- // }
|
|
|
|
|
- return
|
|
|
|
|
- } else { // 购物车合并失败
|
|
|
|
|
- // 三种 情况: 登录成功; 注册成功;
|
|
|
|
|
- if(res.loginSuccess) {
|
|
|
|
|
- // 登录成功 需要刷新页面
|
|
|
|
|
- showToast('You have logged in successfully. But something wrong','danger');
|
|
|
|
|
- await confirmDialog({
|
|
|
|
|
- title: "Warning",
|
|
|
|
|
- content: "You have logged in successfully. But something wrong. You must refresh the page.",
|
|
|
|
|
- noCancel: true,
|
|
|
|
|
- });
|
|
|
|
|
- window.location.reload();
|
|
|
|
|
- }
|
|
|
|
|
- if(res.registerSuccess && !res.loginSuccess) {
|
|
|
|
|
- // 注册成功,去登录
|
|
|
|
|
- await confirmDialog({
|
|
|
|
|
- title: "Warning",
|
|
|
|
|
- content: "You have registered successfully. But something wrong. You have to login.",
|
|
|
|
|
- noCancel: true,
|
|
|
|
|
- });
|
|
|
|
|
- router.replace('/customer/login');
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <>
|
|
|
|
|
- <AnimatePresence>
|
|
|
|
|
- {/* {isOpen && ( */}
|
|
|
|
|
- <>
|
|
|
|
|
- <motion.div
|
|
|
|
|
- // initial={{ opacity: 0 }}
|
|
|
|
|
- // animate={{ opacity: 1 }}
|
|
|
|
|
- initial={false}
|
|
|
|
|
- animate={{
|
|
|
|
|
- opacity: isOpen ? 1 : 0,
|
|
|
|
|
- pointerEvents: isOpen ? "auto" : "none"
|
|
|
|
|
- }}
|
|
|
|
|
- // exit={{ opacity: 0 }}
|
|
|
|
|
- onClick={onClose}
|
|
|
|
|
- className="fixed inset-0 z-40 bg-transparent lg:hidden"
|
|
|
|
|
- style={{ top: "68px", bottom: "64px" }}
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <motion.div
|
|
|
|
|
- // initial={{ x: "-100%" }}
|
|
|
|
|
- // animate={{ x: 0 }}
|
|
|
|
|
- // exit={{ x: "-100%" }}
|
|
|
|
|
- initial={false}
|
|
|
|
|
- animate={{
|
|
|
|
|
- x: isOpen ? 0 : "-100%"
|
|
|
|
|
- }}
|
|
|
|
|
- transition={{ type: "spring", damping: 25, stiffness: 200 }}
|
|
|
|
|
- className="fixed left-0 z-50 w-full max-w-[448px] border-r border-neutral-200 bg-white dark:border-neutral-800 dark:bg-black lg:hidden drawer-scrollbar-hidden"
|
|
|
|
|
- style={{
|
|
|
|
|
- top: "0px",
|
|
|
|
|
- bottom: "0px",
|
|
|
|
|
- width: "100%",
|
|
|
|
|
- maxWidth: "448px",
|
|
|
|
|
- height: "calc(var(--visual-viewport-height) - 132px)",
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- {/* <div className="h-full overflow-y-auto px-4 py-4 drawer-scrollbar-hidden">
|
|
|
|
|
- <MobileSearchBar onClose={onClose} />
|
|
|
|
|
-
|
|
|
|
|
- <h1 className="mt-4 px-2 text-2xl font-semibold text-black dark:text-white">
|
|
|
|
|
- Category
|
|
|
|
|
- </h1>
|
|
|
|
|
-
|
|
|
|
|
- <ul className="mt-2 flex w-full flex-col drawer-scrollbar-hidden">
|
|
|
|
|
- {menu.map((item: any) => (
|
|
|
|
|
- <li
|
|
|
|
|
- key={item.id + item.name}
|
|
|
|
|
- className="p-2 text-xl text-black dark:text-white"
|
|
|
|
|
- >
|
|
|
|
|
- <Link
|
|
|
|
|
- href={item.slug ? `/category/${item.slug}/${item._id}` : "/search"}
|
|
|
|
|
- aria-label={`${item?.name}`}
|
|
|
|
|
- onClick={onClose}
|
|
|
|
|
- >
|
|
|
|
|
- {item.name}{item._id} 4444
|
|
|
|
|
- </Link>
|
|
|
|
|
- </li>
|
|
|
|
|
- ))}
|
|
|
|
|
- </ul>
|
|
|
|
|
- </div> */}
|
|
|
|
|
- <div className="h-screen flex flex-col overflow-hidden bg-white">
|
|
|
|
|
-
|
|
|
|
|
- <div className="flex justify-between items-center px-4 h-13.5 border-b border-[#E1E1E1FF]">
|
|
|
|
|
- <h2 className="text-base font-medium">VIP Rewards</h2>
|
|
|
|
|
- <div className="flex gap-3 items-center">
|
|
|
|
|
- {
|
|
|
|
|
- isGuest ? (
|
|
|
|
|
- <button onClick={guestUserClick} className="bg-emerald-700 text-white w-25.75 h-9 flex items-center justify-center rounded-full text-xs font-normal">
|
|
|
|
|
- Register/Login
|
|
|
|
|
- </button>
|
|
|
|
|
- ) : null
|
|
|
|
|
- }
|
|
|
|
|
- <svg
|
|
|
|
|
- width="24"
|
|
|
|
|
- height="24"
|
|
|
|
|
- viewBox="0 0 24 24"
|
|
|
|
|
- fill="none"
|
|
|
|
|
- stroke="currentColor"
|
|
|
|
|
- strokeWidth="2"
|
|
|
|
|
- onClick={onClose}
|
|
|
|
|
- >
|
|
|
|
|
- <path d="M18 6L6 18M6 6l12 12" />
|
|
|
|
|
- </svg>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div className="flex flex-1 overflow-hidden">
|
|
|
|
|
-
|
|
|
|
|
- <aside className="w-23 border-r border-gray-200 overflow-y-auto">
|
|
|
|
|
- {menu.map((mainItem) => (
|
|
|
|
|
- <div
|
|
|
|
|
- key={mainItem._id}
|
|
|
|
|
- onClick={() => setActiveMainId(mainItem._id)}
|
|
|
|
|
- className={`py-3 pl-4 pr-3 text-xs cursor-pointer border-r-2 border-b border-[#E1E1E1FF]
|
|
|
|
|
- ${
|
|
|
|
|
- activeMainId === mainItem._id
|
|
|
|
|
- ? "border-r-emerald-700 text-emerald-700 font-bold"
|
|
|
|
|
- : " text-gray-700 font-normal"
|
|
|
|
|
- }
|
|
|
|
|
- `}
|
|
|
|
|
- >
|
|
|
|
|
- {mainItem.name} {mainItem.showIcon&&(<Image
|
|
|
|
|
- className=" block"
|
|
|
|
|
- src="/image/theme/image_flame.webp"
|
|
|
|
|
- alt={"userphoto"}
|
|
|
|
|
- width={16}
|
|
|
|
|
- height={16}
|
|
|
|
|
- />)}
|
|
|
|
|
- </div>
|
|
|
|
|
- ))}
|
|
|
|
|
- </aside>
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- <main className="flex-1 overflow-y-auto p-3 pr-4">
|
|
|
|
|
- {/*顶部横幅占位 */}
|
|
|
|
|
- {activeMainMenu?.wapImage &&
|
|
|
|
|
- <div className="w-full bg-gray-100 mb-4 flex items-end">
|
|
|
|
|
- {/* <span className="text-3xl text-white pl-6 pb-4">Shop By Feature</span> */}
|
|
|
|
|
- <Image
|
|
|
|
|
- src={activeMainMenu?.wapImage||''}
|
|
|
|
|
- alt={activeMainMenu?.name||''}
|
|
|
|
|
- className="object-cover"
|
|
|
|
|
- width={269}
|
|
|
|
|
- height={37}
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- {subMenuList.length === 0 ? (
|
|
|
|
|
- <div className="text-gray-500 text-xl">No sub categories</div>
|
|
|
|
|
- ) : (
|
|
|
|
|
- subMenuList.map((subItem) => {
|
|
|
|
|
- const isOpen = !!expandedSubIds[subItem._id];
|
|
|
|
|
-
|
|
|
|
|
- const cardEdges = subItem?.children?.edges ?? [];
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <div key={subItem._id} className="mb-6">
|
|
|
|
|
-
|
|
|
|
|
- <div
|
|
|
|
|
- onClick={() => toggleSubExpand(subItem._id)}
|
|
|
|
|
- className="flex justify-between items-center cursor-pointer py-2 px-3 h-10 bg-[#F9F9F9FF]"
|
|
|
|
|
- >
|
|
|
|
|
- <h3 className="text-2xl font-medium">{subItem.name}</h3>
|
|
|
|
|
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
|
|
|
- className={`transition-transform duration-300 ${isOpen ? "rotate-0" : "rotate-180"}`}
|
|
|
|
|
- >
|
|
|
|
|
- <path stroke="rgba(0, 0, 0, 1)" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="square" d="M18 15L12 9L6 15"></path>
|
|
|
|
|
- </svg>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- {/*二级展开之后渲染三级卡片网格 */}
|
|
|
|
|
- {isOpen && cardEdges.length > 0 && (
|
|
|
|
|
- <div className="grid grid-cols-3 gap-2 pt-3">
|
|
|
|
|
- {cardEdges.map((edge) => {
|
|
|
|
|
- const cardItem = edge.node;
|
|
|
|
|
- return (
|
|
|
|
|
- <div
|
|
|
|
|
- key={cardItem._id}
|
|
|
|
|
- className="flex flex-col items-center cursor-pointer"
|
|
|
|
|
- >
|
|
|
|
|
- <div className="w-full relative bg-gray-200 mb-1 overflow-hidden">
|
|
|
|
|
-
|
|
|
|
|
- <Image
|
|
|
|
|
- src={cardItem.image||''}
|
|
|
|
|
- alt={cardItem.name||''}
|
|
|
|
|
- width={84}
|
|
|
|
|
- height={112}
|
|
|
|
|
- className="object-cover"
|
|
|
|
|
- />
|
|
|
|
|
- {
|
|
|
|
|
- cardItem.showIcon&&(
|
|
|
|
|
- <Image
|
|
|
|
|
- className=" block absolute top-0 left-0"
|
|
|
|
|
- src="/image/theme/image_hot.webp"
|
|
|
|
|
- alt={"userphoto"}
|
|
|
|
|
- width={30}
|
|
|
|
|
- height={14}
|
|
|
|
|
- />
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- </div>
|
|
|
|
|
- <span className="text-xs font-normal text-center">{cardItem.name}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- })}
|
|
|
|
|
- </div>
|
|
|
|
|
- )}
|
|
|
|
|
- {isOpen && cardEdges.length === 0 && (
|
|
|
|
|
- <div className="py-3 text-gray-500">No feature items</div>
|
|
|
|
|
- )}
|
|
|
|
|
- </div>
|
|
|
|
|
- );
|
|
|
|
|
- })
|
|
|
|
|
- )}
|
|
|
|
|
- </main>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- {/* =========底部固定货币选择栏 ========= */}
|
|
|
|
|
- <div className="h-12 border-t border-gray-200 flex items-center px-4 gap-4">
|
|
|
|
|
- <CurrencySwitch/>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- </motion.div>
|
|
|
|
|
- </>
|
|
|
|
|
- {/* )} */}
|
|
|
|
|
- </AnimatePresence>
|
|
|
|
|
- </>
|
|
|
|
|
- );
|
|
|
|
|
-}
|
|
|