Files
novalon-website/docs/plans/2026-02-13-website-redesign-implementation-plan.md
T

33 KiB
Raw Blame History

睿新致远官网重构实施计划

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 基于优秀案例参考,全面升级睿新致远官网,融合印章文化与现代科技,提升品牌形象和转化率

Architecture: 采用 Next.js 16 App Router + React 19 + TypeScript 技术栈,基于现有组件库扩展,优化 Hero 区域印章动画,新增技术洞察板块和客户评价模块,全面提升用户体验和转化路径

Tech Stack: Next.js 16.1.6, React 19.2.3, TypeScript 5, Tailwind CSS v4, Framer Motion 12.29.2, shadcn/ui


实施策略

优先级说明

P0(核心功能) - 必须完成,直接影响品牌形象和转化 P1(重要功能) - 显著提升用户体验和专业度 P2(增强功能) - 锦上添花,可后续迭代

开发原则

  • TDD(测试驱动开发):先写测试,再写实现
  • DRY(不重复):复用现有组件和工具函数
  • YAGNI(不过度设计):只实现当前需要的功能
  • 频繁提交:每个小功能点独立提交
  • 性能优先:关注 LCP、FID、CLS 指标

Phase 1: P0 核心功能优化(预计 5-7 天)

Task 1: 优化印章粒子动画组件

目标: 增强印章粒子动画的视觉效果和交互体验

Files:

  • Modify: src/components/effects/seal-particle.tsx
  • Create: src/components/effects/seal-animation-enhanced.tsx
  • Test: src/components/effects/__tests__/seal-animation-enhanced.test.tsx

Step 1: 创建增强版印章动画组件

创建文件:src/components/effects/seal-animation-enhanced.tsx

'use client';

import { useEffect, useRef, useCallback, useState } from 'react';

interface Particle {
  x: number;
  y: number;
  targetX: number;
  targetY: number;
  vx: number;
  vy: number;
  size: number;
  opacity: number;
  color: string;
  life: number;
  maxLife: number;
  stage: 'idle' | 'dispersing' | 'reforming';
}

interface SealAnimationEnhancedProps {
  width?: number;
  height?: number;
  particleCount?: number;
  colors?: string[];
  sealText?: string;
  animationStages?: boolean;
  onStageChange?: (stage: string) => void;
  className?: string;
}

