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

1801 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```bash
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`
```typescript
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:
```bash
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`
在文件顶部添加:
```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:
```bash
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`
```typescript
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:
```bash
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`
在文件末尾添加:
```typescript
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:
```bash
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`
```typescript
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:
```bash
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`
```typescript
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:
```bash
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`
```typescript
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:
```bash
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`
```typescript
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:
```bash
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`
```typescript
"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:
```bash
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`
```typescript
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:
```bash
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`
```typescript
"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:
```bash
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`
```typescript
"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:
```bash
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`
```typescript
"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:
```bash
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`
```typescript
"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:
```bash
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`
```typescript
"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:
```bash
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:
```bash
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天(基础架构 + 核心组件 + 首页重构)