Files
novalon-website/docs/plans/2026-02-21-website-redesign.md
T

46 KiB
Raw Blame History

Novalon 官网重新设计实施计划

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 将 Novalon 官网从现有设计升级为深色科技风格的现代化企业网站

Architecture: 采用 Next.js 16 App Router + React 19 + TypeScript + Tailwind CSS v4 技术栈,基于组件化设计,实现深色科技风格的视觉系统和流畅的用户体验

Tech Stack: Next.js 16, React 19, TypeScript 5, Tailwind CSS v4, Framer Motion, AntV G2, shadcn/ui


前置准备

Task 0: 创建开发分支

Files:

  • None

Step 1: 检查当前分支状态

Run: git status

Expected: 工作目录干净,在 main 或 develop 分支

Step 2: 创建新的功能分支

Run: git checkout -b feature/website-redesign

Expected: 切换到新分支 feature/website-redesign

Step 3: 推送分支到远程

Run: git push -u origin feature/website-redesign

Expected: 分支推送到远程仓库


阶段一:基础架构搭建

Task 1: 安装必要依赖

Files:

  • Modify: package.json

Step 1: 安装动画库

Run: npm install framer-motion

Expected: framer-motion 安装成功

Step 2: 安装图表库

Run: npm install @antv/g2

Expected: @antv/g2 安装成功

Step 3: 安装 CVA(如果尚未安装)

Run: npm install class-variance-authority

Expected: class-variance-authority 安装成功

Step 4: 验证安装

Run: npm list framer-motion @antv/g2 class-variance-authority

Expected: 显示已安装的版本信息

Step 5: 提交依赖更新

Run:

git add package.json package-lock.json
git commit -m "chore: add framer-motion, @antv/g2, and class-variance-authority dependencies"

Expected: 提交成功


Task 2: 扩展 Tailwind 配置

Files:

  • Modify: tailwind.config.ts(如果不存在则创建)

Step 1: 检查 Tailwind 配置文件

Run: ls -la | grep tailwind

Expected: 显示 tailwind 配置文件

Step 2: 读取现有配置

Read: tailwind.config.ts

Step 3: 扩展配置添加深色主题色彩

Modify: tailwind.config.ts

import type { Config } from "tailwindcss";

export default {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        dark: {
          DEFAULT: "#0A0A0A",
          secondary: "#1A1A1A",
          tertiary: "#2A2A2A",
        },
        tech: {
          blue: "#00D9FF",
          purple: "#A855F7",
          cyan: "#06B6D4",
        },
      },
      fontFamily: {
        sans: ["Inter", "system-ui", "sans-serif"],
        mono: ["JetBrains Mono", "monospace"],
      },
      animation: {
        "fade-in-up": "fadeInUp 0.6s ease-out",
        "fade-in-scale": "fadeInScale 0.6s ease-out",
        glow: "glow 2s ease-in-out infinite",
        float: "float 3s ease-in-out infinite",
      },
      keyframes: {
        fadeInUp: {
          "0%": { opacity: "0", transform: "translateY(20px)" },
          "100%": { opacity: "1", transform: "translateY(0)" },
        },
        fadeInScale: {
          "0%": { opacity: "0", transform: "scale(0.95)" },
          "100%": { opacity: "1", transform: "scale(1)" },
        },
        glow: {
          "0%, 100%": { boxShadow: "0 0 20px rgba(0, 217, 255, 0.3)" },
          "50%": { boxShadow: "0 0 40px rgba(0, 217, 255, 0.6)" },
        },
        float: {
          "0%, 100%": { transform: "translateY(0px)" },
          "50%": { transform: "translateY(-10px)" },
        },
      },
    },
  },
  plugins: [],
} satisfies Config;

Step 4: 验证配置语法

Run: npx tsc --noEmit tailwind.config.ts

Expected: 无错误输出

Step 5: 提交配置更新

Run:

git add tailwind.config.ts
git commit -m "feat: extend Tailwind config with dark theme and tech colors"

Expected: 提交成功


Task 3: 更新全局样式

Files:

  • Modify: src/app/globals.css

Step 1: 读取现有全局样式

Read: src/app/globals.css

Step 2: 添加 CSS 变量和基础样式

Modify: src/app/globals.css

在文件顶部添加:

