33 KiB
睿新致远官网重构实施计划
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
- 印章动画是否正常显示
- 动画阶段是否按预期切换(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
- 技术洞察板块是否正确显示
- 卡片布局是否响应式
- 滚动动画是否触发
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 类型检查通过
- 代码提交信息规范
风险控制
潜在风险
-
动画性能风险
- 缓解:提供降级方案,低端设备自动禁用复杂动画
-
开发周期风险
- 缓解:采用分阶段交付,优先核心功能
-
测试覆盖风险
- 缓解:每个组件都编写单元测试,集成测试覆盖关键路径
后续优化方向
-
数据分析驱动优化
- 集成 Google Analytics 4
- 用户行为热力图分析
- A/B 测试关键页面
-
内容运营支持
- 定期更新技术博客
- 添加新案例展示
- 优化 SEO 关键词
-
功能扩展预留
- 多语言支持(国际化)
- 客户案例视频展示
- 在线演示预约系统
文档状态: 已完成
创建日期: 2026-02-13
预计完成时间: 10-14 天