文档

Banner System Implementation

动态 Banner 系统的实现文档和移除指南

Banner System Implementation

本文档介绍了网站顶部 Banner 系统的完整实现,包括组件结构、状态管理和如何进行修改或移除。

系统架构

核心组件

  1. 状态管理 - src/lib/stores/banner-store.ts
  2. 布局钩子 - src/lib/hooks/use-banner-layout.ts
  3. Banner 组件 - src/modules/shared/components/BetaBanner.tsx
  4. 动态布局包装器
    • src/app/(public)/[locale]/DynamicLayoutWrapper.tsx
    • src/app/(app)/DynamicAppWrapper.tsx

技术特性

  • 状态持久化: 使用 Zustand + persist 中间件,用户关闭后状态保存在 localStorage
  • 动态布局: 根据 Banner 显示状态自动调整 NavBar 位置和内容区域 padding
  • 平滑过渡: 所有组件都有 200ms 的过渡动画效果
  • 响应式设计: 适配不同布局(公共页面 vs 应用页面)

实现细节

1. 状态管理 (banner-store.ts)

interface BannerStore {
  isBetaBannerVisible: boolean;
  hideBetaBanner: () => void;
  showBetaBanner: () => void;
}

使用 Zustand 管理 Banner 的显示状态,支持状态持久化。

2. 动态布局计算 (use-banner-layout.ts)

const styles = {
  navbarTop: isBetaBannerVisible ? 'top-8' : 'top-0',
  publicMainPadding: isBetaBannerVisible ? 'pt-20' : 'pt-16',
  appMainPadding: isBetaBannerVisible ? 'pt-8' : 'pt-0',
};

根据 Banner 状态动态计算各组件的样式。Banner 高度为 32px(使用 py-1 样式)。

3. Banner 组件特性

  • 固定定位在页面顶部 (fixed top-0 z-[60])
  • 黑色背景白色文字的简洁设计
  • 支持关闭功能,关闭后触发状态更新
  • 使用 shadcn/ui 的官方 Banner 组件

4. 布局适配

公共页面布局 (有 NavBar):

  • Banner 显示时: pt-20 (Banner + NavBar 高度)
  • Banner 隐藏时: pt-16 (仅 NavBar 高度)

应用页面布局 (无 NavBar):

  • Banner 显示时: pt-8 (仅 Banner 高度)
  • Banner 隐藏时: pt-0 (无额外 padding)

如何移除 Banner 系统

如果需要完全移除 Banner 系统,请按以下步骤操作:

如何暂时关闭 Banner

如果只需要暂时关闭 Banner(比如在测试期间或特殊情况下),有以下几种方法:

方法 1: 修改默认状态(推荐)

编辑 src/lib/stores/banner-store.ts,将默认显示状态改为 false:

export const useBannerStore = create<BannerStore>()(
  persist(
    (set) => ({
      isBetaBannerVisible: false, // 改为 false 暂时关闭
      hideBetaBanner: () => set({ isBetaBannerVisible: false }),
      showBetaBanner: () => set({ isBetaBannerVisible: true }),
    }),
    {
      name: 'banner-storage',
    }
  )
);

优点:

  • 只需修改一行代码
  • 保持了完整的功能逻辑
  • 用户之前的关闭状态会被保留
  • 将来重新开启只需要改回 true

方法 2: 在组件中直接返回 null

编辑 src/modules/shared/components/BetaBanner.tsx

export function BetaBanner() {
  // 暂时关闭 Banner
  return null;
  
  // 以下代码暂时注释...
}

方法 3: 使用环境变量控制

.env.local 中添加:

NEXT_PUBLIC_SHOW_BETA_BANNER=false

然后在 BetaBanner 组件中:

export function BetaBanner() {
  if (process.env.NEXT_PUBLIC_SHOW_BETA_BANNER === 'false') {
    return null;
  }
  // 原有逻辑...
}

如何完全移除 Banner 系统

步骤 1: 移除核心文件

