Files
photography/ui/CLAUDE.md
xujiang 73197d8da8 feat: 完善模块化 CLAUDE.md 文档体系
- 新增 admin/CLAUDE.md - 管理后台开发指导文档
  - 修正技术栈为 React + TypeScript + shadcn/ui
  - 提供完整的管理后台架构设计
  - 包含照片管理、分类管理、日志管理等核心功能
  - 详细的开发环境配置和部署指南

- 新增 backend/CLAUDE.md - 后端开发指导文档
  - 基于 Golang + Gin + GORM 技术栈
  - 完整的 API 接口设计和数据库架构
  - 包含认证、权限、文件存储等核心功能
  - 详细的部署和监控配置

- 新增 ui/CLAUDE.md - UI 备份模块管理文档
  - 支持组件备份和 A/B 测试功能
  - 详细的同步策略和实验环境配置
  - 完整的版本管理和协作流程

- 更新 CLAUDE.md 根目录文档
  - 完善模块选择指南和协调机制
  - 新增模块间通信和依赖关系说明
  - 优化文档维护和使用建议
  - 建立完整的模块化开发规范

通过模块化设计最大限度减少 AI 幻觉,提高开发效率。
2025-07-09 14:23:15 +08:00

16 KiB

UI 备份模块 - CLAUDE.md

此文件为 Claude Code 在 UI 备份模块工作时提供指导。

🎯 模块概览

UI 备份模块是主前端模块 (frontend/) 的备份和实验环境,用于保存稳定版本、测试新功能和组件开发。

模块用途

  • 🔄 主前端代码的备份版本
  • 🧪 新功能和组件的实验环境
  • 📦 组件库的独立维护
  • 🔧 A/B 测试和版本对比
  • 🛠️ 紧急回滚的备用版本

技术栈

  • 前端: Next.js 15 + React 19 + TypeScript
  • 组件: shadcn/ui + Radix UI
  • 样式: Tailwind CSS
  • 构建: Next.js 内置构建系统
  • 包管理: pnpm (与主前端区分)

📁 目录结构

ui/
├── CLAUDE.md                       # 🔍 当前文件 - UI 备份模块指导
├── app/                           # Next.js 应用目录
│   ├── globals.css               # 全局样式
│   ├── layout.tsx                # 根布局
│   └── page.tsx                  # 主页
├── components/                    # 组件目录
│   ├── ui/                       # shadcn/ui 组件
│   │   ├── button.tsx           # 按钮组件
│   │   ├── card.tsx             # 卡片组件
│   │   ├── dialog.tsx           # 对话框组件
│   │   └── ...                  # 其他 UI 组件
│   ├── about-view.tsx            # 关于页面组件
│   ├── contact-view.tsx          # 联系页面组件
│   ├── filter-bar.tsx            # 过滤栏组件
│   ├── loading-spinner.tsx       # 加载组件
│   ├── navigation.tsx            # 导航组件
│   ├── photo-gallery.tsx         # 照片画廊组件
│   ├── photo-modal.tsx           # 照片弹窗组件
│   ├── theme-provider.tsx        # 主题提供器
│   ├── timeline-stats.tsx        # 时间线统计
│   └── timeline-view.tsx         # 时间线视图
├── hooks/                        # 自定义 Hook
│   ├── use-mobile.tsx            # 移动端检测
│   └── use-toast.ts              # 提示信息
├── lib/                          # 工具库
│   └── utils.ts                  # 工具函数
├── public/                       # 静态资源
│   ├── placeholder-logo.png      # 占位图标
│   ├── placeholder-user.jpg      # 占位头像
│   └── placeholder.jpg           # 占位图片
├── styles/                       # 样式文件
│   └── globals.css               # 全局样式
├── components.json               # shadcn/ui 配置
├── next.config.mjs               # Next.js 配置
├── package.json                  # 依赖配置
├── pnpm-lock.yaml               # pnpm 锁文件
├── postcss.config.mjs           # PostCSS 配置
├── tailwind.config.ts           # Tailwind 配置
├── tsconfig.json                # TypeScript 配置
└── README.md                    # 模块说明

🔄 与主前端的关系

同步策略

