Files
novalon-website/docs/superpowers/plans/2026-04-30-atlassian-brand-fusion-redesign.md
张翔 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

1777 lines
53 KiB
Markdown
Raw Permalink 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.
# Atlassian 风格品牌融合重构 实现计划
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
**目标:** 借鉴 Atlassian 信息架构,重构 Novalon 首页为"收敛但保留印记"的品牌融合风格,同步沉淀 6 个核心组件作为设计系统基础。
**架构:** 保持 Next.js 16 App Router + 静态导出架构不变。首页从单文件长滚动重构为 6 个独立 Section 组件 + 新导航组件。动效从粒子/水墨收敛为微交互。导航从 8 项平铺改为 4 核心 + 2 下拉。
**技术栈:** Next.js 16 + React 19 + Tailwind CSS 4 + Framer Motion 12 + Lucide React
---
## 整体架构图
```mermaid
graph TB
subgraph "Phase 1: 首页重构 + 设计系统基础"
T1[任务1: Design Token 更新] --> T2[任务2: 导航常量重构]
T2 --> T3[任务3: MegaDropdown 组件]
T3 --> T4[任务4: Header 重构]
T4 --> T5[任务5: Hero Section 重构]
T5 --> T6[任务6: StatsBar 组件]
T6 --> T7[任务7: 社会证明 Section]
T7 --> T8[任务8: ProductCard 组件]
T8 --> T9[任务9: 产品矩阵 Section]
T9 --> T10[任务10: ChallengeCard 组件]
T10 --> T11[任务11: 挑战场景 Section]
T11 --> T12[任务12: TestimonialBlock 组件]
T12 --> T13[任务13: 客户成果 Section]
T13 --> T14[任务14: CTASection 组件]
T14 --> T15[任务15: CTA Section]
T15 --> T16[任务16: 首页组装]
T16 --> T17[任务17: 废弃动效清理]
T17 --> T18[任务18: 集成测试与验证]
end
```
## 组件依赖图
```mermaid
graph LR
subgraph "基础层"
DT[Design Tokens<br/>globals.css]
NAV_CONST[Navigation Constants<br/>navigation.ts]
end
subgraph "组件层"
MD[MegaDropdown<br/>mega-dropdown.tsx]
PC[ProductCard<br/>product-card.tsx]
CC[ChallengeCard<br/>challenge-card.tsx]
SB[StatsBar<br/>stats-bar.tsx]
TB[TestimonialBlock<br/>testimonial-block.tsx]
CTA[CTASection<br/>cta-section.tsx]
end
subgraph "Section 层"
HDR[Header<br/>header.tsx]
HERO[HeroSection<br/>hero-section.tsx]
SOCIAL[SocialProofSection<br/>social-proof-section.tsx]
PROD[ProductMatrixSection<br/>product-matrix-section.tsx]
CHAL[ChallengeSection<br/>challenge-section.tsx]
TEST[TestimonialSection<br/>testimonial-section.tsx]
CTAS[CTASectionPage<br/>cta-section-page.tsx]
end
subgraph "页面层"
HOME[home-content.tsx]
end
DT --> MD & PC & CC & SB & TB & CTA
NAV_CONST --> MD
MD --> HDR
PC --> PROD
CC --> CHAL
SB --> SOCIAL
TB --> TEST
CTA --> CTAS
HDR & HERO & SOCIAL & PROD & CHAL & TEST & CTAS --> HOME
```
## 数据流图
```mermaid
graph TD
subgraph "数据源 (src/lib/constants/)"
NAV[navigation.ts<br/>NAVIGATION_V2]
PRODS[products.ts<br/>PRODUCTS]
SOL[solutions.ts<br/>SOLUTIONS]
CASES[cases.ts<br/>CASES]
STATS[stats.ts<br/>STATS]
end
NAV -->|导航项+下拉数据| HDR_COMP[Header]
PRODS -->|4个产品| PROD_SEC[ProductMatrixSection]
SOL -->|3个挑战场景| CHAL_SEC[ChallengeSection]
CASES -->|testimonial+results| TEST_SEC[TestimonialSection]
STATS -->|3个关键数据| SOCIAL_SEC[SocialProofSection]
```
## 文件结构
### 新建文件
| 文件 | 职责 |
|------|------|
| `src/components/layout/mega-dropdown.tsx` | 产品/解决方案矩阵下拉导航组件 |
| `src/components/layout/mega-dropdown.test.tsx` | 下拉导航测试 |
| `src/components/ui/product-card.tsx` | 朱砂红左边框产品卡片 |
| `src/components/ui/product-card.test.tsx` | 产品卡片测试 |
| `src/components/ui/challenge-card.tsx` | 痛点/场景入口卡片 |
| `src/components/ui/challenge-card.test.tsx` | 场景卡片测试 |
| `src/components/ui/stats-bar.tsx` | 三列数据统计条 |
| `src/components/ui/stats-bar.test.tsx` | 统计条测试 |
| `src/components/ui/testimonial-block.tsx` | 深色背景客户证言 |
| `src/components/ui/testimonial-block.test.tsx` | 客户证言测试 |
| `src/components/ui/cta-section.tsx` | 朱砂红渐变行动号召 |
| `src/components/ui/cta-section.test.tsx` | CTA 测试 |
| `src/components/sections/hero-section-v2.tsx` | 新 Hero Section |
| `src/components/sections/hero-section-v2.test.tsx` | 新 Hero 测试 |
| `src/components/sections/social-proof-section.tsx` | 社会证明 Section |
| `src/components/sections/social-proof-section.test.tsx` | 社会证明测试 |
| `src/components/sections/product-matrix-section.tsx` | 产品矩阵 Section |
| `src/components/sections/product-matrix-section.test.tsx` | 产品矩阵测试 |
| `src/components/sections/challenge-section.tsx` | 挑战场景 Section |
| `src/components/sections/challenge-section.test.tsx` | 挑战场景测试 |
| `src/components/sections/testimonial-section.tsx` | 客户成果 Section |
| `src/components/sections/testimonial-section.test.tsx` | 客户成果测试 |
| `src/components/sections/cta-section-page.tsx` | CTA Section |
| `src/components/sections/cta-section-page.test.tsx` | CTA Section 测试 |
### 修改文件
| 文件 | 变更内容 |
|------|---------|
| `src/app/globals.css` | 新增场景色 Token、废弃动效相关 CSS |
| `src/lib/constants/navigation.ts` | 新增 NAVIGATION_V2 常量 + MegaDropdown 数据结构 |
| `src/lib/constants/stats.ts` | 新增 HOME_STATS 三列数据 |
| `src/components/layout/header.tsx` | 集成 MegaDropdown,使用 NAVIGATION_V2 |
| `src/app/(marketing)/home-content.tsx` | 替换为新 Section 组件 |
| `src/components/sections/hero-section.tsx` | 保留旧版,新版本为 hero-section-v2.tsx |
---
## 任务 1Design Token 更新
**文件:**
- 修改:`src/app/globals.css`
- [ ] **步骤 1:在 `:root` 中新增场景色变量**
`globals.css``:root` 块中,在 `/* 状态色 */` 注释之后添加:
```css
/* 场景色 - 挑战卡片 */
--color-challenge-isolation: #FEF2F4;
--color-challenge-isolation-hover: #FDE8EC;
--color-challenge-growth: #FFFBEB;
--color-challenge-growth-hover: #FEF3C7;
--color-challenge-compliance: #F0FDF4;
--color-challenge-compliance-hover: #DCFCE7;
```
- [ ] **步骤 2:验证 CSS 变量可用**
运行:`npx tsc --noEmit`
预期:无类型错误
- [ ] **步骤 3Commit**
```bash
git add src/app/globals.css
git commit -m "feat: add challenge scenario color tokens to design system"
```
---
## 任务 2:导航常量重构
**文件:**
- 修改:`src/lib/constants/navigation.ts`
- [ ] **步骤 1:编写失败的测试**
`src/lib/constants/navigation.test.ts`(新建)中:
```typescript
import { describe, it, expect } from '@jest/globals';
import { NAVIGATION_V2, MEGA_DROPDOWN_DATA } from './navigation';
describe('NAVIGATION_V2', () => {
it('has exactly 6 navigation items', () => {
expect(NAVIGATION_V2).toHaveLength(6);
});
it('contains product dropdown item', () => {
const productItem = NAVIGATION_V2.find(item => item.id === 'products');
expect(productItem).toBeDefined();
expect(productItem?.hasDropdown).toBe(true);
});
it('contains solutions dropdown item', () => {
const solutionsItem = NAVIGATION_V2.find(item => item.id === 'solutions');
expect(solutionsItem).toBeDefined();
expect(solutionsItem?.hasDropdown).toBe(true);
});
});
describe('MEGA_DROPDOWN_DATA', () => {
it('has products dropdown with 4 items', () => {
expect(MEGA_DROPDOWN_DATA.products).toHaveLength(4);
});
it('has solutions dropdown with 4 items', () => {
expect(MEGA_DROPDOWN_DATA.solutions).toHaveLength(4);
});
it('each product has id, title, description, href', () => {
MEGA_DROPDOWN_DATA.products.forEach(product => {
expect(product).toHaveProperty('id');
expect(product).toHaveProperty('title');
expect(product).toHaveProperty('description');
expect(product).toHaveProperty('href');
});
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/lib/constants/navigation.test.ts --no-coverage`
预期:FAIL — `Cannot find module './navigation'``NAVIGATION_V2 is not exported`
- [ ] **步骤 3:实现导航常量**
`src/lib/constants/navigation.ts` 中追加:
```typescript
export interface NavigationItemV2 {
id: string;
label: string;
href: string;
hasDropdown?: boolean;
dropdownKey?: string;
}
export interface MegaDropdownItem {
id: string;
title: string;
description: string;
href: string;
}
export interface MegaDropdownData {
[key: string]: MegaDropdownItem[];
}
export const NAVIGATION_V2: NavigationItemV2[] = [
{ id: 'products', label: '产品', href: '/products', hasDropdown: true, dropdownKey: 'products' },
{ id: 'solutions', label: '解决方案', href: '/solutions', hasDropdown: true, dropdownKey: 'solutions' },
{ id: 'services', label: '服务', href: '/services' },
{ id: 'cases', label: '案例', href: '/cases' },
{ id: 'about', label: '关于我们', href: '/about' },
{ id: 'contact', label: '联系我们', href: '/contact' },
];
export const MEGA_DROPDOWN_DATA: MegaDropdownData = {
products: [
{ id: 'erp', title: 'ERP 管理系统', description: '财务·采购·销售·库存·生产', href: '/products/erp' },
{ id: 'crm', title: 'CRM 客户管理', description: '线索·商机·合同·服务', href: '/products/crm' },
{ id: 'bi', title: 'BI 数据平台', description: '报表·仪表盘·预测·决策', href: '/products/bi' },
{ id: 'cms', title: 'CMS 内容平台', description: '建站·运营·分发·分析', href: '/products/cms' },
],
solutions: [
{ id: 'manufacturing', title: '制造业', description: '智能制造·MES·质量管控', href: '/solutions/manufacturing' },
{ id: 'retail', title: '零售业', description: '全渠道·会员·精准营销', href: '/solutions/retail' },
{ id: 'education', title: '教育行业', description: '招生·教学·学情分析', href: '/solutions/education' },
{ id: 'healthcare', title: '医疗健康', description: '临床·运营·患者服务', href: '/solutions/healthcare' },
],
};
```
- [ ] **步骤 4:更新 index.ts 导出**
`src/lib/constants/index.ts` 中追加导出:
```typescript
export { NAVIGATION_V2, MEGA_DROPDOWN_DATA } from './navigation';
export type { NavigationItemV2, MegaDropdownItem, MegaDropdownData } from './navigation';
```
- [ ] **步骤 5:运行测试验证通过**
运行:`npx jest src/lib/constants/navigation.test.ts --no-coverage`
预期:PASS
- [ ] **步骤 6Commit**
```bash
git add src/lib/constants/navigation.ts src/lib/constants/navigation.test.ts src/lib/constants/index.ts
git commit -m "feat: add NAVIGATION_V2 and MEGA_DROPDOWN_DATA for mega dropdown navigation"
```
---
## 任务 3MegaDropdown 组件
**文件:**
- 创建:`src/components/layout/mega-dropdown.tsx`
- 创建:`src/components/layout/mega-dropdown.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
// src/components/layout/mega-dropdown.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { MegaDropdown } from './mega-dropdown';
import { MEGA_DROPDOWN_DATA } from '@/lib/constants';
describe('MegaDropdown', () => {
it('renders trigger button with label', () => {
render(
<MegaDropdown
label="产品"
items={MEGA_DROPDOWN_DATA.products}
isOpen={false}
onToggle={vi.fn()}
/>
);
expect(screen.getByText('产品')).toBeInTheDocument();
});
it('shows dropdown content when isOpen is true', () => {
render(
<MegaDropdown
label="产品"
items={MEGA_DROPDOWN_DATA.products}
isOpen={true}
onToggle={vi.fn()}
/>
);
expect(screen.getByText('ERP 管理系统')).toBeInTheDocument();
});
it('hides dropdown content when isOpen is false', () => {
render(
<MegaDropdown
label="产品"
items={MEGA_DROPDOWN_DATA.products}
isOpen={false}
onToggle={vi.fn()}
/>
);
expect(screen.queryByText('ERP 管理系统')).not.toBeInTheDocument();
});
it('calls onToggle when trigger is clicked', () => {
const onToggle = vi.fn();
render(
<MegaDropdown
label="产品"
items={MEGA_DROPDOWN_DATA.products}
isOpen={false}
onToggle={onToggle}
/>
);
fireEvent.click(screen.getByText('产品'));
expect(onToggle).toHaveBeenCalledTimes(1);
});
it('renders each item with title, description and link', () => {
render(
<MegaDropdown
label="产品"
items={MEGA_DROPDOWN_DATA.products}
isOpen={true}
onToggle={vi.fn()}
/>
);
MEGA_DROPDOWN_DATA.products.forEach(item => {
expect(screen.getByText(item.title)).toBeInTheDocument();
expect(screen.getByText(item.description)).toBeInTheDocument();
});
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/layout/mega-dropdown.test.ts --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 MegaDropdown 组件**
```tsx
// src/components/layout/mega-dropdown.tsx
'use client';
import { useRef, useEffect } from 'react';
import { ChevronDown } from 'lucide-react';
import { AnimatePresence, motion } from 'framer-motion';
import { StaticLink } from '@/components/ui/static-link';
import type { MegaDropdownItem } from '@/lib/constants';
interface MegaDropdownProps {
label: string;
items: MegaDropdownItem[];
isOpen: boolean;
onToggle: () => void;
}
export function MegaDropdown({ label, items, isOpen, onToggle }: MegaDropdownProps) {
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
if (isOpen) { onToggle(); }
}
}
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [isOpen, onToggle]);
return (
<div ref={dropdownRef} className="relative">
<button
onClick={onToggle}
className="flex items-center gap-1 text-sm font-medium text-[#1C1C1C] hover:text-[#C41E3A] transition-colors duration-200"
aria-expanded={isOpen}
aria-haspopup="true"
>
{label}
<ChevronDown
className={`w-4 h-4 transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
/>
</button>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -8 }}
transition={{ duration: 0.15 }}
className="absolute top-full left-0 mt-2 w-[480px] bg-white rounded-xl border border-[#E5E5E5] shadow-lg p-4 z-50"
>
<div className="grid grid-cols-2 gap-2">
{items.map((item) => (
<StaticLink
key={item.id}
href={item.href}
className="block p-3 rounded-lg border-l-[3px] border-l-[#C41E3A] hover:bg-[#FFFBF5] transition-colors duration-200"
>
<div className="text-sm font-semibold text-[#1C1C1C]">{item.title}</div>
<div className="text-xs text-[#5C5C5C] mt-1">{item.description}</div>
</StaticLink>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/layout/mega-dropdown.test.ts --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/layout/mega-dropdown.tsx src/components/layout/mega-dropdown.test.tsx
git commit -m "feat: add MegaDropdown component for product/solution matrix navigation"
```
---
## 任务 4Header 重构
**文件:**
- 修改:`src/components/layout/header.tsx`
- [ ] **步骤 1:编写失败的测试**
`src/components/layout/header.test.tsx` 中追加测试:
```tsx
it('renders NAVIGATION_V2 items in desktop nav', () => {
render(<Header />);
expect(screen.getByText('产品')).toBeInTheDocument();
expect(screen.getByText('解决方案')).toBeInTheDocument();
expect(screen.getByText('服务')).toBeInTheDocument();
expect(screen.getByText('案例')).toBeInTheDocument();
expect(screen.getByText('关于我们')).toBeInTheDocument();
expect(screen.getByText('联系我们')).toBeInTheDocument();
});
it('does not render old navigation items (核心业务, 新闻动态)', () => {
render(<Header />);
expect(screen.queryByText('核心业务')).not.toBeInTheDocument();
expect(screen.queryByText('新闻动态')).not.toBeInTheDocument();
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/layout/header.test.tsx --no-coverage`
预期:FAIL — 新测试用例找不到"产品"等新导航项
- [ ] **步骤 3:重构 Header 组件**
`header.tsx` 中:
1.`NAVIGATION` import 替换为 `NAVIGATION_V2, MEGA_DROPDOWN_DATA`
2.`MegaDropdown` import 添加
3. 桌面导航渲染逻辑改为:遍历 `NAVIGATION_V2`,如果 `hasDropdown` 则渲染 `MegaDropdown`,否则渲染普通链接
4. 移除旧的 `NAVIGATION` 引用
5. 添加 `openDropdown` state 管理下拉开关
关键代码片段(替换桌面导航部分):
```tsx
const [openDropdown, setOpenDropdown] = useState<string | null>(null);
// 在导航渲染中:
{NAVIGATION_V2.map((item) => (
item.hasDropdown ? (
<MegaDropdown
key={item.id}
label={item.label}
items={MEGA_DROPDOWN_DATA[item.dropdownKey!]}
isOpen={openDropdown === item.id}
onToggle={() => setOpenDropdown(openDropdown === item.id ? null : item.id)}
/>
) : (
<StaticLink
key={item.id}
href={item.href}
className={`text-sm font-medium transition-colors duration-200 ${
activeSection === item.id ? 'text-[#C41E3A]' : 'text-[#1C1C1C] hover:text-[#C41E3A]'
}`}
>
{item.label}
</StaticLink>
)
))}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/layout/header.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/layout/header.tsx src/components/layout/header.test.tsx
git commit -m "feat: refactor Header with NAVIGATION_V2 and MegaDropdown integration"
```
---
## 任务 5Hero Section 重构
**文件:**
- 创建:`src/components/sections/hero-section-v2.tsx`
- 创建:`src/components/sections/hero-section-v2.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
// src/components/sections/hero-section-v2.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { HeroSectionV2 } from './hero-section-v2';
describe('HeroSectionV2', () => {
it('renders brand tag NOVALON', () => {
render(<HeroSectionV2 />);
expect(screen.getByText(/NOVALON/)).toBeInTheDocument();
});
it('renders main heading with calligraphy', () => {
render(<HeroSectionV2 />);
const heading = screen.getByRole('heading', { level: 1 });
expect(heading).toBeInTheDocument();
expect(heading.textContent).toContain('智连未来');
});
it('renders two CTA buttons', () => {
render(<HeroSectionV2 />);
expect(screen.getByText('预约演示')).toBeInTheDocument();
expect(screen.getByText('了解方案')).toBeInTheDocument();
});
it('renders subtitle text', () => {
render(<HeroSectionV2 />);
expect(screen.getByText(/从战略规划到系统落地/)).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/sections/hero-section-v2.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 HeroSectionV2**
```tsx
// src/components/sections/hero-section-v2.tsx
'use client';
import { useEffect, useRef, useState } from 'react';
import { motion } from 'framer-motion';
import { StaticLink } from '@/components/ui/static-link';
export function HeroSectionV2() {
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="home"
ref={sectionRef}
aria-labelledby="hero-heading"
className="relative min-h-[85vh] flex items-center overflow-hidden bg-gradient-to-b from-[#FFFBF5] to-white"
>
<div
className="absolute top-12 right-16 w-24 h-24 border border-[rgba(196,30,58,0.08)] rounded-full"
aria-hidden="true"
/>
<div
className="absolute bottom-16 left-10 w-18 h-18 border border-[rgba(196,30,58,0.06)] rounded-full"
aria-hidden="true"
/>
<div className="container-wide py-24 md:py-32 lg:py-40 relative z-10">
<div className="max-w-3xl mx-auto text-center">
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.1 }}
className="text-xs tracking-[0.2em] text-[#C41E3A] mb-6"
>
NOVALON ·
</motion.p>
<motion.h1
id="hero-heading"
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2 }}
className="font-calligraphy text-4xl md:text-5xl lg:text-6xl text-[#1C1C1C] tracking-[0.05em] mb-6"
>
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.3 }}
className="text-base md:text-lg text-[#5C5C5C] max-w-xl mx-auto mb-10"
>
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isVisible ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.4 }}
className="flex flex-col sm:flex-row gap-4 justify-center"
>
<StaticLink
href="/contact"
className="inline-flex items-center justify-center px-8 py-3 bg-[#C41E3A] text-white text-sm font-medium rounded hover:bg-[#A01830] transition-colors duration-200"
>
</StaticLink>
<StaticLink
href="/solutions"
className="inline-flex items-center justify-center px-8 py-3 border border-[#1C1C1C] text-[#1C1C1C] text-sm font-medium rounded hover:bg-[#1C1C1C] hover:text-white transition-colors duration-200"
>
</StaticLink>
</motion.div>
</div>
</div>
</section>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/sections/hero-section-v2.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/sections/hero-section-v2.tsx src/components/sections/hero-section-v2.test.tsx
git commit -m "feat: add HeroSectionV2 with brand fusion style and dual CTAs"
```
---
## 任务 6StatsBar 组件
**文件:**
- 创建:`src/components/ui/stats-bar.tsx`
- 创建:`src/components/ui/stats-bar.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { StatsBar } from './stats-bar';
const mockStats = [
{ value: '200+', label: '企业客户' },
{ value: '15+', label: '行业覆盖' },
{ value: '99.9%', label: '系统可用性' },
];
describe('StatsBar', () => {
it('renders all stat items', () => {
render(<StatsBar stats={mockStats} />);
expect(screen.getByText('200+')).toBeInTheDocument();
expect(screen.getByText('15+')).toBeInTheDocument();
expect(screen.getByText('99.9%')).toBeInTheDocument();
});
it('renders all labels', () => {
render(<StatsBar stats={mockStats} />);
expect(screen.getByText('企业客户')).toBeInTheDocument();
expect(screen.getByText('行业覆盖')).toBeInTheDocument();
expect(screen.getByText('系统可用性')).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/ui/stats-bar.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 StatsBar**
```tsx
// src/components/ui/stats-bar.tsx
'use client';
import { AnimatedNumber } from './animated-number';
export interface StatItem {
value: string;
label: string;
}
interface StatsBarProps {
stats: StatItem[];
}
export function StatsBar({ stats }: StatsBarProps) {
return (
<div className="grid grid-cols-3 gap-8">
{stats.map((stat) => (
<div key={stat.label} className="text-center">
<div className="text-3xl md:text-4xl font-bold text-[#C41E3A]">
{stat.value}
</div>
<div className="text-sm text-[#5C5C5C] mt-1">{stat.label}</div>
</div>
))}
</div>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/ui/stats-bar.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/ui/stats-bar.tsx src/components/ui/stats-bar.test.tsx
git commit -m "feat: add StatsBar component for social proof data display"
```
---
## 任务 7:社会证明 Section
**文件:**
- 创建:`src/components/sections/social-proof-section.tsx`
- 创建:`src/components/sections/social-proof-section.test.tsx`
- 修改:`src/lib/constants/stats.ts`
- [ ] **步骤 1:在 stats.ts 中新增 HOME_STATS**
```typescript
export const HOME_STATS: StatItem[] = [
{ value: '200+', label: '企业客户' },
{ value: '15+', label: '行业覆盖' },
{ value: '99.9%', label: '系统可用性' },
];
```
- [ ] **步骤 2:编写测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { SocialProofSection } from './social-proof-section';
describe('SocialProofSection', () => {
it('renders section with stats', () => {
render(<SocialProofSection />);
expect(screen.getByText('200+')).toBeInTheDocument();
expect(screen.getByText('企业客户')).toBeInTheDocument();
});
it('has correct background class', () => {
const { container } = render(<SocialProofSection />);
const section = container.querySelector('section');
expect(section?.className).toContain('bg-[#F5F5F5]');
});
});
```
- [ ] **步骤 3:实现 SocialProofSection**
```tsx
// src/components/sections/social-proof-section.tsx
'use client';
import { StatsBar } from '@/components/ui/stats-bar';
import { HOME_STATS } from '@/lib/constants/stats';
export function SocialProofSection() {
return (
<section className="bg-[#F5F5F5] py-16 md:py-20" aria-label="社会证明">
<div className="container-wide">
<StatsBar stats={HOME_STATS} />
</div>
</section>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/sections/social-proof-section.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/sections/social-proof-section.tsx src/components/sections/social-proof-section.test.tsx src/lib/constants/stats.ts
git commit -m "feat: add SocialProofSection with HOME_STATS data"
```
---
## 任务 8ProductCard 组件
**文件:**
- 创建:`src/components/ui/product-card.tsx`
- 创建:`src/components/ui/product-card.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { ProductCard } from './product-card';
const mockProduct = {
id: 'erp',
title: 'ERP 管理系统',
description: '财务·采购·销售·库存·生产',
href: '/products/erp',
};
describe('ProductCard', () => {
it('renders product title', () => {
render(<ProductCard {...mockProduct} />);
expect(screen.getByText('ERP 管理系统')).toBeInTheDocument();
});
it('renders product description', () => {
render(<ProductCard {...mockProduct} />);
expect(screen.getByText('财务·采购·销售·库存·生产')).toBeInTheDocument();
});
it('renders link to product page', () => {
render(<ProductCard {...mockProduct} />);
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/products/erp');
});
it('has left border accent', () => {
const { container } = render(<ProductCard {...mockProduct} />);
const card = container.firstChild as HTMLElement;
expect(card.className).toContain('border-l-[3px]');
expect(card.className).toContain('border-l-[#C41E3A]');
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/ui/product-card.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 ProductCard**
```tsx
// src/components/ui/product-card.tsx
import { StaticLink } from '@/components/ui/static-link';
interface ProductCardProps {
id: string;
title: string;
description: string;
href: string;
}
export function ProductCard({ title, description, href }: ProductCardProps) {
return (
<StaticLink
href={href}
className="block p-5 rounded-lg border border-[#E5E5E5] border-l-[3px] border-l-[#C41E3A] bg-white hover:bg-[#FFFBF5] hover:shadow-md hover:-translate-y-0.5 transition-all duration-200"
>
<h3 className="text-base font-semibold text-[#1C1C1C]">{title}</h3>
<p className="text-sm text-[#5C5C5C] mt-1.5">{description}</p>
</StaticLink>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/ui/product-card.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/ui/product-card.tsx src/components/ui/product-card.test.tsx
git commit -m "feat: add ProductCard component with brand-red left border"
```
---
## 任务 9:产品矩阵 Section
**文件:**
- 创建:`src/components/sections/product-matrix-section.tsx`
- 创建:`src/components/sections/product-matrix-section.test.tsx`
- [ ] **步骤 1:编写测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { ProductMatrixSection } from './product-matrix-section';
describe('ProductMatrixSection', () => {
it('renders section heading', () => {
render(<ProductMatrixSection />);
expect(screen.getByText('核心产品')).toBeInTheDocument();
});
it('renders all 4 products', () => {
render(<ProductMatrixSection />);
expect(screen.getByText('ERP 管理系统')).toBeInTheDocument();
expect(screen.getByText('CRM 客户管理')).toBeInTheDocument();
expect(screen.getByText('BI 数据平台')).toBeInTheDocument();
expect(screen.getByText('CMS 内容平台')).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/sections/product-matrix-section.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 ProductMatrixSection**
```tsx
// src/components/sections/product-matrix-section.tsx
'use client';
import { ProductCard } from '@/components/ui/product-card';
import { PRODUCTS } from '@/lib/constants/products';
export function ProductMatrixSection() {
return (
<section className="bg-white py-20 md:py-24" aria-label="核心产品">
<div className="container-wide">
<h2 className="text-2xl md:text-3xl font-bold text-[#1C1C1C] mb-10"></h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{PRODUCTS.map((product) => (
<ProductCard
key={product.id}
id={product.id}
title={product.title}
description={product.description.split('')[0]}
href={`/products/${product.id}`}
/>
))}
</div>
</div>
</section>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/sections/product-matrix-section.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/sections/product-matrix-section.tsx src/components/sections/product-matrix-section.test.tsx
git commit -m "feat: add ProductMatrixSection with 2x2 product card grid"
```
---
## 任务 10ChallengeCard 组件
**文件:**
- 创建:`src/components/ui/challenge-card.tsx`
- 创建:`src/components/ui/challenge-card.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { ChallengeCard } from './challenge-card';
const mockChallenge = {
id: 'isolation',
icon: '🏭',
title: '系统孤岛',
subtitle: '数据不通',
bgColor: '#FEF2F4',
hoverBgColor: '#FDE8EC',
href: '/solutions/manufacturing',
};
describe('ChallengeCard', () => {
it('renders icon', () => {
render(<ChallengeCard {...mockChallenge} />);
expect(screen.getByText('🏭')).toBeInTheDocument();
});
it('renders title and subtitle', () => {
render(<ChallengeCard {...mockChallenge} />);
expect(screen.getByText('系统孤岛')).toBeInTheDocument();
expect(screen.getByText('数据不通')).toBeInTheDocument();
});
it('renders link to solution page', () => {
render(<ChallengeCard {...mockChallenge} />);
const link = screen.getByRole('link');
expect(link).toHaveAttribute('href', '/solutions/manufacturing');
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/ui/challenge-card.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 ChallengeCard**
```tsx
// src/components/ui/challenge-card.tsx
import { StaticLink } from '@/components/ui/static-link';
interface ChallengeCardProps {
id: string;
icon: string;
title: string;
subtitle: string;
bgColor: string;
hoverBgColor: string;
href: string;
}
export function ChallengeCard({ icon, title, subtitle, bgColor, hoverBgColor, href }: ChallengeCardProps) {
return (
<StaticLink
href={href}
className="block p-6 rounded-xl text-center transition-all duration-200 hover:shadow-md hover:-translate-y-0.5"
style={{ backgroundColor: bgColor }}
onMouseEnter={(e) => { (e.currentTarget as HTMLElement).style.backgroundColor = hoverBgColor; }}
onMouseLeave={(e) => { (e.currentTarget as HTMLElement).style.backgroundColor = bgColor; }}
>
<div className="text-3xl mb-3">{icon}</div>
<h3 className="text-sm font-bold text-[#1C1C1C]">{title}</h3>
<p className="text-xs text-[#C41E3A] mt-1">{subtitle}</p>
</StaticLink>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/ui/challenge-card.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/ui/challenge-card.tsx src/components/ui/challenge-card.test.tsx
git commit -m "feat: add ChallengeCard component for scenario entry points"
```
---
## 任务 11:挑战场景 Section
**文件:**
- 创建:`src/components/sections/challenge-section.tsx`
- 创建:`src/components/sections/challenge-section.test.tsx`
- [ ] **步骤 1:编写测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { ChallengeSection } from './challenge-section';
describe('ChallengeSection', () => {
it('renders section heading', () => {
render(<ChallengeSection />);
expect(screen.getByText('您面临什么挑战?')).toBeInTheDocument();
});
it('renders three challenge cards', () => {
render(<ChallengeSection />);
expect(screen.getByText('系统孤岛')).toBeInTheDocument();
expect(screen.getByText('增长瓶颈')).toBeInTheDocument();
expect(screen.getByText('合规风险')).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/sections/challenge-section.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 ChallengeSection**
```tsx
// src/components/sections/challenge-section.tsx
'use client';
import { ChallengeCard } from '@/components/ui/challenge-card';
const CHALLENGES = [
{
id: 'isolation',
icon: '🏭',
title: '系统孤岛',
subtitle: '数据不通',
bgColor: 'var(--color-challenge-isolation)',
hoverBgColor: 'var(--color-challenge-isolation-hover)',
href: '/solutions/manufacturing',
},
{
id: 'growth',
icon: '📈',
title: '增长瓶颈',
subtitle: '效率低下',
bgColor: 'var(--color-challenge-growth)',
hoverBgColor: 'var(--color-challenge-growth-hover)',
href: '/solutions/retail',
},
{
id: 'compliance',
icon: '🔒',
title: '合规风险',
subtitle: '安全隐忧',
bgColor: 'var(--color-challenge-compliance)',
hoverBgColor: 'var(--color-challenge-compliance-hover)',
href: '/solutions/healthcare',
},
];
export function ChallengeSection() {
return (
<section className="bg-[#FFFBF5] py-20 md:py-24" aria-label="挑战与场景">
<div className="container-wide">
<h2 className="text-2xl md:text-3xl font-bold text-[#1C1C1C] mb-10"></h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{CHALLENGES.map((challenge) => (
<ChallengeCard key={challenge.id} {...challenge} />
))}
</div>
</div>
</section>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/sections/challenge-section.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/sections/challenge-section.tsx src/components/sections/challenge-section.test.tsx
git commit -m "feat: add ChallengeSection with three scenario entry cards"
```
---
## 任务 12TestimonialBlock 组件
**文件:**
- 创建:`src/components/ui/testimonial-block.tsx`
- 创建:`src/components/ui/testimonial-block.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { TestimonialBlock } from './testimonial-block';
const mockTestimonial = {
quote: '与睿新合作后,运营效率提升了40%',
author: '某上市制造企业 CTO',
results: [
{ value: '40%', label: '效率提升' },
{ value: '6月', label: '交付周期' },
],
};
describe('TestimonialBlock', () => {
it('renders quote text', () => {
render(<TestimonialBlock {...mockTestimonial} />);
expect(screen.getByText(/运营效率提升了40%/)).toBeInTheDocument();
});
it('renders author attribution', () => {
render(<TestimonialBlock {...mockTestimonial} />);
expect(screen.getByText(/某上市制造企业 CTO/)).toBeInTheDocument();
});
it('renders result metrics', () => {
render(<TestimonialBlock {...mockTestimonial} />);
expect(screen.getByText('40%')).toBeInTheDocument();
expect(screen.getByText('效率提升')).toBeInTheDocument();
expect(screen.getByText('6月')).toBeInTheDocument();
expect(screen.getByText('交付周期')).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/ui/testimonial-block.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 TestimonialBlock**
```tsx
// src/components/ui/testimonial-block.tsx
interface TestimonialResult {
value: string;
label: string;
}
interface TestimonialBlockProps {
quote: string;
author: string;
results: TestimonialResult[];
}
export function TestimonialBlock({ quote, author, results }: TestimonialBlockProps) {
return (
<div className="bg-[#1C1C1C] rounded-xl p-8 md:p-10">
<p className="text-xs text-[rgba(255,255,255,0.6)] mb-4"></p>
<blockquote className="text-base md:text-lg text-white italic mb-4">
&ldquo;{quote}&rdquo;
</blockquote>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<p className="text-xs text-[rgba(255,255,255,0.5)]">&mdash; {author}</p>
<div className="flex gap-6">
{results.map((result) => (
<div key={result.label} className="text-center">
<div className="text-lg font-bold text-[#C41E3A]">{result.value}</div>
<div className="text-[10px] text-[rgba(255,255,255,0.4)]">{result.label}</div>
</div>
))}
</div>
</div>
</div>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/ui/testimonial-block.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/ui/testimonial-block.tsx src/components/ui/testimonial-block.test.tsx
git commit -m "feat: add TestimonialBlock component with dark background and metrics"
```
---
## 任务 13:客户成果 Section
**文件:**
- 创建:`src/components/sections/testimonial-section.tsx`
- 创建:`src/components/sections/testimonial-section.test.tsx`
- [ ] **步骤 1:编写测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { TestimonialSection } from './testimonial-section';
describe('TestimonialSection', () => {
it('renders client testimonial quote', () => {
render(<TestimonialSection />);
expect(screen.getByText(/客户成果/)).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/sections/testimonial-section.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 TestimonialSection**
```tsx
// src/components/sections/testimonial-section.tsx
'use client';
import { TestimonialBlock } from '@/components/ui/testimonial-block';
import { CASES } from '@/lib/constants/cases';
export function TestimonialSection() {
const featuredCase = CASES.find(c => c.testimonial) || CASES[0];
const testimonial = featuredCase.testimonial
? {
quote: featuredCase.testimonial.quote,
author: `${featuredCase.testimonial.author}${featuredCase.testimonial.role}`,
results: featuredCase.results.slice(0, 3),
}
: {
quote: featuredCase.description.slice(0, 60) + '...',
author: featuredCase.client,
results: featuredCase.results.slice(0, 3),
};
return (
<section className="bg-[#1C1C1C] py-20 md:py-24" aria-label="客户成果">
<div className="container-wide">
<TestimonialBlock {...testimonial} />
</div>
</section>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/sections/testimonial-section.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/sections/testimonial-section.tsx src/components/sections/testimonial-section.test.tsx
git commit -m "feat: add TestimonialSection using CASES data for client stories"
```
---
## 任务 14CTASection 组件
**文件:**
- 创建:`src/components/ui/cta-section.tsx`
- 创建:`src/components/ui/cta-section.test.tsx`
- [ ] **步骤 1:编写失败的测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { CTASection } from './cta-section';
describe('CTASection', () => {
it('renders heading', () => {
render(<CTASection heading="开启数字化转型之旅" />);
expect(screen.getByText('开启数字化转型之旅')).toBeInTheDocument();
});
it('renders subtitle when provided', () => {
render(<CTASection heading="开启" subtitle="免费咨询 · 专属方案" />);
expect(screen.getByText('免费咨询 · 专属方案')).toBeInTheDocument();
});
it('renders CTA button', () => {
render(<CTASection heading="开启" buttonText="立即咨询" buttonHref="/contact" />);
const link = screen.getByText('立即咨询');
expect(link).toBeInTheDocument();
expect(link.closest('a')).toHaveAttribute('href', '/contact');
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/ui/cta-section.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 CTASection**
```tsx
// src/components/ui/cta-section.tsx
import { StaticLink } from '@/components/ui/static-link';
interface CTASectionProps {
heading: string;
subtitle?: string;
buttonText?: string;
buttonHref?: string;
}
export function CTASection({
heading,
subtitle,
buttonText = '立即咨询',
buttonHref = '/contact',
}: CTASectionProps) {
return (
<div className="bg-gradient-to-br from-[#C41E3A] to-[#A01830] rounded-xl p-10 md:p-14 text-center">
<h2 className="text-xl md:text-2xl font-bold text-white mb-3">{heading}</h2>
{subtitle && (
<p className="text-sm text-[rgba(255,255,255,0.8)] mb-8">{subtitle}</p>
)}
<StaticLink
href={buttonHref}
className="inline-flex items-center justify-center px-8 py-3 border border-white text-white text-sm font-medium rounded hover:bg-white hover:text-[#C41E3A] transition-colors duration-200"
>
{buttonText}
</StaticLink>
</div>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/ui/cta-section.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/ui/cta-section.tsx src/components/ui/cta-section.test.tsx
git commit -m "feat: add CTASection component with brand-red gradient"
```
---
## 任务 15CTA Section 页面级
**文件:**
- 创建:`src/components/sections/cta-section-page.tsx`
- 创建:`src/components/sections/cta-section-page.test.tsx`
- [ ] **步骤 1:编写测试**
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { CTASectionPage } from './cta-section-page';
describe('CTASectionPage', () => {
it('renders CTA with correct content', () => {
render(<CTASectionPage />);
expect(screen.getByText('开启数字化转型之旅')).toBeInTheDocument();
expect(screen.getByText('免费咨询 · 专属方案 · 30天试用')).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/components/sections/cta-section-page.test.tsx --no-coverage`
预期:FAIL
- [ ] **步骤 3:实现 CTASectionPage**
```tsx
// src/components/sections/cta-section-page.tsx
import { CTASection } from '@/components/ui/cta-section';
export function CTASectionPage() {
return (
<section className="py-20 md:py-24" aria-label="行动号召">
<div className="container-wide">
<CTASection
heading="开启数字化转型之旅"
subtitle="免费咨询 · 专属方案 · 30天试用"
buttonText="立即咨询"
buttonHref="/contact"
/>
</div>
</section>
);
}
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/components/sections/cta-section-page.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/components/sections/cta-section-page.tsx src/components/sections/cta-section-page.test.tsx
git commit -m "feat: add CTASectionPage with homepage-specific CTA content"
```
---
## 任务 16:首页组装
**文件:**
- 修改:`src/app/(marketing)/home-content.tsx`
- [ ] **步骤 1:编写测试**
`src/app/(marketing)/page.test.tsx`(如不存在则新建)中:
```tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import HomeContent from './home-content';
describe('HomeContent V2', () => {
it('renders new hero section', () => {
render(<HomeContent heroStats={null} />);
expect(screen.getByText('智连未来,成长伙伴')).toBeInTheDocument();
});
it('renders product matrix section', () => {
render(<HomeContent heroStats={null} />);
expect(screen.getByText('核心产品')).toBeInTheDocument();
});
it('renders challenge section', () => {
render(<HomeContent heroStats={null} />);
expect(screen.getByText('您面临什么挑战?')).toBeInTheDocument();
});
it('renders CTA section', () => {
render(<HomeContent heroStats={null} />);
expect(screen.getByText('开启数字化转型之旅')).toBeInTheDocument();
});
});
```
- [ ] **步骤 2:运行测试验证失败**
运行:`npx jest src/app/\(marketing\)/page.test.tsx --no-coverage`
预期:FAIL — 找不到新 Section 内容
- [ ] **步骤 3:重构 home-content.tsx**
替换 `home-content.tsx` 的全部内容:
```tsx
'use client';
import dynamic from 'next/dynamic';
import { HeroSectionV2 } from '@/components/sections/hero-section-v2';
import { SectionSkeleton } from '@/components/ui/loading-skeleton';
import type { ReactNode } from 'react';
const SocialProofSection = dynamic(
() => import('@/components/sections/social-proof-section').then(mod => ({ default: mod.SocialProofSection })),
{ loading: () => <SectionSkeleton />, ssr: false }
);
const ProductMatrixSection = dynamic(
() => import('@/components/sections/product-matrix-section').then(mod => ({ default: mod.ProductMatrixSection })),
{ loading: () => <SectionSkeleton />, ssr: false }
);
const ChallengeSection = dynamic(
() => import('@/components/sections/challenge-section').then(mod => ({ default: mod.ChallengeSection })),
{ loading: () => <SectionSkeleton />, ssr: false }
);
const TestimonialSection = dynamic(
() => import('@/components/sections/testimonial-section').then(mod => ({ default: mod.TestimonialSection })),
{ loading: () => <SectionSkeleton />, ssr: false }
);
const CTASectionPage = dynamic(
() => import('@/components/sections/cta-section-page').then(mod => ({ default: mod.CTASectionPage })),
{ loading: () => <SectionSkeleton />, ssr: false }
);
function HomeContent({ heroStats }: { heroStats: ReactNode }) {
return (
<>
<HeroSectionV2 />
<SocialProofSection />
<ProductMatrixSection />
<ChallengeSection />
<TestimonialSection />
<CTASectionPage />
</>
);
}
export default HomeContent;
```
- [ ] **步骤 4:运行测试验证通过**
运行:`npx jest src/app/\(marketing\)/page.test.tsx --no-coverage`
预期:PASS
- [ ] **步骤 5Commit**
```bash
git add src/app/\(marketing\)/home-content.tsx src/app/\(marketing\)/page.test.tsx
git commit -m "feat: assemble homepage with new brand-fusion sections"
```
---
## 任务 17:废弃动效清理
**文件:**
- 修改:`src/components/effects/index.ts`
- [ ] **步骤 1:从 effects/index.ts 移除废弃组件导出**
移除以下导出:
- `InkBackground` / `InkTechFusion`
- `DataParticleFlow`
- `ParticleGalaxy`
- `MouseInteractiveParticles`
- `FluidWaveBackground`
- `GeometricAbstract`
- `GridLines`
- `AdvancedFloatingEffects`
保留以下导出:
- `SubtleDots`
- `GradientFlow` / `GradientFlowOptimized`
- `GradientOrbs`
- `GlowEffect`
- `ParallaxEffect`
- `SealAnimationEnhanced`
- [ ] **步骤 2:验证构建无报错**
运行:`npx tsc --noEmit`
预期:无类型错误(如果有引用废弃组件的文件,需更新引用)
- [ ] **步骤 3Commit**
```bash
git add src/components/effects/index.ts
git commit -m "chore: remove deprecated particle/ink effect exports from index"
```
---
## 任务 18:集成测试与验证
- [ ] **步骤 1:运行全量单元测试**
运行:`npx jest --no-coverage`
预期:所有测试 PASS
- [ ] **步骤 2:运行类型检查**
运行:`npx tsc --noEmit`
预期:无类型错误
- [ ] **步骤 3:运行构建**
运行:`npm run build`
预期:构建成功
- [ ] **步骤 4:启动开发服务器,手动验证首页**
运行:`npm run dev`
验证项:
1. 导航栏显示 6 项,"产品"和"解决方案"有下拉
2. Hero 白底 + 书法标题 + 双 CTA
3. 社会证明三列数字
4. 产品矩阵 2×2 卡片 + 朱砂红左边框
5. 挑战场景三列卡片 + 场景色
6. 客户成果深色背景 + 引言
7. CTA 朱砂红渐变
8. 无粒子/水墨动效
- [ ] **步骤 5:运行 Lighthouse 审计**
运行:`npm run lighthouse:mobile`
预期:Performance ≥ 90, Accessibility ≥ 95
- [ ] **步骤 6:最终 Commit**
```bash
git add -A
git commit -m "feat: complete Phase 1 of Atlassian-style brand fusion homepage redesign"
```
---
## 自检清单
| 检查项 | 状态 |
|--------|------|
| 规格中每个 Section 都有对应任务 | ✅ Hero/SocialProof/ProductMatrix/Challenge/Testimonial/CTA |
| 规格中每个组件都有对应任务 | ✅ MegaDropdown/ProductCard/ChallengeCard/StatsBar/TestimonialBlock/CTASection |
| 无占位符(TODO/TBD/待定) | ✅ 所有步骤包含完整代码 |
| 类型一致性 | ✅ NavigationItemV2/MegaDropdownItem/StatItem 等类型定义明确 |
| 废弃动效清理有对应任务 | ✅ 任务 17 |
| 集成验证有对应任务 | ✅ 任务 18 |