:root {
  --color-dark: #0A0A0A;
  --color-dark-secondary: #1A1A1A;
  --color-dark-tertiary: #2A2A2A;
  --color-tech-blue: #00D9FF;
  --color-tech-purple: #A855F7;
  --color-tech-cyan: #06B6D4;
  
  --gradient-primary: linear-gradient(135deg, #00D9FF 0%, #A855F7 100%);
  --gradient-dark: linear-gradient(180deg, #0A0A0A 0%, #1A1A1A 100%);
  --gradient-glow: radial-gradient(circle, #00D9FF20 0%, transparent 70%);
}

* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  background-color: var(--color-dark);
  color: #FFFFFF;
  font-family: 'Inter', system-ui, sans-serif;
  line-height: 1.6;
}

a {
  color: inherit;
  text-decoration: none;
}

button {
  font-family: inherit;
}

input, textarea {
  font-family: inherit;
}

::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}

::-webkit-scrollbar-track {
  background: var(--color-dark-secondary);
}

::-webkit-scrollbar-thumb {
  background: var(--color-tech-blue);
  border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
  background: var(--color-tech-purple);
}

Step 3: 验证样式文件

Run: npm run build

Expected: 构建成功,无样式错误

Step 4: 提交样式更新

Run:

git add src/app/globals.css
git commit -m "feat: add CSS variables and base styles for dark theme"

Expected: 提交成功


Task 4: 创建工具函数

Files:

  • Modify: src/lib/utils.ts

Step 1: 读取现有工具函数

Read: src/lib/utils.ts

Step 2: 添加新的工具函数

Modify: src/lib/utils.ts

import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function formatNumber(num: number): string {
  return new Intl.NumberFormat("zh-CN").format(num);
}

export function formatCurrency(amount: number): string {
  return new Intl.NumberFormat("zh-CN", {
    style: "currency",
    currency: "CNY",
  }).format(amount);
}

export function debounce<T extends (...args: unknown[]) => unknown>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: NodeJS.Timeout | null = null;
  return (...args: Parameters<T>) => {
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), wait);
  };
}

export function throttle<T extends (...args: unknown[]) => unknown>(
  func: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle: boolean;
  return (...args: Parameters<T>) => {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => (inThrottle = false), limit);
    }
  };
}

export function randomBetween(min: number, max: number): number {
  return Math.random() * (max - min) + min;
}

export function lerp(start: number, end: number, t: number): number {
  return start + (end - start) * t;
}

export function clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max);
}

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交工具函数

Run:

git add src/lib/utils.ts
git commit -m "feat: add utility functions for formatting and animation"

Expected: 提交成功


Task 5: 创建主题常量

Files:

  • Modify: src/lib/constants.ts

Step 1: 读取现有常量

Read: src/lib/constants.ts

Step 2: 添加主题相关常量

Modify: src/lib/constants.ts

在文件末尾添加:

export const THEME = {
  colors: {
    dark: {
      DEFAULT: "#0A0A0A",
      secondary: "#1A1A1A",
      tertiary: "#2A2A2A",
    },
    tech: {
      blue: "#00D9FF",
      purple: "#A855F7",
      cyan: "#06B6D4",
    },
    text: {
      primary: "#FFFFFF",
      secondary: "#A0A0A0",
      disabled: "#606060",
    },
  },
  gradients: {
    primary: "linear-gradient(135deg, #00D9FF 0%, #A855F7 100%)",
    dark: "linear-gradient(180deg, #0A0A0A 0%, #1A1A1A 100%)",
    glow: "radial-gradient(circle, #00D9FF20 0%, transparent 70%)",
  },
  spacing: {
    xs: "4px",
    sm: "8px",
    md: "16px",
    lg: "24px",
    xl: "32px",
    "2xl": "48px",
    "3xl": "64px",
  },
  borderRadius: {
    sm: "8px",
    md: "12px",
    lg: "16px",
    full: "9999px",
  },
  animation: {
    fast: "150ms",
    standard: "300ms",
    slow: "500ms",
    verySlow: "1000ms",
  },
  breakpoints: {
    sm: "640px",
    md: "768px",
    lg: "1024px",
    xl: "1280px",
    "2xl": "1536px",
  },
} as const;

export const ANIMATION_VARIANTS = {
  fadeInUp: {
    initial: { opacity: 0, y: 20 },
    animate: { opacity: 1, y: 0 },
    exit: { opacity: 0, y: -20 },
  },
  fadeInScale: {
    initial: { opacity: 0, scale: 0.95 },
    animate: { opacity: 1, scale: 1 },
    exit: { opacity: 0, scale: 0.95 },
  },
  slideInLeft: {
    initial: { opacity: 0, x: -20 },
    animate: { opacity: 1, x: 0 },
    exit: { opacity: 0, x: 20 },
  },
  slideInRight: {
    initial: { opacity: 0, x: 20 },
    animate: { opacity: 1, x: 0 },
    exit: { opacity: 0, x: -20 },
  },
} as const;

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交常量更新

Run:

git add src/lib/constants.ts
git commit -m "feat: add theme constants and animation variants"

Expected: 提交成功


阶段二:基础UI组件开发

Task 6: 创建增强版按钮组件

Files:

  • Modify: src/components/ui/button.tsx

Step 1: 读取现有按钮组件

Read: src/components/ui/button.tsx

Step 2: 重写按钮组件以支持深色主题

Modify: src/components/ui/button.tsx

import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const buttonVariants = cva(
  "inline-flex items-center justify-center whitespace-nowrap rounded-lg text-sm font-medium transition-all duration-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-tech-blue focus-visible:ring-offset-2 focus-visible:ring-offset-dark disabled:pointer-events-none disabled:opacity-50",
  {
    variants: {
      variant: {
        default:
          "bg-gradient-to-r from-tech-blue to-tech-purple text-white hover:shadow-lg hover:shadow-tech-blue/50 hover:scale-105 active:scale-95",
        secondary:
          "bg-dark-tertiary text-white border border-gray-700 hover:border-tech-blue hover:shadow-lg hover:shadow-tech-blue/20 hover:scale-105 active:scale-95",
        outline:
          "border border-tech-blue text-tech-blue hover:bg-tech-blue/10 hover:shadow-lg hover:shadow-tech-blue/20",
        ghost:
          "text-gray-300 hover:text-tech-blue hover:bg-dark-tertiary",
        link: "text-tech-blue underline-offset-4 hover:underline",
      },
      size: {
        default: "h-10 px-6 py-2",
        sm: "h-8 px-4 text-xs",
        lg: "h-12 px-8 text-base",
        xl: "h-14 px-10 text-lg",
        icon: "h-10 w-10",
      },
    },
    defaultVariants: {
      variant: "default",
      size: "default",
    },
  }
);

export interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  asChild?: boolean;
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, ...props }, ref) => {
    return (
      <button
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    );
  }
);
Button.displayName = "Button";

export { Button, buttonVariants };

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交按钮组件

Run:

git add src/components/ui/button.tsx
git commit -m "feat: enhance button component with dark theme variants"

Expected: 提交成功


Task 7: 创建增强版卡片组件

Files:

  • Modify: src/components/ui/card.tsx

Step 1: 读取现有卡片组件

Read: src/components/ui/card.tsx

Step 2: 重写卡片组件以支持深色主题和悬停效果

Modify: src/components/ui/card.tsx

import * as React from "react";
import { cn } from "@/lib/utils";

const Card = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement> & {
    variant?: "default" | "hover" | "glow";
  }
>(({ className, variant = "default", ...props }, ref) => (
  <div
    ref={ref}
    className={cn(
      "rounded-xl border border-gray-800 bg-dark-tertiary text-white transition-all duration-300",
      {
        "hover:border-tech-blue hover:shadow-lg hover:shadow-tech-blue/20 hover:-translate-y-2":
          variant === "hover",
        "hover:border-tech-blue hover:shadow-xl hover:shadow-tech-blue/30 hover:-translate-y-2 animate-glow":
          variant === "glow",
      },
      className
    )}
    {...props}
  />
));
Card.displayName = "Card";

const CardHeader = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn("flex flex-col space-y-1.5 p-6", className)}
    {...props}
  />
));
CardHeader.displayName = "CardHeader";

const CardTitle = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
  <h3
    ref={ref}
    className={cn(
      "text-2xl font-bold leading-none tracking-tight text-white",
      className
    )}
    {...props}
  />
));
CardTitle.displayName = "CardTitle";

const CardDescription = React.forwardRef<
  HTMLParagraphElement,
  React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
  <p
    ref={ref}
    className={cn("text-sm text-gray-400", className)}
    {...props}
  />
));
CardDescription.displayName = "CardDescription";

const CardContent = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
));
CardContent.displayName = "CardContent";

const CardFooter = React.forwardRef<
  HTMLDivElement,
  React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
  <div
    ref={ref}
    className={cn("flex items-center p-6 pt-0", className)}
    {...props}
  />
));
CardFooter.displayName = "CardFooter";

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交卡片组件

Run:

git add src/components/ui/card.tsx
git commit -m "feat: enhance card component with dark theme and hover effects"

Expected: 提交成功


Task 8: 创建输入框组件

Files:

  • Modify: src/components/ui/input.tsx

Step 1: 读取现有输入框组件

Read: src/components/ui/input.tsx

Step 2: 重写输入框组件以支持深色主题

Modify: src/components/ui/input.tsx

import * as React from "react";
import { cn } from "@/lib/utils";

export interface InputProps
  extends React.InputHTMLAttributes<HTMLInputElement> {}

const Input = React.forwardRef<HTMLInputElement, InputProps>(
  ({ className, type, ...props }, ref) => {
    return (
      <input
        type={type}
        className={cn(
          "flex h-10 w-full rounded-lg border border-gray-700 bg-dark-secondary px-4 py-2 text-sm text-white placeholder:text-gray-500 transition-all duration-300",
          "focus:border-tech-blue focus:outline-none focus:ring-2 focus:ring-tech-blue/20 focus:shadow-lg focus:shadow-tech-blue/10",
          "disabled:cursor-not-allowed disabled:opacity-50",
          "hover:border-gray-600",
          className
        )}
        ref={ref}
        {...props}
      />
    );
  }
);
Input.displayName = "Input";

export { Input };

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交输入框组件

Run:

git add src/components/ui/input.tsx
git commit -m "feat: enhance input component with dark theme styles"

Expected: 提交成功


Task 9: 创建徽章组件

Files:

  • Modify: src/components/ui/badge.tsx

Step 1: 读取现有徽章组件

Read: src/components/ui/badge.tsx

Step 2: 重写徽章组件以支持深色主题

Modify: src/components/ui/badge.tsx

import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