export function SealAnimationEnhanced({
  width = 300,
  height = 300,
  particleCount = 150,
  colors = ['#C41E3A', '#D4A574', '#8B4513'],
  sealText = '睿新',
  animationStages = true,
  onStageChange,
  className = '',
}: SealAnimationEnhancedProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const particlesRef = useRef<Particle[]>([]);
  const animationRef = useRef<number | null>(null);
  const [currentStage, setCurrentStage] = useState<'idle' | 'dispersing' | 'reforming'>('idle');
  const stageTimerRef = useRef<NodeJS.Timeout | null>(null);

  const createSealShape = useCallback((width: number, height: number) => {
    const centerX = width / 2;
    const centerY = height / 2;
    const sealSize = Math.min(width, height) * 0.35;
    const particles: { x: number; y: number }[] = [];

    // 创建印章形状的粒子位置(简化版方形印章)
    for (let i = 0; i < particleCount; i++) {
      const angle = (i / particleCount) * Math.PI * 2;
      const radius = sealSize * (0.8 + Math.random() * 0.4);
      particles.push({
        x: centerX + Math.cos(angle) * radius * (Math.random() > 0.5 ? 1 : 0.8),
        y: centerY + Math.sin(angle) * radius * (Math.random() > 0.5 ? 1 : 0.8),
      });
    }

    return particles;
  }, [particleCount]);

  const createParticle = useCallback(
    (x: number, y: number, targetX: number, targetY: number): Particle => {
      const color = colors[Math.floor(Math.random() * colors.length)];
      const size = 2 + Math.random() * 3;
      const maxLife = 200 + Math.random() * 100;

      return {
        x,
        y,
        targetX,
        targetY,
        vx: (Math.random() - 0.5) * 2,
        vy: (Math.random() - 0.5) * 2,
        size,
        opacity: 0.6 + Math.random() * 0.4,
        color,
        life: 0,
        maxLife,
        stage: 'idle',
      };
    },
    [colors]
  );

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    canvas.width = width;
    canvas.height = height;

    const sealPositions = createSealShape(width, height);
    particlesRef.current = sealPositions.map((pos, i) =>
      createParticle(pos.x, pos.y, pos.x, pos.y)
    );

    // 动画阶段控制
    if (animationStages) {
      // 3秒后开始分散
      stageTimerRef.current = setTimeout(() => {
        setCurrentStage('dispersing');
        onStageChange?.('dispersing');
        
        particlesRef.current.forEach(p => {
          p.vx = (Math.random() - 0.5) * 4;
          p.vy = (Math.random() - 0.5) * 4;
          p.stage = 'dispersing';
        });

        // 2秒后开始重组
        setTimeout(() => {
          setCurrentStage('reforming');
          onStageChange?.('reforming');
          
          particlesRef.current.forEach(p => {
            p.stage = 'reforming';
          });

          // 3秒后回到idle
          setTimeout(() => {
            setCurrentStage('idle');
            onStageChange?.('idle');
          }, 3000);
        }, 2000);
      }, 3000);
    }

    const animate = () => {
      ctx.clearRect(0, 0, width, height);

      particlesRef.current.forEach((particle) => {
        if (particle.stage === 'reforming') {
          // 向目标位置移动
          const dx = particle.targetX - particle.x;
          const dy = particle.targetY - particle.y;
          particle.vx += dx * 0.02;
          particle.vy += dy * 0.02;
          particle.vx *= 0.95;
          particle.vy *= 0.95;
        }

        particle.x += particle.vx;
        particle.y += particle.vy;
        particle.life++;

        // 绘制粒子
        ctx.beginPath();
        ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
        ctx.fillStyle = particle.color;
        ctx.globalAlpha = particle.opacity;
        ctx.fill();
        ctx.globalAlpha = 1;
      });

      animationRef.current = requestAnimationFrame(animate);
    };

    animate();

    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
      if (stageTimerRef.current) {
        clearTimeout(stageTimerRef.current);
      }
    };
  }, [width, height, createSealShape, createParticle, animationStages, onStageChange]);

  return (
    <canvas
      ref={canvasRef}
      className={className}
      style={{ width, height }}
    />
  );
}

Step 2: 创建测试文件

创建文件:src/components/effects/__tests__/seal-animation-enhanced.test.tsx

import { render, screen } from '@testing-library/react';
import { SealAnimationEnhanced } from '../seal-animation-enhanced';

describe('SealAnimationEnhanced', () => {
  it('should render canvas element', () => {
    render(<SealAnimationEnhanced />);
    const canvas = screen.getByRole('img');
    expect(canvas).toBeInTheDocument();
  });

  it('should accept custom props', () => {
    const { container } = render(
      <SealAnimationEnhanced
        width={400}
        height={400}
        particleCount={200}
        colors={['#FF0000', '#00FF00']}
        sealText="测试"
        animationStages={false}
      />
    );
    const canvas = container.querySelector('canvas');
    expect(canvas).toHaveAttribute('width', '400');
    expect(canvas).toHaveAttribute('height', '400');
  });

  it('should call onStageChange callback', (done) => {
    const handleStageChange = (stage: string) => {
      expect(stage).toBe('dispersing');
      done();
    };

    render(
      <SealAnimationEnhanced
        animationStages={true}
        onStageChange={handleStageChange}
      />
    );
  });
});

Step 3: 运行测试

npm test src/components/effects/__tests__/seal-animation-enhanced.test.tsx

Expected: PASS

Step 4: 提交代码

