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

53 KiB
Raw Blame History

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


整体架构图

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

组件依赖图

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

数据流图

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 块中,在 /* 状态色 */ 注释之后添加:

  /* 场景色 - 挑战卡片 */
  --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
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(新建)中:

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 中追加:

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 中追加导出:

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
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:编写失败的测试

// 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 组件
// 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
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 中追加测试:

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 管理下拉开关

关键代码片段(替换桌面导航部分):

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
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:编写失败的测试

// 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
// 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
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:编写失败的测试

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
// 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
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

export const HOME_STATS: StatItem[] = [
  { value: '200+', label: '企业客户' },
  { value: '15+', label: '行业覆盖' },
  { value: '99.9%', label: '系统可用性' },
];
  • 步骤 2:编写测试
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
// 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
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:编写失败的测试

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
// 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
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:编写测试

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
// 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
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:编写失败的测试

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
// 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
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:编写测试

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
// 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
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:编写失败的测试

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
// 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
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:编写测试

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
// 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
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:编写失败的测试

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
// 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
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:编写测试

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
// 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
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(如不存在则新建)中:

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 的全部内容:

'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
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
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
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