# 从主前端同步到 UI 备份
# 1. 同步核心组件
cp -r frontend/components/ui/* ui/components/ui/

# 2. 同步配置文件
cp frontend/tailwind.config.ts ui/
cp frontend/components.json ui/

# 3. 同步样式文件
cp frontend/app/globals.css ui/app/
cp frontend/styles/globals.css ui/styles/

# 4. 同步工具函数
cp frontend/lib/utils.ts ui/lib/

版本控制

# 创建备份分支
git checkout -b ui-backup-v1.0.0

# 标记稳定版本
git tag ui-v1.0.0

# 推送备份版本
git push origin ui-backup-v1.0.0
git push origin ui-v1.0.0

🧪 实验环境配置

开发环境设置

# 进入 UI 备份目录
cd ui/

# 安装依赖 (使用 pnpm)
pnpm install

# 启动开发服务器
pnpm dev

# 构建项目
pnpm build

# 启动生产预览
pnpm start

环境变量配置

# .env.local
NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_MOCK_API=true
NEXT_PUBLIC_EXPERIMENTAL_FEATURES=true

实验性功能开关

// lib/experimental.ts
export const experimentalFeatures = {
  // 新组件测试
  newPhotoGallery: process.env.NEXT_PUBLIC_EXPERIMENTAL_FEATURES === 'true',
  
  // A/B 测试
  alternativeNavigation: false,
  
  // 性能优化
  optimizedImages: true,
  
  // 新主题
  darkModeV2: false,
} as const

// 使用示例
import { experimentalFeatures } from '@/lib/experimental'

export default function PhotoGallery() {
  if (experimentalFeatures.newPhotoGallery) {
    return <NewPhotoGallery />
  }
  
  return <OriginalPhotoGallery />
}

🎨 组件库管理

组件分类

components/
├── ui/                          # 基础 UI 组件
│   ├── button.tsx              # 按钮 - 基础交互
│   ├── input.tsx               # 输入框 - 表单控件
│   ├── card.tsx                # 卡片 - 内容容器
│   ├── dialog.tsx              # 对话框 - 模态框
│   ├── dropdown-menu.tsx       # 下拉菜单 - 菜单控件
│   ├── navigation-menu.tsx     # 导航菜单 - 导航控件
│   ├── tabs.tsx                # 标签页 - 切换控件
│   ├── toast.tsx               # 提示信息 - 反馈组件
│   └── ...                     # 其他基础组件
├── business/                   # 业务组件
│   ├── photo-gallery.tsx       # 照片画廊
│   ├── photo-modal.tsx         # 照片弹窗
│   ├── timeline-view.tsx       # 时间线视图
│   ├── filter-bar.tsx          # 过滤栏
│   └── navigation.tsx          # 站点导航
├── layout/                     # 布局组件
│   ├── header.tsx              # 页头
│   ├── footer.tsx              # 页脚
│   └── sidebar.tsx             # 侧边栏
└── experimental/               # 实验性组件
    ├── new-photo-gallery.tsx   # 新照片画廊
    ├── advanced-filter.tsx     # 高级过滤器
    └── ai-search.tsx           # AI 搜索

组件开发规范

// components/experimental/new-photo-gallery.tsx
import { useState, useCallback } from 'react'
import { Card, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button'

interface Photo {
  id: string
  src: string
  alt: string
  title: string
  description?: string
}

interface NewPhotoGalleryProps {
  photos: Photo[]
  onPhotoClick?: (photo: Photo) => void
  className?: string
}

export function NewPhotoGallery({ 
  photos, 
  onPhotoClick, 
  className 
}: NewPhotoGalleryProps) {
  const [selectedPhoto, setSelectedPhoto] = useState<Photo | null>(null)

  const handlePhotoClick = useCallback((photo: Photo) => {
    setSelectedPhoto(photo)
    onPhotoClick?.(photo)
  }, [onPhotoClick])

  return (
    <div className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 ${className}`}>
      {photos.map((photo) => (
        <Card key={photo.id} className="overflow-hidden hover:shadow-lg transition-shadow">
          <CardContent className="p-0">
            <div 
              className="relative aspect-square cursor-pointer"
              onClick={() => handlePhotoClick(photo)}
            >
              <img
                src={photo.src}
                alt={photo.alt}
                className="w-full h-full object-cover"
              />
              <div className="absolute inset-0 bg-black/0 hover:bg-black/20 transition-colors" />
            </div>
            {photo.title && (
              <div className="p-3">
                <h3 className="font-semibold text-sm">{photo.title}</h3>
                {photo.description && (
                  <p className="text-xs text-muted-foreground mt-1">
                    {photo.description}
                  </p>
                )}
              </div>
            )}
          </CardContent>
        </Card>
      ))}
    </div>
  )
}

🔧 版本管理和同步

版本管理策略

# 1. 创建功能分支
git checkout -b feature/new-component

# 2. 开发和测试
# ... 组件开发 ...

# 3. 合并到 UI 备份主分支
git checkout main
git merge feature/new-component

# 4. 测试稳定后,同步到主前端
cp ui/components/experimental/new-component.tsx frontend/components/

自动同步脚本

#!/bin/bash
# scripts/sync-ui-to-frontend.sh

set -e

UI_DIR="ui"
FRONTEND_DIR="frontend"

echo "🔄 开始同步 UI 备份到主前端..."

# 同步基础组件
echo "📦 同步基础组件..."
rsync -av --exclude='experimental' $UI_DIR/components/ui/ $FRONTEND_DIR/components/ui/

# 同步配置文件
echo "⚙️ 同步配置文件..."
cp $UI_DIR/tailwind.config.ts $FRONTEND_DIR/
cp $UI_DIR/components.json $FRONTEND_DIR/

# 同步样式文件
echo "🎨 同步样式文件..."
cp $UI_DIR/app/globals.css $FRONTEND_DIR/app/
cp $UI_DIR/styles/globals.css $FRONTEND_DIR/styles/

# 同步工具函数
echo "🛠️ 同步工具函数..."
cp $UI_DIR/lib/utils.ts $FRONTEND_DIR/lib/

echo "✅ 同步完成!"

反向同步脚本

#!/bin/bash
# scripts/sync-frontend-to-ui.sh

set -e

FRONTEND_DIR="frontend"
UI_DIR="ui"

echo "🔄 开始同步主前端到 UI 备份..."

# 创建备份
echo "💾 创建备份..."
timestamp=$(date +%Y%m%d_%H%M%S)
cp -r $UI_DIR $UI_DIR.backup.$timestamp

# 同步核心文件
echo "📦 同步核心文件..."
rsync -av --exclude='experimental' --exclude='backup.*' $FRONTEND_DIR/ $UI_DIR/

# 保留实验性功能
echo "🧪 保留实验性功能..."
if [ -d "$UI_DIR.backup.$timestamp/components/experimental" ]; then
    cp -r $UI_DIR.backup.$timestamp/components/experimental $UI_DIR/components/
fi

echo "✅ 同步完成!"

🧪 A/B 测试和实验

A/B 测试框架

// lib/ab-testing.ts
interface ABTest {
  name: string
  variants: {
    control: React.ComponentType<any>
    treatment: React.ComponentType<any>
  }
  percentage: number // 0-100
  enabled: boolean
}

class ABTestManager {
  private tests: Map<string, ABTest> = new Map()
  
  registerTest(test: ABTest) {
    this.tests.set(test.name, test)
  }
  
  getVariant(testName: string): 'control' | 'treatment' | null {
    const test = this.tests.get(testName)
    if (!test || !test.enabled) return null
    
    // 基于用户 ID 或 session 的一致性分组
    const hash = this.hashString(testName + this.getUserId())
    const percentage = hash % 100
    
    return percentage < test.percentage ? 'treatment' : 'control'
  }
  
  renderComponent(testName: string, props: any) {
    const test = this.tests.get(testName)
    if (!test) return null
    
    const variant = this.getVariant(testName)
    if (variant === 'treatment') {
      return <test.variants.treatment {...props} />
    }
    return <test.variants.control {...props} />
  }
  
  private hashString(str: string): number {
    let hash = 0
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i)
      hash = ((hash << 5) - hash) + char
      hash = hash & hash // Convert to 32-bit integer
    }
    return Math.abs(hash)
  }
  
  private getUserId(): string {
    // 实现用户 ID 获取逻辑
    return sessionStorage.getItem('userId') || 'anonymous'
  }
}

export const abTestManager = new ABTestManager()

// 使用示例
abTestManager.registerTest({
  name: 'photo-gallery-layout',
  variants: {
    control: OriginalPhotoGallery,
    treatment: NewPhotoGallery
  },
  percentage: 50,
  enabled: true
})

实验性功能管理

// components/experimental/feature-flag.tsx
import { experimentalFeatures } from '@/lib/experimental'

interface FeatureFlagProps {
  feature: keyof typeof experimentalFeatures
  children: React.ReactNode
  fallback?: React.ReactNode
}

export function FeatureFlag({ feature, children, fallback = null }: FeatureFlagProps) {
  if (experimentalFeatures[feature]) {
    return <>{children}</>
  }
  
  return <>{fallback}</>
}

// 使用示例
<FeatureFlag feature="newPhotoGallery" fallback={<OriginalPhotoGallery />}>
  <NewPhotoGallery />
</FeatureFlag>

🚀 构建和部署

构建配置

{
  "scripts": {
    "dev": "next dev -p 3002",
    "build": "next build",
    "start": "next start -p 3002",
    "lint": "next lint",
    "type-check": "tsc --noEmit",
    "export": "next build && next export",
    "sync-from-frontend": "./scripts/sync-frontend-to-ui.sh",
    "sync-to-frontend": "./scripts/sync-ui-to-frontend.sh"
  }
}

Next.js 配置

// next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
  // 输出静态文件
  output: 'export',
  
  // 基础路径 (用于部署到子路径)
  basePath: '/ui',
  
  // 禁用图片优化 (静态导出)
  images: {
    unoptimized: true
  },
  
  // 实验性功能
  experimental: {
    appDir: true,
    serverComponentsExternalPackages: []
  },
  
  // 环境变量
  env: {
    NEXT_PUBLIC_UI_VERSION: process.env.npm_package_version || '1.0.0'
  }
}

export default nextConfig

部署配置

# 构建并部署到 UI 预览环境
npm run build
npm run export

# 部署到独立的 UI 预览域名
rsync -av out/ user@server:/var/www/ui-preview/

🔄 与其他模块的集成

与主前端的协作

  • 新功能在 UI 备份中实验和测试
  • 稳定后同步到主前端
  • 主前端的 bug 修复反向同步到 UI 备份

与管理后台的关系

  • 共享基础组件库
  • 管理后台可以复用 UI 备份中的组件
  • 保持设计系统的一致性

与后端的集成

  • 使用相同的 API 接口
  • 支持 Mock API 进行独立开发
  • 与后端版本保持兼容

🐛 问题排查

常见问题

  1. 组件不同步: 检查同步脚本和版本控制
  2. 样式冲突: 检查 Tailwind 配置和全局样式
  3. 依赖版本不一致: 检查 package.json 和锁文件
  4. 构建失败: 检查 TypeScript 配置和依赖

调试技巧

# 比较两个版本的差异
diff -r frontend/components ui/components

# 检查依赖差异
diff frontend/package.json ui/package.json

# 清理缓存
rm -rf ui/.next ui/node_modules
cd ui && pnpm install

📊 监控和分析

性能监控

// lib/performance.ts
export function measurePerformance(name: string, fn: () => void) {
  const start = performance.now()
  fn()
  const end = performance.now()
  
  console.log(`${name} took ${end - start} milliseconds`)
  
  // 发送到分析服务
  if (typeof window !== 'undefined' && (window as any).gtag) {
    (window as any).gtag('event', 'timing_complete', {
      name: name,
      value: Math.round(end - start)
    })
  }
}

实验数据收集

// lib/analytics.ts
export function trackExperiment(testName: string, variant: string) {
  if (typeof window !== 'undefined' && (window as any).gtag) {
    (window as any).gtag('event', 'experiment_view', {
      experiment_id: testName,
      variant_id: variant
    })
  }
}

export function trackConversion(testName: string, variant: string, action: string) {
  if (typeof window !== 'undefined' && (window as any).gtag) {
    (window as any).gtag('event', 'experiment_conversion', {
      experiment_id: testName,
      variant_id: variant,
      action: action
    })
  }
}

🔮 未来规划

功能扩展

  • 🔧 可视化组件编辑器
  • 📊 实验结果分析面板
  • 🎨 设计系统自动化
  • 🤖 AI 辅助组件生成
  • 📱 响应式设计测试工具

工作流优化

  • 自动化同步流程
  • 持续集成测试
  • 可视化差异对比
  • 自动化 A/B 测试报告

📚 参考资料


💡 使用提示: UI 备份模块主要用于实验和备份,不建议在生产环境中直接使用。所有稳定的功能都应该同步到主前端模块。在进行实验时,请确保做好数据备份,避免影响主前端的稳定性。