git add src/components/effects/seal-animation-enhanced.tsx
git add src/components/effects/__tests__/seal-animation-enhanced.test.tsx
git commit -m "feat: add enhanced seal animation component with stages"

Task 2: 更新 Hero Section 使用增强版印章动画

目标: 在 Hero 区域集成增强版印章动画,提升视觉冲击力

Files:

  • Modify: src/components/sections/hero-section.tsx
  • Create: src/lib/constants.ts (如果不存在)

Step 1: 检查并创建 constants 文件

检查文件是否存在:src/lib/constants.ts

如果不存在,创建:

export const COMPANY_INFO = {
  name: '睿新致远',
  slogan: '以技术创新,铸就卓越未来',
  description: '专注于数字化转型与技术创新,为企业提供全方位的技术解决方案',
  foundedYear: 2014,
  teamSize: '50+',
};

export const STATS = [
  { value: '10+', label: '年技术积累' },
  { value: '200+', label: '成功案例' },
  { value: '50+', label: '技术专家' },
  { value: '99.5%', label: '客户满意度' },
];

export const NAV_LINKS = [
  { href: '#home', label: '首页' },
  { href: '#solutions', label: '解决方案' },
  { href: '#cases', label: '成功案例' },
  { href: '#about', label: '关于我们' },
  { href: '#contact', label: '联系我们' },
];

Step 2: 更新 Hero Section

修改文件:src/components/sections/hero-section.tsx

在文件顶部添加导入:

import { SealAnimationEnhanced } from '@/components/effects/seal-animation-enhanced';

在 Hero Section 组件中,找到现有的 SealParticle 组件,替换为:

{/* 增强版印章动画 */}
<div className="absolute right-0 top-1/2 -translate-y-1/2 hidden lg:block">
  <SealAnimationEnhanced
    width={400}
    height={400}
    particleCount={150}
    colors={['#C41E3A', '#D4A574', '#8B4513']}
    sealText="睿新"
    animationStages={true}
    onStageChange={(stage) => console.log('Animation stage:', stage)}
    className="opacity-80"
  />
</div>

Step 3: 测试更新后的 Hero Section

npm run dev