const badgeVariants = cva(
  "inline-flex items-center rounded-full px-3 py-1 text-xs font-medium transition-colors duration-300",
  {
    variants: {
      variant: {
        default:
          "bg-tech-blue/20 text-tech-blue border border-tech-blue/30",
        secondary:
          "bg-tech-purple/20 text-tech-purple border border-tech-purple/30",
        success:
          "bg-green-500/20 text-green-400 border border-green-500/30",
        warning:
          "bg-yellow-500/20 text-yellow-400 border border-yellow-500/30",
        error:
          "bg-red-500/20 text-red-400 border border-red-500/30",
        outline:
          "bg-transparent text-gray-300 border border-gray-600",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  }
);

export interface BadgeProps
  extends React.HTMLAttributes<HTMLDivElement>,
    VariantProps<typeof badgeVariants> {}

function Badge({ className, variant, ...props }: BadgeProps) {
  return (
    <div className={cn(badgeVariants({ variant }), className)} {...props} />
  );
}

export { Badge, badgeVariants };

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交徽章组件

Run:

git add src/components/ui/badge.tsx
git commit -m "feat: enhance badge component with dark theme variants"

Expected: 提交成功


阶段三:布局组件重构

Task 10: 重构导航栏组件

Files:

  • Modify: src/components/layout/header.tsx

Step 1: 读取现有导航栏组件

Read: src/components/layout/header.tsx

Step 2: 重写导航栏组件以支持深色主题和下拉菜单

Modify: src/components/layout/header.tsx

"use client";

import { useState, useEffect } from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { NAVIGATION, COMPANY_INFO } from "@/lib/constants";
import { cn } from "@/lib/utils";
import { ChevronDown, Menu, X } from "lucide-react";

export function Header() {
  const [isScrolled, setIsScrolled] = useState(false);
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
  const [activeDropdown, setActiveDropdown] = useState<string | null>(null);

  useEffect(() => {
    const handleScroll = () => {
      setIsScrolled(window.scrollY > 10);
    };
    window.addEventListener("scroll", handleScroll);
    return () => window.removeEventListener("scroll", handleScroll);
  }, []);

  return (
    <header
      className={cn(
        "fixed top-0 left-0 right-0 z-50 transition-all duration-300",
        isScrolled
          ? "bg-dark/95 backdrop-blur-md border-b border-gray-800 shadow-lg"
          : "bg-transparent"
      )}
    >
      <nav className="container-wide h-16 flex items-center justify-between">
        <Link href="/" className="flex items-center gap-2 group">
          <div className="relative">
            <img
              src="/logo.svg"
              alt={COMPANY_INFO.shortName}
              className="h-8 w-auto transition-all duration-300 group-hover:drop-shadow-[0_0_10px_rgba(0,217,255,0.5)]"
            />
          </div>
          <span className="text-lg font-bold text-white hidden sm:block">
            {COMPANY_INFO.shortName}
          </span>
        </Link>

        <div className="hidden lg:flex items-center gap-8">
          {NAVIGATION.map((item) => (
            <Link
              key={item.id}
              href={item.href}
              className="text-gray-300 hover:text-tech-blue transition-colors duration-300 relative group"
            >
              {item.label}
              <span className="absolute -bottom-1 left-0 w-0 h-0.5 bg-tech-blue transition-all duration-300 group-hover:w-full" />
            </Link>
          ))}
        </div>

        <div className="hidden lg:flex items-center gap-4">
          <Button variant="ghost" size="sm">
            登录
          </Button>
          <Button variant="default" size="sm">
            免费试用
          </Button>
        </div>

        <button
          className="lg:hidden text-white p-2"
          onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
          aria-label="Toggle menu"
        >
          {isMobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
        </button>
      </nav>

      {isMobileMenuOpen && (
        <div className="lg:hidden bg-dark-secondary border-t border-gray-800">
          <div className="container-wide py-4 space-y-4">
            {NAVIGATION.map((item) => (
              <Link
                key={item.id}
                href={item.href}
                className="block text-gray-300 hover:text-tech-blue transition-colors duration-300 py-2"
                onClick={() => setIsMobileMenuOpen(false)}
              >
                {item.label}
              </Link>
            ))}
            <div className="flex flex-col gap-2 pt-4 border-t border-gray-800">
              <Button variant="ghost" className="w-full">
                登录
              </Button>
              <Button variant="default" className="w-full">
                免费试用
              </Button>
            </div>
          </div>
        </div>
      )}
    </header>
  );
}

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交导航栏组件

Run:

git add src/components/layout/header.tsx
git commit -m "feat: refactor header with dark theme and mobile menu"

Expected: 提交成功


Task 11: 重构页脚组件

Files:

  • Modify: src/components/layout/footer.tsx

Step 1: 读取现有页脚组件

Read: src/components/layout/footer.tsx

Step 2: 重写页脚组件以支持深色主题

Modify: src/components/layout/footer.tsx

import Link from "next/link";
import { COMPANY_INFO } from "@/lib/constants";

export function Footer() {
  const currentYear = new Date().getFullYear();

  const footerLinks = {
    products: [
      { label: "ERP系统", href: "/products/erp" },
      { label: "CRM系统", href: "/products/crm" },
      { label: "OA平台", href: "/products/oa" },
      { label: "BI平台", href: "/products/bi" },
    ],
    solutions: [
      { label: "金融行业", href: "/solutions/finance" },
      { label: "制造业", href: "/solutions/manufacturing" },
      { label: "零售行业", href: "/solutions/retail" },
      { label: "教育行业", href: "/solutions/education" },
    ],
    company: [
      { label: "关于我们", href: "/about" },
      { label: "新闻动态", href: "/news" },
      { label: "成功案例", href: "/cases" },
      { label: "联系我们", href: "/contact" },
    ],
  };

  return (
    <footer className="bg-dark-secondary border-t border-gray-800">
      <div className="container-wide py-12">
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
          <div className="space-y-4">
            <Link href="/" className="flex items-center gap-2">
              <img
                src="/logo.svg"
                alt={COMPANY_INFO.shortName}
                className="h-8 w-auto"
              />
              <span className="text-lg font-bold text-white">
                {COMPANY_INFO.shortName}
              </span>
            </Link>
            <p className="text-sm text-gray-400">
              {COMPANY_INFO.slogan}
            </p>
            <div className="flex gap-4">
              <a
                href="#"
                className="text-gray-400 hover:text-tech-blue transition-colors duration-300"
                aria-label="WeChat"
              >
                <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
                  <path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098 10.16 10.16 0 002.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 01-1.162 1.178A1.17 1.17 0 014.623 7.17c0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18a1.17 1.17 0 01-1.162 1.178 1.17 1.17 0 01-1.162-1.178c0-.651.52-1.18 1.162-1.18zm5.34 2.867c-1.797-.052-3.746.512-5.28 1.786-1.72 1.428-2.687 3.72-1.78 6.22.942 2.453 3.666 4.229 6.884 4.229.826 0 1.622-.12 2.361-.336a.722.722 0 01.598.082l1.584.926a.272.272 0 00.14.047c.134 0 .24-.111.24-.247 0-.06-.023-.12-.038-.177l-.327-1.233a.582.582 0 01-.023-.156.49.49 0 01.201-.398C23.024 18.48 24 16.82 24 14.98c0-3.21-2.931-5.837-7.062-6.122zm-2.036 2.87c.535 0 .969.44.969.982a.976.976 0 01-.969.983.976.976 0 01-.969-.983c0-.542.434-.982.97-.982zm4.844 0c.535 0 .969.44.969.982a.976.976 0 01-.969.983.976.976 0 01-.969-.983c0-.542.434-.982.97-.982z" />
                </svg>
              </a>
              <a
                href="#"
                className="text-gray-400 hover:text-tech-blue transition-colors duration-300"
                aria-label="Weibo"
              >
                <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
                  <path d="M10.098 20.323c-3.977.391-7.414-1.406-7.672-4.02-.259-2.609 2.759-5.047 6.74-5.441 3.979-.394 7.413 1.404 7.671 4.018.259 2.6-2.759 5.049-6.737 5.439l-.002.004zM9.05 17.219c-.384.616-1.208.884-1.829.602-.612-.279-.793-.991-.406-1.593.379-.595 1.176-.861 1.793-.601.622.263.82.972.442 1.592zm1.27-1.627c-.141.237-.449.353-.689.253-.236-.09-.313-.361-.177-.586.138-.227.436-.346.672-.24.239.09.315.36.18.573h.014zm.176-2.719c-1.893-.493-4.033.45-4.857 2.118-.836 1.704-.026 3.591 1.886 4.21 1.983.64 4.318-.341 5.132-2.179.8-1.793-.201-3.642-2.161-4.149zm7.563-1.224c-.346-.105-.579-.18-.405-.649.381-1.017.422-1.896-.001-2.521-.792-1.163-2.951-1.102-5.418-.03 0 0-.776.34-.578-.275.381-1.217.324-2.236-.27-2.82-1.349-1.326-4.935.05-8.012 3.073C.417 11.473-.27 14.034-.27 16.218c0 4.179 5.37 6.717 10.624 6.717 6.886 0 11.469-3.997 11.469-7.169 0-1.917-1.618-3.004-3.764-3.617h.002zm2.029-5.47c-.439-.511-1.09-.744-1.741-.653-.651.091-1.178.488-1.434 1.065-.256.577-.197 1.23.157 1.742.354.512.941.812 1.592.812.651 0 1.238-.3 1.592-.812.354-.512.413-1.165.157-1.742-.256-.577-.783-.974-1.434-1.065-.651-.091-1.302.142-1.741.653h-.003v.003z" />
                </svg>
              </a>
            </div>
          </div>

          <div>
            <h3 className="text-white font-semibold mb-4">产品服务</h3>
            <ul className="space-y-2">
              {footerLinks.products.map((link) => (
                <li key={link.href}>
                  <Link
                    href={link.href}
                    className="text-sm text-gray-400 hover:text-tech-blue transition-colors duration-300"
                  >
                    {link.label}
                  </Link>
                </li>
              ))}
            </ul>
          </div>

          <div>
            <h3 className="text-white font-semibold mb-4">解决方案</h3>
            <ul className="space-y-2">
              {footerLinks.solutions.map((link) => (
                <li key={link.href}>
                  <Link
                    href={link.href}
                    className="text-sm text-gray-400 hover:text-tech-blue transition-colors duration-300"
                  >
                    {link.label}
                  </Link>
                </li>
              ))}
            </ul>
          </div>

          <div>
            <h3 className="text-white font-semibold mb-4">关于我们</h3>
            <ul className="space-y-2">
              {footerLinks.company.map((link) => (
                <li key={link.href}>
                  <Link
                    href={link.href}
                    className="text-sm text-gray-400 hover:text-tech-blue transition-colors duration-300"
                  >
                    {link.label}
                  </Link>
                </li>
              ))}
            </ul>
          </div>
        </div>

        <div className="mt-12 pt-8 border-t border-gray-800">
          <div className="flex flex-col md:flex-row justify-between items-center gap-4">
            <p className="text-sm text-gray-400">
              © {currentYear} {COMPANY_INFO.name}. All rights reserved.
            </p>
            <div className="flex gap-6">
              <Link
                href="/privacy"
                className="text-sm text-gray-400 hover:text-tech-blue transition-colors duration-300"
              >
                隐私政策
              </Link>
              <Link
                href="/terms"
                className="text-sm text-gray-400 hover:text-tech-blue transition-colors duration-300"
              >
                服务条款
              </Link>
            </div>
          </div>
        </div>
      </div>
    </footer>
  );
}

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交页脚组件

Run:

git add src/components/layout/footer.tsx
git commit -m "feat: refactor footer with dark theme and improved layout"

Expected: 提交成功


阶段四:特效组件开发

Task 12: 创建粒子背景组件

Files:

  • Create: src/components/effects/particle-background.tsx

Step 1: 创建粒子背景组件

Create: src/components/effects/particle-background.tsx

"use client";

import { useEffect, useRef } from "react";
import { randomBetween } from "@/lib/utils";

interface Particle {
  x: number;
  y: number;
  vx: number;
  vy: number;
  size: number;
  color: string;
  opacity: number;
}

interface ParticleBackgroundProps {
  particleCount?: number;
  colors?: string[];
  minSize?: number;
  maxSize?: number;
  speed?: number;
  className?: string;
}

export function ParticleBackground({
  particleCount = 50,
  colors = ["#00D9FF", "#A855F7", "#06B6D4"],
  minSize = 1,
  maxSize = 3,
  speed = 0.5,
  className = "",
}: ParticleBackgroundProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const particlesRef = useRef<Particle[]>([]);
  const animationRef = useRef<number>();

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const resizeCanvas = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    };

    resizeCanvas();
    window.addEventListener("resize", resizeCanvas);

    particlesRef.current = Array.from({ length: particleCount }, () => ({
      x: randomBetween(0, canvas.width),
      y: randomBetween(0, canvas.height),
      vx: randomBetween(-speed, speed),
      vy: randomBetween(-speed, speed),
      size: randomBetween(minSize, maxSize),
      color: colors[Math.floor(Math.random() * colors.length)],
      opacity: randomBetween(0.1, 0.5),
    }));

    const animate = () => {
      if (!ctx || !canvas) return;

      ctx.clearRect(0, 0, canvas.width, canvas.height);

      particlesRef.current.forEach((particle) => {
        particle.x += particle.vx;
        particle.y += particle.vy;

        if (particle.x < 0 || particle.x > canvas.width) particle.vx *= -1;
        if (particle.y < 0 || particle.y > canvas.height) particle.vy *= -1;

        ctx.beginPath();
        ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
        ctx.fillStyle = particle.color;
        ctx.globalAlpha = particle.opacity;
        ctx.fill();
      });

      ctx.globalAlpha = 1;
      animationRef.current = requestAnimationFrame(animate);
    };

    animate();

    return () => {
      window.removeEventListener("resize", resizeCanvas);
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
    };
  }, [particleCount, colors, minSize, maxSize, speed]);

  return (
    <canvas
      ref={canvasRef}
      className={`absolute inset-0 pointer-events-none ${className}`}
      style={{ zIndex: 0 }}
    />
  );
}

