fe6e4b1c54
- Remove TestimonialSection from homepage (no customers yet) - Footer: dark theme, NAVIGATION_V2 + MEGA_DROPDOWN_DATA links - Mobile Menu: NAVIGATION_V2 with collapsible dropdown support
1262 lines
35 KiB
Markdown
1262 lines
35 KiB
Markdown
# 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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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';
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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>
|
||
);
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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} />;
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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]'}
|
||
...
|
||
`}
|
||
>
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```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
|