访问 http://localhost:3000,检查

  • 印章动画是否正常显示
  • 动画阶段是否按预期切换(idle → dispersing → reforming → idle
  • 移动端是否隐藏动画(lg:block

Step 4: 提交代码

git add src/components/sections/hero-section.tsx
git add src/lib/constants.ts
git commit -m "feat: integrate enhanced seal animation in hero section"

Task 3: 创建技术洞察卡片组件

目标: 创建可复用的技术洞察卡片组件,用于展示技术文章和白皮书

Files:

  • Create: src/components/ui/insight-card.tsx
  • Create: src/components/ui/__tests__/insight-card.test.tsx

Step 1: 创建 InsightCard 组件

创建文件:src/components/ui/insight-card.tsx

'use client';

import { Calendar, Clock, ArrowRight } from 'lucide-react';
import { Badge } from '@/components/ui/badge';

export interface InsightCardProps {
  title: string;
  excerpt: string;
  category: string;
  readTime: string;
  publishedAt: string;
  imageUrl?: string;
  href: string;
  featured?: boolean;
}

export function InsightCard({
  title,
  excerpt,
  category,
  readTime,
  publishedAt,
  imageUrl,
  href,
  featured = false,
}: InsightCardProps) {
  return (
    <article
      className={`
        group relative overflow-hidden rounded-lg border border-[#E5E5E5]/50
        bg-white transition-all duration-300 hover:shadow-lg
        ${featured ? 'md:col-span-2' : ''}
      `}
    >
      {imageUrl && (
        <div className="relative h-48 overflow-hidden">
          <img
            src={imageUrl}
            alt={title}
            className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
          />
          <div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" />
        </div>
      )}
      
      <div className="p-6">
        <div className="flex items-center gap-3 mb-3">
          <Badge variant="secondary" className="text-xs">
            {category}
          </Badge>
          <div className="flex items-center gap-1 text-xs text-[#737373]">
            <Clock className="w-3 h-3" />
            <span>{readTime}</span>
          </div>
        </div>

        <h3 className="text-lg font-semibold text-[#171717] mb-2 line-clamp-2 group-hover:text-[#C41E3A] transition-colors">
          {title}
        </h3>

        <p className="text-sm text-[#737373] mb-4 line-clamp-2">
          {excerpt}
        </p>

        <div className="flex items-center justify-between">
          <div className="flex items-center gap-1 text-xs text-[#A3A3A3]">
            <Calendar className="w-3 h-3" />
            <span>{publishedAt}</span>
          </div>

          <a
            href={href}
            className="inline-flex items-center gap-1 text-sm font-medium text-[#C41E3A] hover:gap-2 transition-all"
          >
            阅读更多
            <ArrowRight className="w-4 h-4" />
          </a>
        </div>
      </div>
    </article>
  );
}

Step 2: 创建测试文件

创建文件:src/components/ui/__tests__/insight-card.test.tsx

import { render, screen } from '@testing-library/react';
import { InsightCard } from '../insight-card';

describe('InsightCard', () => {
  const defaultProps = {
    title: '测试文章标题',
    excerpt: '这是一篇测试文章的摘要内容',
    category: '技术洞察',
    readTime: '5 分钟',
    publishedAt: '2026-02-13',
    href: '/insights/test-article',
  };

  it('should render card with all props', () => {
    render(<InsightCard {...defaultProps} />);
    
    expect(screen.getByText('测试文章标题')).toBeInTheDocument();
    expect(screen.getByText('这是一篇测试文章的摘要内容')).toBeInTheDocument();
    expect(screen.getByText('技术洞察')).toBeInTheDocument();
    expect(screen.getByText('5 分钟')).toBeInTheDocument();
    expect(screen.getByText('2026-02-13')).toBeInTheDocument();
  });

  it('should render image when imageUrl provided', () => {
    render(
      <InsightCard
        {...defaultProps}
        imageUrl="/test-image.jpg"
      />
    );
    
    const image = screen.getByRole('img');
    expect(image).toHaveAttribute('src', '/test-image.jpg');
  });

  it('should have correct link href', () => {
    render(<InsightCard {...defaultProps} />);
    
    const link = screen.getByRole('link', { name: /阅读更多/i });
    expect(link).toHaveAttribute('href', '/insights/test-article');
  });

  it('should apply featured class when featured prop is true', () => {
    const { container } = render(
      <InsightCard {...defaultProps} featured={true} />
    );
    
    const article = container.querySelector('article');
    expect(article).toHaveClass('md:col-span-2');
  });
});

Step 3: 运行测试

npm test src/components/ui/__tests__/insight-card.test.tsx

Expected: PASS

Step 4: 提交代码

git add src/components/ui/insight-card.tsx
git add src/components/ui/__tests__/insight-card.test.tsx
git commit -m "feat: add insight card component for tech insights section"

Task 4: 创建技术洞察板块组件

目标: 创建完整的技术洞察板块,展示最新技术文章和白皮书

Files:

  • Create: src/components/sections/insights-section.tsx
  • Create: src/components/sections/__tests__/insights-section.test.tsx
  • Modify: src/app/(marketing)/page.tsx

Step 1: 创建 InsightsSection 组件

创建文件:src/components/sections/insights-section.tsx

'use client';

import { useEffect, useState, useRef } from 'react';
import { InsightCard } from '@/components/ui/insight-card';
import { Button } from '@/components/ui/button';
import { ArrowRight } from 'lucide-react';

interface Insight {
  id: string;
  title: string;
  excerpt: string;
  category: string;
  readTime: string;
  publishedAt: string;
  imageUrl?: string;
  href: string;
  featured?: boolean;
}

const MOCK_INSIGHTS: Insight[] = [
  {
    id: '1',
    title: '2025年技术趋势:AI驱动的数字化转型',
    excerpt: '深入探讨人工智能如何重塑企业技术架构,以及如何把握AI时代的机遇',
    category: '技术趋势',
    readTime: '8 分钟',
    publishedAt: '2026-02-10',
    imageUrl: '/insights/ai-trend.jpg',
    href: '/insights/ai-trend-2025',
    featured: true,
  },
  {
    id: '2',
    title: '微服务架构最佳实践指南',
    excerpt: '从单体应用到微服务的演进之路,包含实战案例和避坑指南',
    category: '架构设计',
    readTime: '12 分钟',
    publishedAt: '2026-02-08',
    imageUrl: '/insights/microservices.jpg',
    href: '/insights/microservices-best-practices',
  },
  {
    id: '3',
    title: '云原生技术栈选型指南',
    excerpt: 'Kubernetes、Docker、Service Mesh等技术栈的深度对比与选型建议',
    category: '云原生',
    readTime: '10 分钟',
    publishedAt: '2026-02-05',
    imageUrl: '/insights/cloud-native.jpg',
    href: '/insights/cloud-native-stack-guide',
  },
];

export function InsightsSection() {
  const [isVisible, setIsVisible] = useState(false);
  const sectionRef = useRef<HTMLElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
        }
      },
      { threshold: 0.1 }
    );

    if (sectionRef.current) {
      observer.observe(sectionRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <section
      id="insights"
      ref={sectionRef}
      className="py-24 bg-[#FAFAFA]"
    >
      <div className="container-wide">
        <div
          className={`
            text-center mb-16
            opacity-0 translate-y-4
            ${isVisible ? 'animate-fade-in-up' : ''}
          `}
        >
          <h2 className="text-3xl sm:text-4xl font-semibold text-[#171717] mb-4">
            技术洞察
          </h2>
          <p className="text-lg text-[#737373] max-w-2xl mx-auto">
            分享前沿技术趋势、最佳实践和深度思考,助力企业数字化转型
          </p>
        </div>

        <div
          className={`
            grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12
            opacity-0 translate-y-4
            ${isVisible ? 'animate-fade-in-up stagger-1' : ''}
          `}
        >
          {MOCK_INSIGHTS.map((insight) => (
            <InsightCard key={insight.id} {...insight} />
          ))}
        </div>

        <div
          className={`
            text-center
            opacity-0 translate-y-4
            ${isVisible ? 'animate-fade-in-up stagger-2' : ''}
          `}
        >
          <Button
            variant="outline"
            size="lg"
            className="group"
            onClick={() => window.location.href = '/insights'}
          >
            查看全部洞察
            <ArrowRight className="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" />
          </Button>
        </div>
      </div>
    </section>
  );
}