Step 2: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 3: 提交粒子背景组件

Run:

git add src/components/effects/particle-background.tsx
git commit -m "feat: create particle background component for hero section"

Expected: 提交成功


Task 13: 创建发光效果组件

Files:

  • Create: src/components/effects/glow-effect.tsx

Step 1: 创建发光效果组件

Create: src/components/effects/glow-effect.tsx

"use client";

import { cn } from "@/lib/utils";

interface GlowEffectProps {
  color?: string;
  size?: "sm" | "md" | "lg" | "xl";
  className?: string;
  children?: React.ReactNode;
}

const sizeMap = {
  sm: "w-32 h-32",
  md: "w-48 h-48",
  lg: "w-64 h-64",
  xl: "w-96 h-96",
};

export function GlowEffect({
  color = "#00D9FF",
  size = "lg",
  className,
  children,
}: GlowEffectProps) {
  return (
    <div className={cn("relative", className)}>
      <div
        className={cn(
          "absolute rounded-full blur-3xl opacity-20 animate-pulse",
          sizeMap[size]
        )}
        style={{
          background: `radial-gradient(circle, ${color}40 0%, transparent 70%)`,
        }}
      />
      {children}
    </div>
  );
}

interface GradientOrbProps {
  position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
  color?: string;
  className?: string;
}

