Files
novalon-website/docs/superpowers/plans/2026-04-24-erp-product-landing-page.md
T
张翔 fe6e4b1c54 refactor: P0 - remove testimonial, migrate footer & mobile menu to NAVIGATION_V2
- Remove TestimonialSection from homepage (no customers yet)
- Footer: dark theme, NAVIGATION_V2 + MEGA_DROPDOWN_DATA links
- Mobile Menu: NAVIGATION_V2 with collapsible dropdown support
2026-04-30 22:00:00 +08:00

1262 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ERP 产品落地页实现计划
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
**目标:** 将 ERP 产品详情页改造为 Terminal 风格的全屏沉浸式落地页
**架构:** 组件化重构,每个 section 独立组件,复用现有 Framer Motion 动画库,通过客户端组件组合所有 section
**技术栈:** Next.js 16 + React 19 + Tailwind CSS v4 + Framer Motion
---
## 文件结构
```
新建文件:
src/components/products/
├── product-hero-section.tsx # Section 1: 全屏沉浸 Hero
├── product-overview-section.tsx # Section 2: 产品概述
├── product-features-section.tsx # Section 3: 核心功能(滚动叙事)
├── product-benefits-section.tsx # Section 4: 产品优势
├── product-process-section.tsx # Section 5: 实施流程
├── product-specs-section.tsx # Section 6: 技术规格
├── product-pricing-section.tsx # Section 7: 定价方案
├── product-cta-section.tsx # Section 8: CTA 横幅
└── index.ts # 统一导出
src/app/(marketing)/products/[id]/
└── product-detail-client.tsx # 客户端组件入口
修改文件:
src/app/(marketing)/products/[id]/page.tsx
src/components/layout/header.tsx
```
---
## 任务 1:创建产品 Hero Section 组件
**文件:**
- 创建:`src/components/products/product-hero-section.tsx`
- [ ] **步骤 1:创建 product-hero-section.tsx**
```tsx
'use client';
import { useEffect, useRef, useState } from 'react';
import { motion, useScroll, useTransform } from 'framer-motion';
import dynamic from 'next/dynamic';
import { MagneticButton } from '@/lib/animations';
import { ChevronDown } from 'lucide-react';
import type { Product } from '@/lib/constants/products';
const InkBackground = dynamic(
() => import('@/components/ui/ink-decoration').then(mod => ({ default: mod.InkBackground })),
{ ssr: false }
);
const DataParticleFlow = dynamic(
() => import('@/components/effects/data-particle-flow').then(mod => ({ default: mod.DataParticleFlow })),
{ ssr: false }
);
interface ProductHeroSectionProps {
product: Product;
}
export function ProductHeroSection({ product }: ProductHeroSectionProps) {
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="relative min-h-screen flex items-center justify-center overflow-hidden bg-[#0A0A0A]"
>
{/* 背景特效 */}
<InkBackground />
<DataParticleFlow
particleCount={80}
color="#C41E3A"
intensity="subtle"
shape="square"
effect="pulse"
/>
{/* 内容 */}
<div className="container-wide relative z-10 py-24 md:py-32">
<div className="max-w-4xl mx-auto text-center">
{/* 分类标签 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="inline-block px-4 py-2 bg-[#C41E3A]/20 rounded-full text-[#C41E3A] text-sm mb-6"
>
{product.category}
</motion.div>
{/* 产品名称 */}
<motion.h1
initial={{ opacity: 0, y: 30 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.8, delay: 0.2 }}
className="text-4xl md:text-6xl lg:text-7xl font-bold text-white mb-6"
>
{product.title}
</motion.h1>
{/* 价值主张 */}
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.4 }}
className="text-lg md:text-xl text-[#B0B0B0] leading-relaxed mb-10 max-w-2xl mx-auto"
>
{product.description}
</motion.p>
{/* CTA 按钮 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.6 }}
>
<MagneticButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-8 py-4 rounded-lg text-lg font-semibold"
>
</MagneticButton>
</motion.div>
</div>
</div>
{/* 滚动指示器 */}
<motion.div
initial={{ opacity: 0 }}
animate={isVisible ? { opacity: 1 } : {}}
transition={{ duration: 0.6, delay: 1 }}
className="absolute bottom-8 left-1/2 -translate-x-1/2"
>
<motion.div
animate={{ y: [0, 10, 0] }}
transition={{ duration: 1.5, repeat: Infinity, ease: 'easeInOut' }}
className="text-white/60"
>
<ChevronDown className="w-8 h-8" />
</motion.div>
</motion.div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-hero-section.tsx
git commit -m "feat(products): add product hero section component"
```
---
## 任务 2:创建产品概述 Section 组件
**文件:**
- 创建:`src/components/products/product-overview-section.tsx`
- [ ] **步骤 1:创建 product-overview-section.tsx**
```tsx
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { TextReveal } from '@/components/ui/scroll-animations';
import type { Product } from '@/lib/constants/products';
interface ProductOverviewSectionProps {
product: Product;
}
export function ProductOverviewSection({ product }: ProductOverviewSectionProps) {
const ref = useRef<HTMLElement>(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section
ref={ref}
className="relative py-24 md:py-32 bg-white overflow-hidden"
>
<div className="container-wide">
<div className="max-w-4xl mx-auto">
{/* 标题 */}
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-[#1C1C1C] mb-8 text-center"
>
</motion.h2>
{/* 概述文字 - 逐词揭示 */}
<TextReveal
text={product.overview}
className="text-lg md:text-xl text-[#5C5C5C] leading-relaxed text-center"
/>
</div>
</div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-overview-section.tsx
git commit -m "feat(products): add product overview section component"
```
---
## 任务 3:创建核心功能 Section 组件(滚动叙事)
**文件:**
- 创建:`src/components/products/product-features-section.tsx`
- [ ] **步骤 1:创建 product-features-section.tsx**
```tsx
'use client';
import { motion, useInView, useScroll, useTransform } from 'framer-motion';
import { useRef } from 'react';
import type { Product } from '@/lib/constants/products';
interface ProductFeaturesSectionProps {
product: Product;
}
function FeatureItem({
feature,
index,
}: {
feature: string;
index: number;
}) {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true, margin: '-50px' });
// 解析功能标题和描述
const [title, ...descParts] = feature.split('');
const description = descParts.join('');
// 编号格式化
const number = String(index + 1).padStart(2, '0');
return (
<div
ref={ref}
className="min-h-[60vh] flex items-center py-16"
>
<div className="container-wide">
<div className="max-w-6xl mx-auto grid md:grid-cols-2 gap-12 items-center">
{/* 左侧:编号和文字 */}
<div className="order-2 md:order-1">
{/* 编号 */}
<motion.span
initial={{ opacity: 0, x: -30 }}
animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.6 }}
className="block text-7xl md:text-8xl font-mono text-[#C41E3A]/20 mb-4"
>
{number}
</motion.span>
{/* 功能标题 */}
<motion.h3
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-2xl md:text-3xl font-bold text-white mb-4"
>
{title}
</motion.h3>
{/* 功能描述 */}
{description && (
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-lg text-[#B0B0B0] leading-relaxed"
>
{description}
</motion.p>
)}
</div>
{/* 右侧:动画占位区域 */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={isInView ? { opacity: 1, scale: 1 } : {}}
transition={{ duration: 0.8, delay: 0.3 }}
className="order-1 md:order-2 aspect-square rounded-2xl bg-gradient-to-br from-[#1A1A1A] to-[#0A0A0A] border border-[#2A2A2A] flex items-center justify-center"
>
<div className="w-24 h-24 rounded-full bg-[#C41E3A]/20 flex items-center justify-center">
<div className="w-12 h-12 rounded-full bg-[#C41E3A]/40" />
</div>
</motion.div>
</div>
</div>
</div>
);
}
export function ProductFeaturesSection({ product }: ProductFeaturesSectionProps) {
const sectionRef = useRef<HTMLElement>(null);
return (
<section
ref={sectionRef}
className="relative bg-[#0A0A0A] overflow-hidden"
>
{/* 标题 */}
<div className="pt-24 md:pt-32 pb-8">
<div className="container-wide">
<motion.h2
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-white text-center mb-4"
>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg text-[#B0B0B0] text-center max-w-2xl mx-auto"
>
</motion.p>
</div>
</div>
{/* 功能列表 */}
{product.features.map((feature, index) => (
<FeatureItem key={index} feature={feature} index={index} />
))}
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-features-section.tsx
git commit -m "feat(products): add product features section with scroll storytelling"
```
---
## 任务 4:创建产品优势 Section 组件
**文件:**
- 创建:`src/components/products/product-benefits-section.tsx`
- [ ] **步骤 1:创建 product-benefits-section.tsx**
```tsx
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { StaggerReveal, StaggerItem, CounterWithEffect } from '@/lib/animations';
import type { Product } from '@/lib/constants/products';
interface ProductBenefitsSectionProps {
product: Product;
}
// 解析优势中的数字
function extractNumber(text: string): { number: number; suffix: string } | null {
const match = text.match(/(\d+)%/);
if (match) {
return { number: parseInt(match[1], 10), suffix: '%' };
}
return null;
}
function BenefitCard({
benefit,
index,
}: {
benefit: string;
index: number;
}) {
const numberInfo = extractNumber(benefit);
return (
<StaggerItem className="p-6 md:p-8 bg-[#F5F7FA] rounded-2xl hover:bg-[#FFFBF5] transition-colors">
{numberInfo && (
<div className="mb-4">
<span className="text-4xl md:text-5xl font-bold text-[#C41E3A]">
<CounterWithEffect end={numberInfo.number} suffix={numberInfo.suffix} />
</span>
</div>
)}
<p className="text-lg text-[#1C1C1C] leading-relaxed">
{benefit}
</p>
</StaggerItem>
);
}
export function ProductBenefitsSection({ product }: ProductBenefitsSectionProps) {
const ref = useRef<HTMLElement>(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section
ref={ref}
className="relative py-24 md:py-32 bg-white overflow-hidden"
>
<div className="container-wide">
{/* 标题 */}
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-[#1C1C1C] mb-4 text-center"
>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg text-[#5C5C5C] text-center mb-12 max-w-2xl mx-auto"
>
</motion.p>
{/* 优势卡片网格 */}
<StaggerReveal className="grid md:grid-cols-2 gap-6 max-w-4xl mx-auto">
{product.benefits.map((benefit, index) => (
<BenefitCard key={index} benefit={benefit} index={index} />
))}
</StaggerReveal>
</div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-benefits-section.tsx
git commit -m "feat(products): add product benefits section with counter animation"
```
---
## 任务 5:创建实施流程 Section 组件
**文件:**
- 创建:`src/components/products/product-process-section.tsx`
- [ ] **步骤 1:创建 product-process-section.tsx**
```tsx
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import type { Product } from '@/lib/constants/products';
interface ProductProcessSectionProps {
product: Product;
}
function ProcessStep({
step,
index,
total,
}: {
step: string;
index: number;
total: number;
}) {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true, margin: '-50px' });
// 解析步骤标题和描述
const [title, ...descParts] = step.split('');
const description = descParts.join('');
return (
<motion.div
ref={ref}
initial={{ opacity: 0, x: -30 }}
animate={isInView ? { opacity: 1, x: 0 } : {}}
transition={{ duration: 0.6, delay: index * 0.1 }}
className="flex items-start gap-6"
>
{/* 编号圆圈 */}
<div className="flex-shrink-0">
<div className="w-12 h-12 rounded-full bg-[#C41E3A] flex items-center justify-center text-white font-bold text-lg">
{index + 1}
</div>
{index < total - 1 && (
<div className="w-0.5 h-16 bg-[#C41E3A]/20 ml-6 mt-2" />
)}
</div>
{/* 内容 */}
<div className="flex-1 pb-8">
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-2">
{title}
</h3>
{description && (
<p className="text-[#5C5C5C] leading-relaxed">
{description}
</p>
)}
</div>
</motion.div>
);
}
export function ProductProcessSection({ product }: ProductProcessSectionProps) {
const ref = useRef<HTMLElement>(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section
ref={ref}
className="relative py-24 md:py-32 bg-[#F5F5F5] overflow-hidden"
>
<div className="container-wide">
<div className="max-w-3xl mx-auto">
{/* 标题 */}
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-[#1C1C1C] mb-4 text-center"
>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg text-[#5C5C5C] text-center mb-12"
>
</motion.p>
{/* 步骤列表 */}
<div className="mt-8">
{product.process.map((step, index) => (
<ProcessStep
key={index}
step={step}
index={index}
total={product.process.length}
/>
))}
</div>
</div>
</div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-process-section.tsx
git commit -m "feat(products): add product process section with timeline"
```
---
## 任务 6:创建技术规格 Section 组件
**文件:**
- 创建:`src/components/products/product-specs-section.tsx`
- [ ] **步骤 1:创建 product-specs-section.tsx**
```tsx
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { StaggerReveal, StaggerItem } from '@/components/ui/scroll-animations';
import type { Product } from '@/lib/constants/products';
interface ProductSpecsSectionProps {
product: Product;
}
export function ProductSpecsSection({ product }: ProductSpecsSectionProps) {
const ref = useRef<HTMLElement>(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section
ref={ref}
className="relative py-24 md:py-32 bg-white overflow-hidden"
>
<div className="container-wide">
{/* 标题 */}
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-[#1C1C1C] mb-4 text-center"
>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg text-[#5C5C5C] text-center mb-12 max-w-2xl mx-auto"
>
</motion.p>
{/* 规格网格 */}
<StaggerReveal className="grid md:grid-cols-2 gap-4 max-w-3xl mx-auto">
{product.specs.map((spec, index) => (
<StaggerItem
key={index}
className="flex items-center gap-3 p-4 bg-[#F5F7FA] rounded-lg"
>
<div className="w-2 h-2 bg-[#C41E3A] rounded-full flex-shrink-0" />
<span className="text-[#1C1C1C]">{spec}</span>
</StaggerItem>
))}
</StaggerReveal>
</div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-specs-section.tsx
git commit -m "feat(products): add product specs section"
```
---
## 任务 7:创建定价方案 Section 组件
**文件:**
- 创建:`src/components/products/product-pricing-section.tsx`
- [ ] **步骤 1:创建 product-pricing-section.tsx**
```tsx
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { StaggerReveal, StaggerItem } from '@/components/ui/scroll-animations';
import { MagneticButton } from '@/lib/animations';
import { Check } from 'lucide-react';
import type { Product } from '@/lib/constants/products';
interface ProductPricingSectionProps {
product: Product;
}
const pricingFeatures = {
base: ['基础功能模块', '邮件支持', '标准报表'],
standard: ['全部功能模块', '电话支持', '自定义报表', '优先响应'],
enterprise: ['全部功能模块', '专属客服', '定制开发', 'SLA保障'],
};
function PricingCard({
name,
price,
features,
isRecommended = false,
}: {
name: string;
price: string;
features: string[];
isRecommended?: boolean;
}) {
const ref = useRef<HTMLDivElement>(null);
const isInView = useInView(ref, { once: true, margin: '-50px' });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 30 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className={`
relative p-6 md:p-8 rounded-2xl
${isRecommended
? 'bg-gradient-to-br from-[#C41E3A] to-[#A01830] text-white'
: 'bg-[#1A1A1A] border border-[#2A2A2A]'
}
`}
>
{/* 推荐标签 */}
{isRecommended && (
<div className="absolute -top-3 left-1/2 -translate-x-1/2 bg-white text-[#C41E3A] px-4 py-1 rounded-full text-sm font-semibold">
</div>
)}
{/* 名称 */}
<h3 className={`text-xl font-semibold mb-2 ${isRecommended ? 'text-white' : 'text-white'}`}>
{name}
</h3>
{/* 价格 */}
<p className={`text-3xl font-bold mb-6 ${isRecommended ? 'text-white' : 'text-[#C41E3A]'}`}>
{price}
</p>
{/* 功能列表 */}
<ul className="space-y-3 mb-8">
{features.map((feature, index) => (
<li key={index} className="flex items-center gap-2">
<Check className={`w-5 h-5 ${isRecommended ? 'text-white' : 'text-[#C41E3A]'}`} />
<span className={isRecommended ? 'text-white/90' : 'text-[#B0B0B0]'}>
{feature}
</span>
</li>
))}
</ul>
{/* 按钮 */}
<MagneticButton
href="/contact"
className={`
w-full py-3 rounded-lg font-semibold text-center
${isRecommended
? 'bg-white text-[#C41E3A] hover:bg-white/90'
: 'bg-[#C41E3A] text-white hover:bg-[#A01830]'
}
`}
>
</MagneticButton>
</motion.div>
);
}
export function ProductPricingSection({ product }: ProductPricingSectionProps) {
const ref = useRef<HTMLElement>(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section
ref={ref}
className="relative py-24 md:py-32 bg-[#0A0A0A] overflow-hidden"
>
<div className="container-wide">
{/* 标题 */}
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-white mb-4 text-center"
>
</motion.h2>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg text-[#B0B0B0] text-center mb-12 max-w-2xl mx-auto"
>
</motion.p>
{/* 定价卡片 */}
<div className="grid md:grid-cols-3 gap-6 max-w-5xl mx-auto">
<PricingCard
name="基础版"
price={product.pricing.base}
features={pricingFeatures.base}
/>
<PricingCard
name="标准版"
price={product.pricing.standard}
features={pricingFeatures.standard}
isRecommended
/>
<PricingCard
name="企业版"
price={product.pricing.enterprise}
features={pricingFeatures.enterprise}
/>
</div>
</div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-pricing-section.tsx
git commit -m "feat(products): add product pricing section with dark theme"
```
---
## 任务 8:创建 CTA Section 组件
**文件:**
- 创建:`src/components/products/product-cta-section.tsx`
- [ ] **步骤 1:创建 product-cta-section.tsx**
```tsx
'use client';
import { motion, useInView } from 'framer-motion';
import { useRef } from 'react';
import { MagneticButton } from '@/lib/animations';
export function ProductCTASection() {
const ref = useRef<HTMLElement>(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section
ref={ref}
className="relative py-24 md:py-32 bg-gradient-to-br from-[#C41E3A] to-[#A01830] overflow-hidden"
>
<div className="container-wide">
<div className="max-w-3xl mx-auto text-center">
{/* 标题 */}
<motion.h2
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-3xl md:text-4xl font-bold text-white mb-6"
>
</motion.h2>
{/* 描述 */}
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-lg text-white/90 mb-10"
>
</motion.p>
{/* 按钮 */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2 }}
className="flex flex-col sm:flex-row gap-4 justify-center"
>
<MagneticButton
href="/contact"
className="bg-white text-[#C41E3A] hover:bg-white/90 px-8 py-4 rounded-lg text-lg font-semibold"
>
</MagneticButton>
<MagneticButton
href="tel:+8613800138000"
className="bg-transparent border-2 border-white text-white hover:bg-white/10 px-8 py-4 rounded-lg text-lg font-semibold"
>
</MagneticButton>
</motion.div>
</div>
</div>
</section>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/product-cta-section.tsx
git commit -m "feat(products): add product CTA section"
```
---
## 任务 9:创建统一导出文件
**文件:**
- 创建:`src/components/products/index.ts`
- [ ] **步骤 1:创建 index.ts**
```tsx
export { ProductHeroSection } from './product-hero-section';
export { ProductOverviewSection } from './product-overview-section';
export { ProductFeaturesSection } from './product-features-section';
export { ProductBenefitsSection } from './product-benefits-section';
export { ProductProcessSection } from './product-process-section';
export { ProductSpecsSection } from './product-specs-section';
export { ProductPricingSection } from './product-pricing-section';
export { ProductCTASection } from './product-cta-section';
```
- [ ] **步骤 2Commit**
```bash
git add src/components/products/index.ts
git commit -m "feat(products): add products components index"
```
---
## 任务 10:创建产品详情客户端组件
**文件:**
- 创建:`src/app/(marketing)/products/[id]/product-detail-client.tsx`
- [ ] **步骤 1:创建 product-detail-client.tsx**
```tsx
'use client';
import { useEffect, useState } from 'react';
import dynamic from 'next/dynamic';
import { PRODUCTS, type Product } from '@/lib/constants/products';
// 动态导入所有 section 组件
const ProductHeroSection = dynamic(
() => import('@/components/products/product-hero-section').then(mod => ({ default: mod.ProductHeroSection })),
{ ssr: false }
);
const ProductOverviewSection = dynamic(
() => import('@/components/products/product-overview-section').then(mod => ({ default: mod.ProductOverviewSection })),
{ ssr: false }
);
const ProductFeaturesSection = dynamic(
() => import('@/components/products/product-features-section').then(mod => ({ default: mod.ProductFeaturesSection })),
{ ssr: false }
);
const ProductBenefitsSection = dynamic(
() => import('@/components/products/product-benefits-section').then(mod => ({ default: mod.ProductBenefitsSection })),
{ ssr: false }
);
const ProductProcessSection = dynamic(
() => import('@/components/products/product-process-section').then(mod => ({ default: mod.ProductProcessSection })),
{ ssr: false }
);
const ProductSpecsSection = dynamic(
() => import('@/components/products/product-specs-section').then(mod => ({ default: mod.ProductSpecsSection })),
{ ssr: false }
);
const ProductPricingSection = dynamic(
() => import('@/components/products/product-pricing-section').then(mod => ({ default: mod.ProductPricingSection })),
{ ssr: false }
);
const ProductCTASection = dynamic(
() => import('@/components/products/product-cta-section').then(mod => ({ default: mod.ProductCTASection })),
{ ssr: false }
);
interface ProductDetailClientProps {
productId: string;
}
export function ProductDetailClient({ productId }: ProductDetailClientProps) {
const [product, setProduct] = useState<Product | null>(null);
const [isDarkHeader, setIsDarkHeader] = useState(true);
useEffect(() => {
const found = PRODUCTS.find(p => p.id === productId);
setProduct(found || null);
}, [productId]);
// 监听滚动,控制导航栏颜色
useEffect(() => {
const handleScroll = () => {
const heroHeight = window.innerHeight;
setIsDarkHeader(window.scrollY < heroHeight * 0.8);
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
// 设置导航栏颜色(通过 CSS 变量或全局状态)
useEffect(() => {
document.documentElement.setAttribute('data-header-theme', isDarkHeader ? 'dark' : 'light');
}, [isDarkHeader]);
if (!product) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<p className="text-[#5C5C5C]">...</p>
</div>
);
}
return (
<main className="min-h-screen">
<ProductHeroSection product={product} />
<ProductOverviewSection product={product} />
<ProductFeaturesSection product={product} />
<ProductBenefitsSection product={product} />
<ProductProcessSection product={product} />
<ProductSpecsSection product={product} />
<ProductPricingSection product={product} />
<ProductCTASection />
</main>
);
}
```
- [ ] **步骤 2Commit**
```bash
git add src/app/\(marketing\)/products/\[id\]/product-detail-client.tsx
git commit -m "feat(products): add product detail client component"
```
---
## 任务 11:修改产品详情页面入口
**文件:**
- 修改:`src/app/(marketing)/products/[id]/page.tsx`
- [ ] **步骤 1:修改 page.tsx**
将现有内容替换为:
```tsx
import { notFound } from 'next/navigation';
import { PRODUCTS } from '@/lib/constants';
import { ProductDetailClient } from './product-detail-client';
export async function generateStaticParams() {
return PRODUCTS.map((product) => ({
id: product.id,
}));
}
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = PRODUCTS.find((p) => p.id === id);
if (!product) {
return {
title: '产品未找到',
};
}
return {
title: `${product.title} - 睿新致远`,
description: product.description,
};
}
export default async function ProductDetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const product = PRODUCTS.find((p) => p.id === id);
if (!product) {
notFound();
}
return <ProductDetailClient productId={id} />;
}
```
- [ ] **步骤 2Commit**
```bash
git add src/app/\(marketing\)/products/\[id\]/page.tsx
git commit -m "refactor(products): update product detail page to use client component"
```
---
## 任务 12:修改 Header 组件支持深色模式
**文件:**
- 修改:`src/components/layout/header.tsx`
- [ ] **步骤 1:修改 Header 组件**
`HeaderContent` 函数中,找到导航栏的 className 定义,修改为支持深色模式:
```tsx
// 在 HeaderContent 函数开头添加
const [headerTheme, setHeaderTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
const handleThemeChange = () => {
const theme = document.documentElement.getAttribute('data-header-theme');
setHeaderTheme(theme === 'dark' ? 'dark' : 'light');
};
// 初始检查
handleThemeChange();
// 使用 MutationObserver 监听属性变化
const observer = new MutationObserver(handleThemeChange);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-header-theme'],
});
return () => observer.disconnect();
}, []);
// 修改 header 的 className
<header
className={`
fixed top-0 left-0 right-0 z-50
transition-all duration-300 ease-out
${headerTheme === 'dark'
? isScrolled
? 'bg-[#0A0A0A]/90 backdrop-blur-xl border-b border-[#1A1A1A]'
: 'bg-transparent'
: isScrolled
? 'bg-white/95 backdrop-blur-xl border-b border-[#E2E8F0] shadow-sm'
: 'bg-transparent'
}
`}
>
```
同时修改 Logo 和导航链接的颜色:
```tsx
// Logo 颜色
<Image
src={headerTheme === 'dark' ? '/logo-white.svg' : '/logo.svg'}
alt={COMPANY_INFO.name}
// ... 其他属性
/>
// 导航链接颜色
<StaticLink
href={item.href}
className={`
...
${headerTheme === 'dark' ? 'text-white' : 'text-[#1C1C1C]'}
...
`}
>
```
- [ ] **步骤 2Commit**
```bash
git add src/components/layout/header.tsx
git commit -m "feat(layout): add dark mode support to header component"
```
---
## 任务 13:验证构建
- [ ] **步骤 1:运行构建**
```bash
npm run build
```
预期:构建成功,无错误
- [ ] **步骤 2:本地预览**
```bash
npm run start
```
访问 `http://localhost:3000/products/erp` 验证效果
- [ ] **步骤 3:最终 Commit**
```bash
git add -A
git commit -m "feat(products): complete ERP product landing page with Terminal-style design"
```
---
## 规格覆盖度检查
| 规格章节 | 对应任务 |
|---------|---------|
| Section 1: 全屏沉浸 Hero | 任务 1 |
| Section 2: 产品概述 | 任务 2 |
| Section 3: 核心功能(滚动叙事) | 任务 3 |
| Section 4: 产品优势 | 任务 4 |
| Section 5: 实施流程 | 任务 5 |
| Section 6: 技术规格 | 任务 6 |
| Section 7: 定价方案 | 任务 7 |
| Section 8: CTA 横幅 | 任务 8 |
| 导航栏动态变色 | 任务 12 |
| 文件结构 | 任务 9, 10, 11 |
✅ 所有规格需求已覆盖
---
**计划版本**: 1.0
**最后更新**: 2026-04-24