Step 2: 创建测试文件

创建文件:src/components/sections/__tests__/insights-section.test.tsx

import { render, screen } from '@testing-library/react';
import { InsightsSection } from '../insights-section';

describe('InsightsSection', () => {
  it('should render section with title', () => {
    render(<InsightsSection />);
    
    expect(screen.getByText('技术洞察')).toBeInTheDocument();
    expect(screen.getByText(/分享前沿技术趋势/)).toBeInTheDocument();
  });

  it('should render insight cards', () => {
    render(<InsightsSection />);
    
    expect(screen.getByText('2025年技术趋势:AI驱动的数字化转型')).toBeInTheDocument();
    expect(screen.getByText('微服务架构最佳实践指南')).toBeInTheDocument();
    expect(screen.getByText('云原生技术栈选型指南')).toBeInTheDocument();
  });

  it('should render view all button', () => {
    render(<InsightsSection />);
    
    expect(screen.getByText('查看全部洞察')).toBeInTheDocument();
  });
});

Step 3: 更新首页集成技术洞察板块

修改文件:src/app/(marketing)/page.tsx

在导入部分添加:

import { InsightsSection } from '@/components/sections/insights-section';

HomePage 组件中,在 <CasesSection /> 之前添加:

<InsightsSection />

Step 4: 测试集成效果