# 删除状态管理
rm src/lib/stores/banner-store.ts

# 删除布局钩子
rm src/lib/hooks/use-banner-layout.ts

# 删除 Banner 组件
rm src/modules/shared/components/BetaBanner.tsx

# 删除动态布局包装器
rm src/app/(public)/[locale]/DynamicLayoutWrapper.tsx
rm src/app/(app)/DynamicAppWrapper.tsx

步骤 2: 还原 NavBar 组件

编辑 src/modules/marketing/shared/components/NavBar.tsx:

- import { useBannerLayout } from "@/lib/hooks/use-banner-layout";

export function NavBar() {
  const t = useTranslations();
  const { user } = useSession();
- const { navbarTop } = useBannerLayout();
  // ... 其他代码保持不变

  return (
    <nav
      className={cn(
-       "fixed left-0 z-50 w-full transition-all duration-200",
-       navbarTop,
+       "fixed top-0 left-0 z-50 w-full transition-shadow duration-200",
        !isTop || isDocsPage || isHome1Page
          ? "bg-card/80 shadow-sm backdrop-blur-lg"
          : "shadow-none",
      )}
      data-test="navigation"
    >

步骤 3: 还原公共页面布局

编辑 src/app/(public)/[locale]/layout.tsx:

- import { BetaBanner } from "@/components/shared/BetaBanner";
- import { DynamicLayoutWrapper } from "./DynamicLayoutWrapper";

// 在 JSX 中移除 Banner 和动态包装器
<NextIntlClientProvider locale={locale} messages={messages}>
  <SessionProvider>
-   <BetaBanner />
    <NavBar />
-   <DynamicLayoutWrapper>{children}</DynamicLayoutWrapper>
+   <main className="min-h-screen pt-16">{children}</main>
    <ConditionalFooter locale={locale} />
    <TabBar />
  </SessionProvider>
</NextIntlClientProvider>

步骤 4: 还原应用页面布局

编辑 src/app/(app)/layout.tsx:

- import { BetaBanner } from "@/components/shared/BetaBanner";
- import { DynamicAppWrapper } from "./DynamicAppWrapper";

return (
  <Document locale={locale}>
    <NextIntlClientProvider messages={messages}>
-     <BetaBanner />
-     <DynamicAppWrapper>{children}</DynamicAppWrapper>
+     {children}
    </NextIntlClientProvider>
  </Document>
);

步骤 5: 清理翻译文件

编辑 src/lib/i18n/translations/zh.jsonen.json:

{
- "beta": {
-   "label": "BETA",
-   "message": "本平台处于测试阶段,数据可能丢失",
-   "close": "关闭"
- },
  // ... 其他翻译保持不变
}

步骤 6: 清理依赖(可选)

如果项目中没有其他地方使用 Zustand,可以移除依赖:

bun remove zustand

修改 Banner 内容

如果只需要修改 Banner 的文字内容,编辑翻译文件:

中文 (src/lib/i18n/translations/zh.json):

{
  "beta": {
    "label": "BETA",
    "message": "你的新消息内容",
    "close": "关闭"
  }
}

英文 (src/lib/i18n/translations/en.json):

{
  "beta": {
    "label": "BETA", 
    "message": "Your new message content",
    "close": "Close"
  }
}

修改 Banner 样式

如果需要修改 Banner 的外观,编辑 src/modules/shared/components/BetaBanner.tsx 中的 className:

<Banner className="bg-blue-600 text-white fixed top-0 left-0 w-full z-[60]">

注意事项

  1. 状态持久化: Banner 的显示状态会保存在用户的 localStorage 中,清除浏览器数据后会重新显示
  2. Z-index 层级: Banner 使用 z-[60],高于 NavBar 的 z-50
  3. 过渡动画: 所有相关组件都有 200ms 的过渡效果,移除时注意保持一致性
  4. 响应式适配: 当前实现在移动端和桌面端都有良好的表现
  5. Banner 高度: 当前使用 py-1 样式,实际高度约 32px,布局计算基于此高度