const positionMap = {
  "top-left": "top-0 left-0",
  "top-right": "top-0 right-0",
  "bottom-left": "bottom-0 left-0",
  "bottom-right": "bottom-0 right-0",
};

export function GradientOrb({
  position = "top-right",
  color = "#00D9FF",
  className,
}: GradientOrbProps) {
  return (
    <div
      className={cn(
        "absolute w-96 h-96 rounded-full blur-3xl opacity-5 pointer-events-none",
        positionMap[position],
        className
      )}
      style={{
        background: `radial-gradient(circle, ${color} 0%, transparent 70%)`,
      }}
    />
  );
}

Step 2: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 3: 提交发光效果组件

Run:

git add src/components/effects/glow-effect.tsx
git commit -m "feat: create glow effect and gradient orb components"

Expected: 提交成功


阶段五:首页重构

Task 14: 重构 Hero Section

Files:

  • Modify: src/components/sections/hero-section.tsx

Step 1: 读取现有 Hero Section

Read: src/components/sections/hero-section.tsx

Step 2: 重写 Hero Section 以支持深色主题和动画

Modify: src/components/sections/hero-section.tsx

"use client";

import { motion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { ParticleBackground } from "@/components/effects/particle-background";
import { GradientOrb } from "@/components/effects/glow-effect";
import { COMPANY_INFO } from "@/lib/constants";
import { ArrowRight, Sparkles } from "lucide-react";

export function HeroSection() {
  return (
    <section className="relative min-h-screen flex items-center justify-center overflow-hidden pt-16">
      <ParticleBackground
        particleCount={80}
        colors={["#00D9FF", "#A855F7", "#06B6D4"]}
        minSize={1}
        maxSize={4}
        speed={0.3}
      />

      <GradientOrb position="top-right" color="#00D9FF" />
      <GradientOrb position="bottom-left" color="#A855F7" />

      <div className="container-wide relative z-10 py-24 md:py-32 lg:py-40">
        <div className="max-w-4xl mx-auto text-center">
          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6 }}
            className="mb-6"
          >
            <span className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-tech-blue/10 border border-tech-blue/20 text-tech-blue text-sm font-medium">
              <Sparkles className="w-4 h-4" />
              专注科技创新
            </span>
          </motion.div>

          <motion.h1
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6, delay: 0.1 }}
            className="text-4xl md:text-5xl lg:text-6xl font-bold text-white mb-6 leading-tight"
          >
            {COMPANY_INFO.slogan}
          </motion.h1>

          <motion.p
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6, delay: 0.2 }}
            className="text-lg md:text-xl text-gray-400 mb-8 max-w-2xl mx-auto"
          >
            {COMPANY_INFO.description}
          </motion.p>

          <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.6, delay: 0.3 }}
            className="flex flex-col sm:flex-row gap-4 justify-center"
          >
            <Button size="lg" className="group">
              立即体验
              <ArrowRight className="ml-2 w-4 h-4 group-hover:translate-x-1 transition-transform" />
            </Button>
            <Button size="lg" variant="secondary">
              了解更多
            </Button>
          </motion.div>
        </div>
      </div>

      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 1, delay: 0.5 }}
        className="absolute bottom-8 left-1/2 -translate-x-1/2"
      >
        <div className="flex flex-col items-center gap-2 text-gray-400">
          <span className="text-sm">向下滚动</span>
          <div className="w-6 h-10 rounded-full border-2 border-gray-600 flex justify-center pt-2">
            <motion.div
              animate={{ y: [0, 12, 0] }}
              transition={{ duration: 1.5, repeat: Infinity }}
              className="w-1.5 h-1.5 bg-tech-blue rounded-full"
            />
          </div>
        </div>
      </motion.div>
    </section>
  );
}

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交 Hero Section