npm run dev

访问 http://localhost:3000,检查

  • 技术洞察板块是否正确显示
  • 卡片布局是否响应式
  • 滚动动画是否触发

Step 5: 提交代码

git add src/components/sections/insights-section.tsx
git add src/components/sections/__tests__/insights-section.test.tsx
git add src/app/(marketing)/page.tsx
git commit -m "feat: add insights section to homepage"

Task 5: 创建客户评价组件

目标: 创建客户评价展示组件,增强社会证明

Files:

  • Create: src/components/ui/testimonial-card.tsx
  • Create: src/components/sections/testimonials-section.tsx
  • Modify: src/app/(marketing)/page.tsx

Step 1: 创建 TestimonialCard 组件

创建文件:src/components/ui/testimonial-card.tsx

'use client';

import { Quote } from 'lucide-react';

export interface TestimonialCardProps {
  quote: string;
  author: string;
  position: string;
  company: string;
  avatarUrl?: string;
  rating?: number;
}

export function TestimonialCard({
  quote,
  author,
  position,
  company,
  avatarUrl,
  rating = 5,
}: TestimonialCardProps) {
  return (
    <div className="relative p-8 rounded-lg border border-[#E5E5E5]/50 bg-white hover:shadow-md transition-shadow">
      <Quote className="absolute top-6 right-6 w-8 h-8 text-[#C41E3A]/10" />
      
      {rating > 0 && (
        <div className="flex gap-1 mb-4">
          {Array.from({ length: rating }).map((_, i) => (
            <svg
              key={i}
              className="w-4 h-4 text-[#C41E3A]"
              fill="currentColor"
              viewBox="0 0 20 20"
            >
              <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
            </svg>
          ))}
        </div>
      )}

      <blockquote className="text-base text-[#171717] mb-6 leading-relaxed">
        "{quote}"
      </blockquote>

      <div className="flex items-center gap-4">
        {avatarUrl && (
          <img
            src={avatarUrl}
            alt={author}
            className="w-12 h-12 rounded-full object-cover"
          />
        )}
        <div>
          <div className="font-semibold text-[#171717]">{author}</div>
          <div className="text-sm text-[#737373]">
            {position} · {company}
          </div>
        </div>
      </div>
    </div>
  );
}

Step 2: 创建 TestimonialsSection 组件

创建文件:src/components/sections/testimonials-section.tsx

'use client';

import { useEffect, useState, useRef } from 'react';
import { TestimonialCard } from '@/components/ui/testimonial-card';

interface Testimonial {
  id: string;
  quote: string;
  author: string;
  position: string;
  company: string;
  avatarUrl?: string;
  rating?: number;
}

const MOCK_TESTIMONIALS: Testimonial[] = [
  {
    id: '1',
    quote: '睿新致远团队的专业能力和服务态度让我们印象深刻,他们不仅交付了高质量的产品,还帮助我们建立了完善的技术体系。',
    author: '张总',
    position: 'CTO',
    company: '某金融科技公司',
    rating: 5,
  },
  {
    id: '2',
    quote: '在与睿新致远的合作中,我们感受到了他们对技术创新的执着追求。他们提供的解决方案极大地提升了我们的业务效率。',
    author: '李经理',
    position: '技术总监',
    company: '某制造企业',
    rating: 5,
  },
  {
    id: '3',
    quote: '感谢睿新致远团队的专业支持,他们帮助我们完成了数字化转型的关键一步,为未来的发展奠定了坚实基础。',
    author: '王总',
    position: '总经理',
    company: '某零售集团',
    rating: 5,
  },
];

