Kaynağa Gözat

top bar 优化;

fogwind 1 gün önce
ebeveyn
işleme
2ab1093c7d

+ 9 - 9
src/components/layout/navbar/TopSwiperBanner.tsx

@@ -1,6 +1,6 @@
 "use client";
 
-import { useEffect, useState } from "react";
+import { useEffect, useState, use } from "react";
 import Link from "next/link";
 
 const ITEM_HEIGHT = 40;
@@ -14,19 +14,19 @@ export interface AnnouncementItem {
 }
 
 interface AnnouncementBarProps {
-  items?: AnnouncementItem[];
+  topBarPromise: Promise<AnnouncementItem[]>;
 }
 
-export default function TopSwiperBanner({ items = [] }: AnnouncementBarProps) {
+export default function TopSwiperBanner({ topBarPromise }: AnnouncementBarProps) {
   const [index, setIndex] = useState(0);
 
   const [enableTransition, setEnableTransition] = useState(true);
 
-  const loopItems = [...items, ...(items.length ? [items[0]] : [])];
+  const loopItems = use(topBarPromise);
 
   // 自动滚动
   useEffect(() => {
-    if (items.length <= 1) {
+    if (loopItems.length <= 1) {
       return;
     }
 
@@ -37,11 +37,11 @@ export default function TopSwiperBanner({ items = [] }: AnnouncementBarProps) {
     return () => {
       window.clearInterval(timer);
     };
-  }, [items.length]);
+  }, [loopItems.length]);
 
   // 无缝循环重置
   useEffect(() => {
-    if (index !== items.length) {
+    if (index !== loopItems.length) {
       return;
     }
 
@@ -60,8 +60,8 @@ export default function TopSwiperBanner({ items = [] }: AnnouncementBarProps) {
     return () => {
       window.clearTimeout(timer);
     };
-  }, [index, items.length]);
-  if (!items.length) {
+  }, [index, loopItems.length]);
+  if (!loopItems.length) {
     return null;
   }
 

+ 18 - 15
src/components/layout/navbar/index.tsx

@@ -1,9 +1,9 @@
 import Link from "next/link";
+import {Suspense} from "react";
 import LogoIcon from "@components/common/icons/LogoIcon";
 import MobileMenuTrigger from "./MobileMenuTrigger";
 import { NavbarActions } from "./NavbarActions";
 import { NavbarErrorBoundary } from "@/components/error/ErrorBoundary";
-
 import { 
   GET_TREEFRONT_MENUS } from "@/graphql";
 import { serverGraphqlFetch } from "@utils/bagisto/index";
@@ -13,29 +13,29 @@ import { auth } from "@/utils/auth/auth-helper";
 import TopSwiperBanner from "./TopSwiperBanner"
 import { restApiFetch } from "@utils/bagisto/index";
 import type { AnnouncementItem } from "./TopSwiperBanner";
+import type { RestfulGetThemeCustomizations } from '@/types/theme/theme-customization';
 export default async function Navbar() {
   // 2. 服务端 fetch 请求我们自己的 Next.js API
-  const res = await restApiFetch({
+  const topBarPromise:Promise<AnnouncementItem[]> = restApiFetch<{data:RestfulGetThemeCustomizations}>({
     api: `/shop/theme-customizations?name=Top_Bar`,
     method: "GET",
     isRoute: false,
     takeAuthorization: false,
-  
-  });
-  let topBarData: AnnouncementItem[] = [];
-  try {
-    // 安全取值链
-    const themeItem = res?.body?.[0];
+  }).then((result) => {
+    const themeItem = result.body[0];
     const optionsStr = themeItem?.translation?.options;
     if (optionsStr) {
       const parsed = JSON.parse(optionsStr);
-      topBarData = parsed?.images ?? [];
+      return parsed?.images ?? [];
+    } else {
+      return [];
     }
-  } catch (err) {
-    console.error("解析 translation.options 失败", err);
-    topBarData = [];
-  }
-  console.log('topbar-res----------------------------:',res);
+  }).catch((err) => {
+      // 获取top_bar失败不应该阻止用户访问网站
+      console.error('get top_bar error: ', err);
+      return [];
+  });
+
   
     const session = await auth();// 游客是null
     const isGuest = session === null;
@@ -44,6 +44,7 @@ export default async function Navbar() {
       GetTreefrontMenusResult
     >({
       query: GET_TREEFRONT_MENUS,
+      takeAuthorization: false,
     });
     console.log('GetTreefrontMenusResult --- ',menudata)
     const menudatas =menudata? menudata.treefrontMenus:[];
@@ -51,7 +52,9 @@ export default async function Navbar() {
   return (
     <NavbarErrorBoundary>
       <header className="sticky top-0 z-10">
-         <TopSwiperBanner  items={topBarData} /> 
+        <Suspense fallback={<div className="flex items-center justify-center h-10 w-full overflow-hidden bg-ly-middlegreen text-ly-14 text-white">Loading top bar...</div>}>
+          <TopSwiperBanner  topBarPromise={topBarPromise} /> 
+        </Suspense>
         <nav className="relative flex flex-col items-center justify-between gap-4 bg-neutral-50 p-4 pt-3 pb-3 dark:bg-neutral-900 md:flex-row lg:px-6 lg:py-4">
           <div className="flex w-full items-center justify-between gap-0 sm:gap-4">
             {/* 1. THE STATIC SHELL (Visible Instantly) */}

+ 25 - 0
src/types/theme/theme-customization.ts

@@ -18,6 +18,31 @@ export interface GetThemeCustomization {
   }
 }
 
+// restful /api/shop/theme-customizations
+export type RestfulGetThemeCustomizations = Array<{
+  id: number;
+  themeCode: string;
+  type: string;
+  name: string;
+  sortOrder: number;
+  status: number;
+  showstyle: string;
+  createdAt: string;
+  updatedAt: string;
+  translation: {
+      id: number;
+      themeCustomizationId: number;
+      locale: string;
+      options: string;
+  },
+  translations: Array<{
+      id: number;
+      themeCustomizationId: number;
+      locale: string;
+      options: string;
+  }>;
+    
+}>;
 
 // Theme Customization Options Types