Run:

git add src/components/sections/hero-section.tsx
git commit -m "feat: refactor hero section with dark theme and animations"

Expected: 提交成功


Task 15: 创建统计数据 Section

Files:

  • Create: src/components/sections/stats-section.tsx

Step 1: 创建统计数据 Section

Create: src/components/sections/stats-section.tsx

"use client";

import { motion } from "framer-motion";
import { STATS } from "@/lib/constants";

export function StatsSection() {
  return (
    <section className="py-20 bg-dark-secondary">
      <div className="container-wide">
        <div className="grid grid-cols-2 md:grid-cols-4 gap-8">
          {STATS.map((stat, index) => (
            <motion.div
              key={stat.label}
              initial={{ opacity: 0, y: 20 }}
              whileInView={{ opacity: 1, y: 0 }}
              viewport={{ once: true }}
              transition={{ duration: 0.6, delay: index * 0.1 }}
              className="text-center"
            >
              <div className="text-4xl md:text-5xl font-bold text-tech-blue mb-2">
                {stat.value}
              </div>
              <div className="text-gray-400 text-sm md:text-base">
                {stat.label}
              </div>
            </motion.div>
          ))}
        </div>
      </div>
    </section>
  );
}

Step 2: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 3: 提交统计数据 Section

Run:

git add src/components/sections/stats-section.tsx
git commit -m "feat: create stats section with animated counters"

Expected: 提交成功


Task 16: 重构产品展示 Section

Files:

  • Modify: src/components/sections/products-section.tsx

Step 1: 读取现有产品展示 Section

Read: src/components/sections/products-section.tsx

Step 2: 重写产品展示 Section 以支持深色主题和卡片效果

Modify: src/components/sections/products-section.tsx

"use client";