export function TestimonialsSection() {
  const [isVisible, setIsVisible] = useState(false);
  const sectionRef = useRef<HTMLElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
        }
      },
      { threshold: 0.1 }
    );

    if (sectionRef.current) {
      observer.observe(sectionRef.current);
    }

    return () => observer.disconnect();
  }, []);

  return (
    <section
      ref={sectionRef}
      className="py-24 bg-white"
    >
      <div className="container-wide">
        <div
          className={`
            text-center mb-16
            opacity-0 translate-y-4
            ${isVisible ? 'animate-fade-in-up' : ''}
          `}
        >
          <h2 className="text-3xl sm:text-4xl font-semibold text-[#171717] mb-4">
            客户评价
          </h2>
          <p className="text-lg text-[#737373] max-w-2xl mx-auto">
            听听我们的客户怎么说
          </p>
        </div>

        <div
          className={`
            grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6
            opacity-0 translate-y-4
            ${isVisible ? 'animate-fade-in-up stagger-1' : ''}
          `}
        >
          {MOCK_TESTIMONIALS.map((testimonial) => (
            <TestimonialCard key={testimonial.id} {...testimonial} />
          ))}
        </div>
      </div>
    </section>
  );
}

Step 3: 更新首页集成客户评价板块

修改文件:src/app/(marketing)/page.tsx

在导入部分添加:

import { TestimonialsSection } from '@/components/sections/testimonials-section';

HomePage 组件中,在 <ContactSection /> 之前添加:

<TestimonialsSection />

Step 4: 测试集成效果

npm run dev

访问 http://localhost:3000,检查客户评价板块是否正确显示

Step 5: 提交代码

git add src/components/ui/testimonial-card.tsx
git add src/components/sections/testimonials-section.tsx
git add src/app/(marketing)/page.tsx
git commit -m "feat: add testimonials section to homepage"

Task 6: 优化联系表单

目标: 优化联系表单的用户体验和验证逻辑

Files:

  • Modify: src/components/sections/contact-section.tsx
  • Create: src/lib/validations/contact.ts

Step 1: 创建表单验证逻辑

创建文件:src/lib/validations/contact.ts

import { z } from 'zod';

export const contactFormSchema = z.object({
  name: z
    .string()
    .min(2, '姓名至少需要2个字符')
    .max(50, '姓名不能超过50个字符'),
  company: z
    .string()
    .min(2, '公司名称至少需要2个字符')
    .max(100, '公司名称不能超过100个字符'),
  email: z
    .string()
    .email('请输入有效的邮箱地址'),
  phone: z
    .string()
    .regex(/^1[3-9]\d{9}$/, '请输入有效的手机号码')
    .optional()
    .or(z.literal('')),
  message: z
    .string()
    .min(10, '留言内容至少需要10个字符')
    .max(500, '留言内容不能超过500个字符'),
});

export type ContactFormData = z.infer<typeof contactFormSchema>;

export function validateContactForm(data: unknown) {
  return contactFormSchema.safeParse(data);
}

Step 2: 更新 ContactSection 组件

修改文件:src/components/sections/contact-section.tsx

添加表单验证和提交逻辑(具体代码根据现有实现调整)

Step 3: 测试表单验证

npm run dev

测试各种表单输入场景

Step 4: 提交代码

git add src/lib/validations/contact.ts
git add src/components/sections/contact-section.tsx
git commit -m "feat: add form validation to contact section"

Phase 2: P1 重要功能优化(预计 3-4 天)

Task 7: 优化移动端体验

目标: 优化移动端的导航、动画和性能

Files:

  • Modify: src/components/layout/header.tsx
  • Modify: src/components/effects/seal-particle.tsx
  • Create: src/hooks/use-device-performance.ts

Step 1: 创建设备性能检测 Hook

创建文件:src/hooks/use-device-performance.ts

'use client';

import { useState, useEffect } from 'react';

export type PerformanceLevel = 'low' | 'medium' | 'high';

export function useDevicePerformance(): PerformanceLevel {
  const [performance, setPerformance] = useState<PerformanceLevel>('high');

  useEffect(() => {
    // 检测设备性能
    const checkPerformance = () => {
      // 检查 CPU 核心数
      const cores = navigator.hardwareConcurrency || 4;
      
      // 检查设备内存(如果可用)
      const memory = (navigator as any).deviceMemory || 8;
      
      // 检查是否为移动设备
      const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
        navigator.userAgent
      );

      // 检查是否启用了减少动画
      const prefersReducedMotion = window.matchMedia(
        '(prefers-reduced-motion: reduce)'
      ).matches;

      if (prefersReducedMotion) {
        setPerformance('low');
      } else if (isMobile && (cores < 4 || memory < 4)) {
        setPerformance('low');
      } else if (isMobile || cores < 8) {
        setPerformance('medium');
      } else {
        setPerformance('high');
      }
    };

    checkPerformance();
  }, []);

  return performance;
}

Step 2: 更新印章动画组件以支持性能降级

修改文件:src/components/effects/seal-particle.tsx

添加性能检测和降级逻辑

Step 3: 优化移动端导航

修改文件:src/components/layout/header.tsx

优化移动端菜单交互

Step 4: 测试移动端体验

npm run dev

使用浏览器开发者工具测试移动端显示效果

Step 5: 提交代码

git add src/hooks/use-device-performance.ts
git add src/components/effects/seal-particle.tsx
git add src/components/layout/header.tsx
git commit -m "feat: optimize mobile experience with performance detection"

Task 8: 添加案例详情页优化

目标: 优化案例详情页的展示效果和用户体验

Files:

  • Modify: src/app/(marketing)/cases/[slug]/page.tsx (如果存在)
  • Create: src/components/ui/case-detail-card.tsx

Step 1: 创建案例详情卡片组件

创建文件:src/components/ui/case-detail-card.tsx

Step 2: 优化案例详情页布局

修改案例详情页文件

Step 3: 测试案例详情页

Step 4: 提交代码


Phase 3: P2 增强功能(预计 2-3 天)

Task 9: 添加深色模式支持

目标: 实现深色/浅色主题切换

Files:

  • Create: src/components/ui/theme-toggle.tsx
  • Modify: src/app/layout.tsx
  • Modify: src/app/globals.css

Step 1: 创建主题切换组件

Step 2: 更新全局样式

Step 3: 测试主题切换

Step 4: 提交代码


Task 10: 性能优化和测试

目标: 全面优化性能指标,确保达到预期目标

Files:

  • Modify: next.config.ts
  • Create: tests/performance/lighthouse.test.ts

Step 1: 配置性能优化

Step 2: 运行 Lighthouse 测试

Step 3: 优化关键指标

Step 4: 提交代码


验收标准

功能验收

  • Hero 区域印章动画正常工作,支持多阶段动画
  • 技术洞察板块正确展示,响应式布局良好
  • 客户评价板块正常显示,星级评分准确
  • 联系表单验证逻辑正确,错误提示清晰
  • 移动端体验流畅,动画降级正常

性能验收

  • LCP < 2.5秒
  • FID < 100毫秒
  • CLS < 0.1
  • 移动端首屏加载 < 3秒

代码质量验收

  • 所有测试通过
  • ESLint 检查无错误
  • TypeScript 类型检查通过
  • 代码提交信息规范

风险控制

潜在风险

  1. 动画性能风险

    • 缓解:提供降级方案,低端设备自动禁用复杂动画
  2. 开发周期风险

    • 缓解:采用分阶段交付,优先核心功能
  3. 测试覆盖风险

    • 缓解:每个组件都编写单元测试,集成测试覆盖关键路径

后续优化方向

  1. 数据分析驱动优化

    • 集成 Google Analytics 4
    • 用户行为热力图分析
    • A/B 测试关键页面
  2. 内容运营支持

    • 定期更新技术博客
    • 添加新案例展示
    • 优化 SEO 关键词
  3. 功能扩展预留

    • 多语言支持(国际化)
    • 客户案例视频展示
    • 在线演示预约系统

文档状态: 已完成
创建日期: 2026-02-13
预计完成时间: 10-14 天