import { motion } from "framer-motion";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { PRODUCTS } from "@/lib/constants";
import { ArrowRight } from "lucide-react";

export function ProductsSection() {
  return (
    <section className="py-20 bg-dark">
      <div className="container-wide">
        <motion.div
          initial={{ opacity: 0, y: 20 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true }}
          transition={{ duration: 0.6 }}
          className="text-center mb-12"
        >
          <h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
            产品服务
          </h2>
          <p className="text-gray-400 max-w-2xl mx-auto">
            为企业提供全方位的数字化解决方案,助力业务增长
          </p>
        </motion.div>

        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
          {PRODUCTS.slice(0, 6).map((product, index) => (
            <motion.div
              key={product.id}
              initial={{ opacity: 0, y: 20 }}
              whileInView={{ opacity: 1, y: 0 }}
              viewport={{ once: true }}
              transition={{ duration: 0.6, delay: index * 0.1 }}
            >
              <Card variant="hover" className="h-full">
                <CardHeader>
                  <div className="flex items-start justify-between mb-2">
                    <Badge variant="default">{product.category}</Badge>
                  </div>
                  <CardTitle className="text-xl">{product.title}</CardTitle>
                </CardHeader>
                <CardContent>
                  <CardDescription className="mb-4">
                    {product.description}
                  </CardDescription>
                  <div className="flex flex-wrap gap-2 mb-4">
                    {product.features.slice(0, 3).map((feature) => (
                      <Badge key={feature} variant="outline" className="text-xs">
                        {feature}
                      </Badge>
                    ))}
                  </div>
                  <a
                    href={`/products/${product.id}`}
                    className="inline-flex items-center text-tech-blue hover:text-tech-purple transition-colors duration-300 text-sm font-medium group"
                  >
                    了解更多
                    <ArrowRight className="ml-1 w-4 h-4 group-hover:translate-x-1 transition-transform" />
                  </a>
                </CardContent>
              </Card>
            </motion.div>
          ))}
        </div>
      </div>
    </section>
  );
}

Step 3: 验证类型检查

Run: npx tsc --noEmit

Expected: 无类型错误

Step 4: 提交产品展示 Section

Run:

git add src/components/sections/products-section.tsx
git commit -m "feat: refactor products section with dark theme and card layout"

Expected: 提交成功


阶段六:测试与验证

Task 17: 运行开发服务器测试

Files:

  • None

Step 1: 启动开发服务器

Run: npm run dev

Expected: 开发服务器成功启动在 http://localhost:3000

Step 2: 在浏览器中验证首页

手动测试:

  • 访问 http://localhost:3000
  • 验证深色主题是否正确应用
  • 验证导航栏是否正常工作
  • 验证 Hero Section 动画是否流畅
  • 验证产品卡片悬停效果
  • 验证响应式布局(移动端、平板、桌面)

Expected: 所有功能正常工作

Step 3: 停止开发服务器

按 Ctrl+C 停止服务器

Expected: 服务器成功停止


Task 18: 运行构建测试

Files:

  • None

Step 1: 运行生产构建

Run: npm run build

Expected: 构建成功,无错误

Step 2: 检查构建输出

验证构建输出中:

  • 页面是否正确生成
  • 是否有优化建议
  • 构建大小是否合理

Expected: 构建输出正常

Step 3: 运行代码检查

Run: npm run lint

Expected: 无 ESLint 错误


Task 19: 提交所有更改

Files:

  • None

Step 1: 检查 Git 状态

Run: git status

Expected: 显示所有修改的文件

Step 2: 添加所有更改

Run: git add .

Expected: 所有更改已暂存

Step 3: 创建提交

Run:

git commit -m "feat: complete website redesign with dark tech theme

- Add dark theme color system and CSS variables
- Enhance UI components (Button, Card, Input, Badge)
- Refactor layout components (Header, Footer)
- Create effect components (ParticleBackground, GlowEffect)
- Refactor sections (Hero, Stats, Products)
- Add Framer Motion animations
- Improve responsive design
- Update Tailwind configuration"

Expected: 提交成功

Step 4: 推送到远程

Run: git push origin feature/website-redesign

Expected: 推送成功


完成清单

  • 安装必要依赖(framer-motion, @antv/g2, class-variance-authority
  • 扩展 Tailwind 配置
  • 更新全局样式
  • 创建工具函数
  • 创建主题常量
  • 增强按钮组件
  • 增强卡片组件
  • 增强输入框组件
  • 增强徽章组件
  • 重构导航栏组件
  • 重构页脚组件
  • 创建粒子背景组件
  • 创建发光效果组件
  • 重构 Hero Section
  • 创建统计数据 Section
  • 重构产品展示 Section
  • 运行开发服务器测试
  • 运行构建测试
  • 提交所有更改

注意事项

  1. 每个任务都应该独立完成并提交,便于版本控制和回滚
  2. 遇到错误立即停止,不要继续下一步
  3. 保持代码风格一致,遵循项目现有的代码规范
  4. 测试每个组件,确保在移动端和桌面端都能正常工作
  5. 关注性能,避免过度使用动画和特效

计划创建者: AI Assistant
创建日期: 2026-02-21
预计工期: 2-3天(基础架构 + 核心组件 + 首页重构)