Files
novalon-website/docs/plans/2026-02-26-website-optimization-implementation.md
T

1631 lines
53 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.
# 网站优化重构实施计划
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 统一网站交互模式、优化首页结构、统一配色方案,提升用户体验和品牌一致性。
**Architecture:** 采用 Next.js App Router 架构,服务详情页使用故事化叙事风格,移除模态框交互,统一使用独立页面。首页精简 About Section,新增 Cases Section。
**Tech Stack:** Next.js 16, React 19, TypeScript, Tailwind CSS, Framer Motion
---
## 实施阶段概览
### 阶段 1: 服务详情页重构(高优先级)
- 创建服务详情页面
- 删除服务详情模态框
- 修改服务卡片添加链接
### 阶段 2: 首页结构优化(高优先级)
- 精简首页 About Section
- 新增首页 Cases Section
- 更新首页 Section 顺序
### 阶段 3: 导航与链接统一(中优先级)
- 更新导航链接
- 创建服务列表页
- 更新 Footer 链接
### 阶段 4: 配色方案统一(中优先级)
- 移除紫色,统一使用品牌红
- 更新所有相关组件
---
## 阶段 1: 服务详情页重构
### Task 1.1: 创建服务详情页面
**Files:**
- Create: `src/app/(marketing)/services/[id]/page.tsx`
- Create: `src/app/(marketing)/services/[id]/client.tsx`
**Step 1: 创建服务详情客户端组件**
```tsx
// src/app/(marketing)/services/[id]/client.tsx
'use client';
import { useEffect, useRef, useState } from 'react';
import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent } from '@/components/ui/card';
import { PageHeader } from '@/components/ui/page-header';
import {
ArrowLeft,
CheckCircle2,
TrendingUp,
Users,
Target,
Clock,
MessageCircle,
ArrowRight
} from 'lucide-react';
import { SERVICES, CASES } from '@/lib/constants';
interface ServiceDetailClientProps {
service: typeof SERVICES[number];
}
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Code: () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
),
Cloud: () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z" />
</svg>
),
BarChart3: () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
),
Shield: () => (
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
</svg>
),
};
const challenges = {
software: [
{ title: '需求不明确', description: '业务部门提不出清晰需求,开发团队反复返工' },
{ title: '技术选型困难', description: '技术栈更新快,不知道该选什么技术方案' },
{ title: '项目延期', description: '开发进度难以把控,上线时间一拖再拖' },
{ title: '维护成本高', description: '系统上线后问题不断,运维压力巨大' },
],
cloud: [
{ title: '资源浪费', description: '服务器资源利用率低,成本居高不下' },
{ title: '扩展困难', description: '业务增长时系统无法快速扩容' },
{ title: '迁移风险', description: '担心数据丢失、业务中断' },
{ title: '安全顾虑', description: '不确定云端数据是否安全' },
],
data: [
{ title: '数据孤岛', description: '各系统数据分散,无法整合分析' },
{ title: '决策盲区', description: '缺乏数据支撑,决策凭感觉' },
{ title: '报表滞后', description: '手工制作报表,时效性差' },
{ title: '价值难挖', description: '数据很多,但不知道怎么用' },
],
security: [
{ title: '安全漏洞', description: '系统存在未知漏洞,随时可能被攻击' },
{ title: '合规压力', description: '监管要求越来越严,不知如何应对' },
{ title: '内部威胁', description: '员工操作不规范,数据泄露风险' },
{ title: '应急能力弱', description: '安全事件发生后不知所措' },
],
};
const outcomes = {
software: [
{ value: '30%', label: '开发效率提升' },
{ value: '50%', label: '返工率降低' },
{ value: '100%', label: '按时交付率' },
],
cloud: [
{ value: '40%', label: '成本降低' },
{ value: '99.9%', label: '可用性保障' },
{ value: '10x', label: '弹性扩展能力' },
],
data: [
{ value: '70%', label: '决策效率提升' },
{ value: '实时', label: '数据更新' },
{ value: '100+', label: '可视化报表' },
],
security: [
{ value: '99%', label: '漏洞修复率' },
{ value: '100%', label: '合规达标' },
{ value: '24/7', label: '安全监控' },
],
};
export function ServiceDetailClient({ service }: ServiceDetailClientProps) {
const [isVisible, setIsVisible] = useState(false);
const contentRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry?.isIntersecting) {
setIsVisible(true);
}
},
{ threshold: 0.1 }
);
if (contentRef.current) {
observer.observe(contentRef.current);
}
return () => observer.disconnect();
}, []);
const serviceChallenges = challenges[service.id as keyof typeof challenges] || [];
const serviceOutcomes = outcomes[service.id as keyof typeof outcomes] || [];
const relatedCases = CASES.slice(0, 2);
const Icon = iconMap[service.icon];
return (
<main className="min-h-screen bg-white">
<div className="relative overflow-hidden bg-gradient-to-b from-[#FAFAFA] to-white">
<div className="container-wide relative z-10 pt-32 pb-20">
<Link href="/services">
<Button variant="ghost" className="text-[#5C5C5C] hover:text-[#C41E3A] hover:bg-[#C41E3A]/10">
<ArrowLeft className="w-4 h-4 mr-2" />
</Button>
</Link>
<div className="max-w-4xl mt-8">
<div className="flex items-center gap-4 mb-6">
<div className="w-16 h-16 bg-[#C41E3A] rounded-2xl flex items-center justify-center text-white">
{Icon && <Icon />}
</div>
<div>
<Badge className="mb-2 bg-[#C41E3A]/10 text-[#C41E3A] hover:bg-[#C41E3A]/20">
</Badge>
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-semibold text-[#1C1C1C]">
{service.title}
</h1>
</div>
</div>
<p className="text-lg text-[#5C5C5C] leading-relaxed">
{service.description}
</p>
</div>
</div>
</div>
<div ref={contentRef} className="container-wide py-12 md:py-16">
<div className="max-w-4xl mx-auto space-y-16">
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<MessageCircle className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="grid md:grid-cols-2 gap-4">
{serviceChallenges.map((challenge, index) => (
<div
key={index}
className="p-4 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors"
>
<h3 className="font-semibold text-[#1C1C1C] mb-2">{challenge.title}</h3>
<p className="text-sm text-[#5C5C5C]">{challenge.description}</p>
</div>
))}
</div>
</section>
<section className="bg-gradient-to-br from-[#F5F7FA] to-white rounded-2xl p-8 border border-[#E5E5E5]">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Target className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<p className="text-lg text-[#5C5C5C] leading-relaxed mb-6">
{service.overview}
</p>
<div className="space-y-3">
{service.features.map((feature, index) => (
<div key={index} className="flex items-start gap-3">
<CheckCircle2 className="w-5 h-5 text-[#C41E3A] mt-0.5 flex-shrink-0" />
<span className="text-[#1C1C1C]">{feature}</span>
</div>
))}
</div>
</section>
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Clock className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="space-y-4">
{service.process.map((step, index) => (
<div key={index} className="flex items-start gap-4">
<div className="w-10 h-10 bg-[#C41E3A] rounded-full flex items-center justify-center flex-shrink-0 text-white font-semibold">
{index + 1}
</div>
<div className="flex-1 pb-4">
<p className="text-[#1C1C1C] font-medium">{step}</p>
</div>
</div>
))}
</div>
</section>
<section className="bg-gradient-to-br from-[#F5F7FA] to-white rounded-2xl p-8 border border-[#E5E5E5]">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<TrendingUp className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="grid sm:grid-cols-3 gap-4">
{serviceOutcomes.map((outcome, index) => (
<div
key={index}
className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors text-center"
>
<div className="text-3xl font-bold text-[#C41E3A] mb-2">
{outcome.value}
</div>
<div className="text-sm text-[#5C5C5C]">{outcome.label}</div>
</div>
))}
</div>
<div className="mt-6 p-4 bg-white rounded-lg border border-[#E5E5E5]">
<p className="text-sm text-[#5C5C5C]">
{service.benefits.map(b => b).join('')}
</p>
</div>
</section>
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[#C41E3A]/20">
<div className="flex items-center gap-3 mb-6">
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
<Users className="w-6 h-6 text-white" />
</div>
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
</h2>
</div>
<div className="grid md:grid-cols-2 gap-4">
{relatedCases.map((caseItem) => (
<Link
key={caseItem.id}
href={`/cases/${caseItem.id}`}
className="group p-4 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors"
>
<Badge variant="secondary" className="mb-2">
{caseItem.industry}
</Badge>
<h3 className="font-semibold text-[#1C1C1C] group-hover:text-[#C41E3A] transition-colors">
{caseItem.title}
</h3>
<p className="text-sm text-[#5C5C5C] mt-2 line-clamp-2">
{caseItem.description}
</p>
</Link>
))}
</div>
</section>
<div className="flex justify-center gap-4 pt-8 border-t border-[#E5E5E5]">
<Button variant="outline" size="lg" asChild>
<Link href="/services">
</Link>
</Button>
<Button size="lg" className="bg-[#C41E3A] hover:bg-[#A01830] text-white" asChild>
<Link href="/contact">
<ArrowRight className="ml-2 w-4 h-4" />
</Link>
</Button>
</div>
</div>
</div>
</main>
);
}
```
**Step 2: 创建服务详情页面入口**
```tsx
// src/app/(marketing)/services/[id]/page.tsx
import { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { SERVICES } from '@/lib/constants';
import { ServiceDetailClient } from './client';
export async function generateStaticParams() {
return SERVICES.map((service) => ({
id: service.id,
}));
}
export async function generateMetadata({ params }: { params: Promise<{ id: string }> }): Promise<Metadata> {
const { id } = await params;
const service = SERVICES.find((s) => s.id === id);
if (!service) {
return {
title: '服务未找到',
};
}
return {
title: `${service.title} - 睿新致远`,
description: service.description,
};
}
export default async function ServiceDetailPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const service = SERVICES.find((s) => s.id === id);
if (!service) {
notFound();
}
return <ServiceDetailClient service={service} />;
}
```
**Step 3: 验证文件创建成功**
Run: `ls -la src/app/\(marketing\)/services/`
Expected: 目录和文件已创建
**Step 4: Commit**
```bash
git add src/app/\(marketing\)/services/
git commit -m "feat: add service detail page with storytelling style"
```
---
### Task 1.2: 创建服务列表页面
**Files:**
- Create: `src/app/(marketing)/services/page.tsx`
**Step 1: 创建服务列表页面**
```tsx
// src/app/(marketing)/services/page.tsx
'use client';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { useInView } from 'framer-motion';
import { useRef } from 'react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent } from '@/components/ui/card';
import { PageHeader } from '@/components/ui/page-header';
import { ArrowRight, Code, Cloud, BarChart3, Shield } from 'lucide-react';
import { SERVICES } from '@/lib/constants';
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Code,
Cloud,
BarChart3,
Shield,
};
export default function ServicesPage() {
const contentRef = useRef(null);
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
return (
<div className="min-h-screen bg-white">
<PageHeader
title="核心业务"
description="专业技术团队,为您提供全方位的数字化解决方案"
/>
<div className="container-wide relative z-10 py-16" ref={contentRef}>
<div className="max-w-6xl mx-auto">
<div className="grid md:grid-cols-2 gap-8">
{SERVICES.map((service, index) => {
const Icon = iconMap[service.icon];
return (
<motion.div
key={service.id}
initial={{ opacity: 0, y: 20 }}
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Link
href={`/services/${service.id}`}
className="group bg-white rounded-2xl border border-[#E5E5E5] overflow-hidden hover:shadow-xl transition-all duration-300 block h-full"
>
<div className="p-8">
<div className="flex items-start gap-4 mb-4">
<div className="w-14 h-14 rounded-xl bg-[#F5F5F5] flex items-center justify-center group-hover:bg-[#C41E3A] transition-all duration-300">
{Icon && <Icon className="w-7 h-7 text-[#1C1C1C] group-hover:text-white transition-colors" />}
</div>
<div className="flex-1">
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-2 group-hover:text-[#C41E3A] transition-colors">
{service.title}
</h3>
<p className="text-[#5C5C5C] text-sm leading-relaxed">
{service.description}
</p>
</div>
</div>
<div className="mt-6 pt-4 border-t border-[#E5E5E5]">
<div className="flex flex-wrap gap-2 mb-4">
{service.features.slice(0, 3).map((feature, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{feature.split('')[0]}
</Badge>
))}
</div>
<div className="flex items-center text-[#C41E3A] font-medium group-hover:translate-x-2 transition-transform">
<ArrowRight className="w-4 h-4 ml-2" />
</div>
</div>
</div>
</Link>
</motion.div>
);
})}
</div>
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.4 }}
className="bg-[#F5F5F5] py-16"
>
<div className="container-wide text-center">
<h2 className="text-3xl font-bold text-[#1C1C1C] mb-6">
</h2>
<p className="text-lg text-[#5C5C5C] mb-8 max-w-2xl mx-auto">
</p>
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
asChild
>
<Link href="/contact">
<ArrowRight className="ml-2 w-4 h-4" />
</Link>
</Button>
</div>
</motion.div>
</div>
);
}
```
**Step 2: 验证文件创建成功**
Run: `ls -la src/app/\(marketing\)/services/page.tsx`
Expected: 文件已创建
**Step 3: Commit**
```bash
git add src/app/\(marketing\)/services/page.tsx
git commit -m "feat: add services list page"
```
---
### Task 1.3: 修改服务卡片组件添加链接
**Files:**
- Modify: `src/components/sections/services-section.tsx`
**Step 1: 移除模态框相关代码,添加链接**
Read: `src/components/sections/services-section.tsx`
Replace entire file with:
```tsx
'use client';
import { motion } from 'framer-motion';
import { useInView } from 'framer-motion';
import { useRef } from 'react';
import Link from 'next/link';
import { Code, Cloud, BarChart3, Shield, ArrowRight } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { SERVICES } from '@/lib/constants';
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Code,
Cloud,
BarChart3,
Shield,
};
export function ServicesSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section id="solutions" aria-labelledby="solutions-heading" className="py-24 bg-white relative overflow-hidden" ref={ref}>
<div className="absolute top-1/3 left-0 w-[400px] h-[400px] bg-[rgba(196,30,58,0.03)] rounded-full blur-3xl" />
<div className="absolute top-1/3 right-0 w-[300px] h-[300px] bg-[rgba(196,30,58,0.02)] rounded-full blur-3xl" />
<div className="container-wide relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-center max-w-3xl mx-auto mb-16"
>
<h2 id="solutions-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
<span className="text-[#C41E3A]"></span>
</h2>
<p className="text-lg text-[#5C5C5C] max-w-2xl mx-auto">
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{SERVICES.map((service, index) => {
const Icon = iconMap[service.icon];
return (
<motion.div
key={service.id}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Link href={`/services/${service.id}`}>
<Card className="p-6 h-full group cursor-pointer border-[#E5E5E5] hover:border-[#C41E3A] transition-colors">
<CardContent className="p-0">
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] flex items-center justify-center mb-4 group-hover:bg-[#C41E3A] transition-all duration-300">
{Icon && <Icon className="w-6 h-6 text-[#1C1C1C] group-hover:text-white transition-colors" />}
</div>
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3 group-hover:text-[#C41E3A] transition-colors">{service.title}</h3>
<p className="text-[#5C5C5C] text-sm leading-relaxed">{service.description}</p>
<div className="mt-4 flex items-center text-[#C41E3A] text-sm font-medium opacity-0 group-hover:opacity-100 transition-opacity">
<ArrowRight className="ml-1 w-4 h-4" />
</div>
</CardContent>
</Card>
</Link>
</motion.div>
);
})}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.4 }}
className="text-center mt-12"
>
<Button variant="outline" size="lg" className="group" asChild>
<Link href="/services">
<ArrowRight className="ml-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>
</Button>
</motion.div>
</div>
</section>
);
}
```
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/components/sections/services-section.tsx
git commit -m "refactor: remove modal, add link navigation for services"
```
---
### Task 1.4: 删除服务详情模态框组件
**Files:**
- Delete: `src/components/services/service-detail-modal.tsx`
**Step 1: 删除模态框组件文件**
Run: `rm src/components/services/service-detail-modal.tsx`
**Step 2: 验证删除成功**
Run: `ls src/components/services/`
Expected: 目录为空或不存在
**Step 3: 如果目录为空,删除目录**
Run: `rmdir src/components/services/ 2>/dev/null || true`
**Step 4: Commit**
```bash
git add -A
git commit -m "refactor: remove service detail modal component"
```
---
## 阶段 2: 首页结构优化
### Task 2.1: 精简首页 About Section
**Files:**
- Modify: `src/components/sections/about-section.tsx`
**Step 1: 精简 About Section 内容**
Read: `src/components/sections/about-section.tsx`
Replace entire file with:
```tsx
'use client';
import { motion } from 'framer-motion';
import { useInView } from 'framer-motion';
import { useRef } from 'react';
import Link from 'next/link';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { COMPANY_INFO, STATS } from '@/lib/constants';
import { ArrowRight } from 'lucide-react';
export function AboutSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
return (
<section id="about" className="py-24 bg-[#FAFAFA] relative" ref={ref}>
<div className="absolute inset-0 bg-[linear-gradient(rgba(28,28,28,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(28,28,28,0.02)_1px,transparent_1px)] bg-[size:40px_40px]" />
<div className="container-wide relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="max-w-4xl mx-auto"
>
<div className="text-center mb-12">
<h2 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
<span className="tracking-tight font-calligraphy text-[#C41E3A]" style={{ fontWeight: 'normal', WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', textRendering: 'optimizeLegibility' }}>{COMPANY_INFO.shortName}</span>
</h2>
<p className="text-lg text-[#5C5C5C] mb-8">
{COMPANY_INFO.slogan}
</p>
</div>
<div className="bg-white rounded-2xl p-8 mb-12 border border-[#E5E5E5]">
<p className="text-lg text-[#5C5C5C] leading-relaxed text-center mb-6">
"企业需要的,不是一个高高在上的'专家',也不是一个做完就跑的'卖家',而是一个能坐下来、一起想办法的同行者。"
</p>
<p className="text-[#1C1C1C] font-medium text-center">
</p>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.2 }}
className="grid grid-cols-2 md:grid-cols-4 gap-6 mb-12"
>
{STATS.map((stat, idx) => (
<Card key={idx} className="text-center border-[#E5E5E5]">
<CardContent className="pt-6">
<div className="text-3xl sm:text-4xl font-bold text-[#C41E3A] mb-2">{stat.value}</div>
<div className="text-sm text-[#5C5C5C]">{stat.label}</div>
</CardContent>
</Card>
))}
</motion.div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.3 }}
className="text-center"
>
<Button size="lg" variant="outline" className="group" asChild>
<Link href="/about">
<ArrowRight className="ml-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>
</Button>
</motion.div>
</motion.div>
</div>
</section>
);
}
```
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/components/sections/about-section.tsx
git commit -m "refactor: simplify about section on homepage"
```
---
### Task 2.2: 创建首页 Cases Section
**Files:**
- Create: `src/components/sections/cases-section.tsx`
**Step 1: 创建 Cases Section 组件**
```tsx
// src/components/sections/cases-section.tsx
'use client';
import { motion } from 'framer-motion';
import { useInView } from 'framer-motion';
import { useRef } from 'react';
import Link from 'next/link';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { CASES } from '@/lib/constants';
import { ArrowRight, Building2, TrendingUp } from 'lucide-react';
export function CasesSection() {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, margin: '-100px' });
const featuredCases = CASES.slice(0, 3);
return (
<section id="cases" className="py-24 bg-white relative overflow-hidden" ref={ref}>
<div className="absolute top-1/3 left-0 w-[400px] h-[400px] bg-[rgba(196,30,58,0.03)] rounded-full blur-3xl" />
<div className="absolute top-1/3 right-0 w-[300px] h-[300px] bg-[rgba(196,30,58,0.02)] rounded-full blur-3xl" />
<div className="container-wide relative z-10">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="text-center max-w-3xl mx-auto mb-16"
>
<h2 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
<span className="text-[#C41E3A]"></span>
</h2>
<p className="text-lg text-[#5C5C5C] max-w-2xl mx-auto">
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{featuredCases.map((caseItem, index) => (
<motion.div
key={caseItem.id}
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: 0.1 + index * 0.1 }}
>
<Link href={`/cases/${caseItem.id}`}>
<Card className="h-full group cursor-pointer border-[#E5E5E5] hover:border-[#C41E3A] transition-colors overflow-hidden">
<div className="relative h-40 bg-gradient-to-br from-[#F5F5F5] to-[#E5E5E5] flex items-center justify-center">
<Building2 className="w-16 h-16 text-[#C41E3A]/20 group-hover:scale-110 transition-transform duration-300" />
<div className="absolute top-4 right-4">
<Badge className="bg-white/90 text-[#1C1C1C] hover:bg-white">
{caseItem.industry}
</Badge>
</div>
</div>
<CardContent className="p-6">
<div className="flex items-center gap-2 mb-3">
<Building2 className="w-4 h-4 text-[#C41E3A]" />
<span className="text-sm text-[#5C5C5C]">{caseItem.client}</span>
</div>
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-3 group-hover:text-[#C41E3A] transition-colors">
{caseItem.title}
</h3>
<p className="text-[#5C5C5C] text-sm line-clamp-2 mb-4">
{caseItem.description}
</p>
{caseItem.results.length > 0 && (
<div className="flex items-center gap-2 text-[#C41E3A]">
<TrendingUp className="w-4 h-4" />
<span className="text-sm font-medium">{caseItem.results[0].value}</span>
<span className="text-sm text-[#5C5C5C]">{caseItem.results[0].label}</span>
</div>
)}
</CardContent>
</Card>
</Link>
</motion.div>
))}
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.4 }}
className="text-center mt-12"
>
<Button variant="outline" size="lg" className="group" asChild>
<Link href="/cases">
<ArrowRight className="ml-2 w-4 h-4 transition-transform group-hover:translate-x-1" />
</Link>
</Button>
</motion.div>
</div>
</section>
);
}
```
**Step 2: 验证文件创建成功**
Run: `ls -la src/components/sections/cases-section.tsx`
Expected: 文件已创建
**Step 3: Commit**
```bash
git add src/components/sections/cases-section.tsx
git commit -m "feat: add cases section to homepage"
```
---
### Task 2.3: 更新首页 Section 顺序
**Files:**
- Modify: `src/app/(marketing)/page.tsx`
**Step 1: 更新首页导入和 Section 顺序**
Read: `src/app/(marketing)/page.tsx`
Replace entire file with:
```tsx
"use client";
import dynamic from 'next/dynamic';
import { Header } from "@/components/layout/header";
import { Footer } from "@/components/layout/footer";
import { HeroSection } from "@/components/sections/hero-section";
import { SectionSkeleton } from "@/components/ui/loading-skeleton";
const ServicesSection = dynamic(
() => import('@/components/sections/services-section').then(mod => ({ default: mod.ServicesSection })),
{
loading: () => <SectionSkeleton />,
ssr: true
}
);
const ProductsSection = dynamic(
() => import('@/components/sections/products-section').then(mod => ({ default: mod.ProductsSection })),
{
loading: () => <SectionSkeleton />,
ssr: true
}
);
const CasesSection = dynamic(
() => import('@/components/sections/cases-section').then(mod => ({ default: mod.CasesSection })),
{
loading: () => <SectionSkeleton />,
ssr: true
}
);
const AboutSection = dynamic(
() => import('@/components/sections/about-section').then(mod => ({ default: mod.AboutSection })),
{
loading: () => <SectionSkeleton />,
ssr: true
}
);
const NewsSection = dynamic(
() => import('@/components/sections/news-section').then(mod => ({ default: mod.NewsSection })),
{
loading: () => <SectionSkeleton />,
ssr: true
}
);
const ContactSection = dynamic(
() => import('@/components/sections/contact-section').then(mod => ({ default: mod.ContactSection })),
{
loading: () => <SectionSkeleton />,
ssr: true
}
);
export default function HomePage() {
return (
<main className="min-h-screen bg-white dark:bg-[var(--color-bg-primary)]">
<Header />
<HeroSection />
<ServicesSection />
<ProductsSection />
<CasesSection />
<AboutSection />
<NewsSection />
<ContactSection />
<Footer />
</main>
);
}
```
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/app/\(marketing\)/page.tsx
git commit -m "refactor: update homepage section order with cases section"
```
---
## 阶段 3: 导航与链接统一
### Task 3.1: 更新导航链接
**Files:**
- Modify: `src/lib/constants.ts`
- Modify: `src/components/layout/header.tsx`
**Step 1: 更新导航配置**
Read: `src/lib/constants.ts`
Find the `NAVIGATION` constant and replace with:
```typescript
// Navigation Items - 独立页面导航
export const NAVIGATION = [
{ id: 'home', label: '首页', href: '/' },
{ id: 'services', label: '核心业务', href: '/services' },
{ id: 'products', label: '产品服务', href: '/products' },
{ id: 'about', label: '关于我们', href: '/about' },
{ id: 'news', label: '新闻动态', href: '/news' },
{ id: 'contact', label: '联系我们', href: '/contact' },
] as const;
```
**Step 2: 更新 Header 组件**
Read: `src/components/layout/header.tsx`
Replace the navigation handling logic to use Link instead of anchor scroll:
```tsx
'use client';
import { useState, useEffect, useCallback, useRef } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { Menu, X } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { Button } from '@/components/ui/button';
import { COMPANY_INFO, NAVIGATION } from '@/lib/constants';
import { useFocusTrap } from '@/hooks/use-focus-trap';
export function Header() {
const [isOpen, setIsOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
const pathname = usePathname();
const focusTrapRef = useFocusTrap<HTMLDivElement>(isOpen);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll, { passive: true });
handleScroll();
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(!isOpen);
}
if (e.key === 'Escape' && isOpen) {
setIsOpen(false);
}
}, [isOpen]);
return (
<>
<header
className={`
fixed top-0 left-0 right-0 z-50
transition-all duration-300 ease-out
${isScrolled
? 'bg-white/95 backdrop-blur-xl border-b border-[#E2E8F0] shadow-sm'
: 'bg-transparent'
}
`}
>
<div className="container-wide">
<div className="flex items-center justify-between h-16">
<Link
href="/"
className="flex items-center group focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 rounded-sm"
>
<img
src="/logo.svg"
alt={COMPANY_INFO.name}
className="h-8 w-auto transition-transform duration-200 group-hover:scale-105"
/>
</Link>
<nav className="hidden md:flex items-center gap-1" role="navigation" aria-label="主导航">
{NAVIGATION.map((item) => (
<Link
key={item.id}
href={item.href}
className={`
relative px-3 py-1.5 text-sm font-medium
transition-all duration-300
focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 rounded-sm
${pathname === item.href || (item.href !== '/' && pathname.startsWith(item.href))
? 'text-[#1C1C1C]'
: 'text-[#3D3D3D] hover:text-[#1C1C1C]'
}
`}
aria-current={pathname === item.href ? 'page' : undefined}
>
{item.label}
{(pathname === item.href || (item.href !== '/' && pathname.startsWith(item.href))) && (
<motion.span
layoutId="activeNav"
className="absolute bottom-0 left-1/2 -translate-x-1/2 w-6 h-0.5 bg-[#C41E3A] rounded-full"
transition={{ type: "spring", stiffness: 380, damping: 30 }}
/>
)}
</Link>
))}
</nav>
<div className="hidden md:flex items-center gap-3">
<Button
size="sm"
asChild
>
<Link href="/contact"></Link>
</Button>
</div>
<button
className="md:hidden p-2 -mr-2 text-[#3D3D3D] hover:text-[#1C1C1C] transition-colors focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-offset-2 rounded-sm"
onClick={() => setIsOpen(!isOpen)}
onKeyDown={handleKeyDown}
aria-expanded={isOpen}
aria-controls="mobile-menu"
aria-label={isOpen ? '关闭菜单' : '打开菜单'}
>
{isOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
</div>
</div>
</header>
<AnimatePresence>
{isOpen && (
<motion.div
ref={focusTrapRef}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-40 md:hidden"
>
<div
className="absolute inset-0 bg-black/20 backdrop-blur-sm"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
<motion.div
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -20, opacity: 0 }}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
className="absolute top-16 left-0 right-0 bg-white/95 backdrop-blur-xl border-b border-[#E2E8F0] shadow-lg"
id="mobile-menu"
role="navigation"
aria-label="移动端导航"
>
<nav className="container-wide py-4">
{NAVIGATION.map((item, index) => (
<motion.div
key={item.id}
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: index * 0.05 }}
>
<Link
href={item.href}
onClick={() => setIsOpen(false)}
className={`
block px-4 py-3 text-base font-medium
transition-all duration-300
border-l-2
focus:outline-none focus:ring-2 focus:ring-[#C41E3A] focus:ring-inset
${pathname === item.href || (item.href !== '/' && pathname.startsWith(item.href))
? 'text-[#1C1C1C] border-[#C41E3A] bg-[#FEF2F4]'
: 'text-[#3D3D3D] border-transparent hover:text-[#1C1C1C] hover:bg-[#F5F5F5]'
}
`}
>
{item.label}
</Link>
</motion.div>
))}
<div className="mt-4 px-4 pt-4 border-t border-[#E2E8F0] space-y-3">
<Button
className="w-full"
asChild
>
<Link href="/contact" onClick={() => setIsOpen(false)}>
</Link>
</Button>
</div>
</nav>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
);
}
```
**Step 3: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 4: Commit**
```bash
git add src/lib/constants.ts src/components/layout/header.tsx
git commit -m "refactor: update navigation to use independent page links"
```
---
### Task 3.2: 创建产品列表页面
**Files:**
- Create: `src/app/(marketing)/products/page.tsx`
**Step 1: 创建产品列表页面**
```tsx
// src/app/(marketing)/products/page.tsx
'use client';
import Link from 'next/link';
import { motion } from 'framer-motion';
import { useInView } from 'framer-motion';
import { useRef } from 'react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { PageHeader } from '@/components/ui/page-header';
import { ArrowRight, Check, TrendingUp } from 'lucide-react';
import { PRODUCTS } from '@/lib/constants';
export default function ProductsPage() {
const contentRef = useRef(null);
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
return (
<div className="min-h-screen bg-white">
<PageHeader
title="产品服务"
description="自主研发的企业级产品,助力企业高效运营,实现数字化转型"
/>
<div className="container-wide relative z-10 py-16" ref={contentRef}>
<div className="max-w-6xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
{PRODUCTS.map((product, index) => (
<motion.div
key={product.id}
initial={{ opacity: 0, y: 20 }}
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Link href={`/products/${product.id}`}>
<Card className="h-full group cursor-pointer border-[#E5E5E5] hover:border-[#C41E3A] transition-colors">
<CardHeader>
<Badge variant="secondary" className="w-fit mb-3">
{product.category}
</Badge>
<CardTitle className="group-hover:text-[#C41E3A] transition-colors">{product.title}</CardTitle>
</CardHeader>
<CardContent className="flex-1 flex flex-col">
<CardDescription className="text-base leading-relaxed mb-4 flex-1">
{product.description}
</CardDescription>
<div className="mb-4">
<p className="text-sm font-medium text-[#1C1C1C] mb-2"></p>
<div className="flex flex-wrap gap-1.5">
{product.features.slice(0, 4).map((feature, idx) => (
<span
key={idx}
className="inline-flex items-center text-xs px-2 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded border border-[#E5E5E5]"
>
<Check className="w-3 h-3 mr-1 text-[#C41E3A]" />
{feature}
</span>
))}
</div>
</div>
<div className="mb-4">
<p className="text-sm font-medium text-[#1C1C1C] mb-2 flex items-center">
<TrendingUp className="w-4 h-4 mr-1 text-[#C41E3A]" />
</p>
<ul className="space-y-1">
{product.benefits.map((benefit, idx) => (
<li key={idx} className="text-xs text-[#5C5C5C] flex items-start">
<span className="text-[#C41E3A] mr-1.5"></span>
{benefit}
</li>
))}
</ul>
</div>
<Button variant="outline" className="w-full mt-auto group-hover:bg-[#C41E3A] group-hover:text-white group-hover:border-[#C41E3A] transition-colors">
<ArrowRight className="ml-2 w-4 h-4" />
</Button>
</CardContent>
</Card>
</Link>
</motion.div>
))}
</div>
</div>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6, delay: 0.4 }}
className="bg-[#F5F5F5] py-16"
>
<div className="container-wide text-center">
<h2 className="text-3xl font-bold text-[#1C1C1C] mb-6">
</h2>
<p className="text-lg text-[#5C5C5C] mb-8 max-w-2xl mx-auto">
</p>
<Button
size="lg"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white"
asChild
>
<Link href="/contact">
<ArrowRight className="ml-2 w-4 h-4" />
</Link>
</Button>
</div>
</motion.div>
</div>
);
}
```
**Step 2: 验证文件创建成功**
Run: `ls -la src/app/\(marketing\)/products/page.tsx`
Expected: 文件已创建
**Step 3: Commit**
```bash
git add src/app/\(marketing\)/products/page.tsx
git commit -m "feat: add products list page"
```
---
### Task 3.3: 更新 Footer 链接
**Files:**
- Modify: `src/components/layout/footer.tsx`
**Step 1: 更新 Footer 导航链接**
Read: `src/components/layout/footer.tsx`
Replace the navigation links section with:
```tsx
<div>
<h3 className="font-semibold text-lg mb-6 text-[#1C1C1C]"></h3>
<ul className="space-y-3">
{NAVIGATION.map((item) => (
<li key={item.id}>
<Link
href={item.href}
className="text-[#3D3D3D] hover:text-[#C41E3A] transition-colors"
>
{item.label}
</Link>
</li>
))}
</ul>
</div>
```
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/components/layout/footer.tsx
git commit -m "refactor: update footer navigation links"
```
---
## 阶段 4: 配色方案统一
### Task 4.1: 更新案例详情页配色
**Files:**
- Modify: `src/app/(marketing)/cases/[id]/client.tsx`
**Step 1: 将紫色替换为品牌红**
Read: `src/app/(marketing)/cases/[id]/client.tsx`
Replace all occurrences of:
- `#4F46E5``#C41E3A`
- `rgba(79,70,229,``rgba(196,30,58,`
Key replacements:
```tsx
// Before
<div className="w-12 h-12 bg-[#4F46E5] rounded-xl flex items-center justify-center">
// After
<div className="w-12 h-12 bg-[#C41E3A] rounded-xl flex items-center justify-center">
```
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/app/\(marketing\)/cases/\[id\]/client.tsx
git commit -m "style: unify color scheme to brand red in case detail page"
```
---
### Task 4.2: 更新 Solutions 页面配色
**Files:**
- Modify: `src/app/(marketing)/solutions/page.tsx`
**Step 1: 将紫色替换为品牌红**
Read: `src/app/(marketing)/solutions/page.tsx`
Replace all occurrences of:
- `#4F46E5``#C41E3A`
- `rgba(79,70,229,``rgba(196,30,58,`
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/app/\(marketing\)/solutions/page.tsx
git commit -m "style: unify color scheme to brand red in solutions page"
```
---
### Task 4.3: 更新 Products Section 配色
**Files:**
- Modify: `src/components/sections/products-section.tsx`
**Step 1: 将紫色替换为品牌红**
Read: `src/components/sections/products-section.tsx`
Replace all occurrences of:
- `#4F46E5``#C41E3A`
- `rgba(79,70,229,``rgba(196,30,58,`
**Step 2: 验证修改**
Run: `npm run lint`
Expected: 无 lint 错误
**Step 3: Commit**
```bash
git add src/components/sections/products-section.tsx
git commit -m "style: unify color scheme to brand red in products section"
```
---
## 阶段 5: 清理与验证
### Task 5.1: 删除旧的 Solutions 页面(可选)
**Files:**
- Delete: `src/app/(marketing)/solutions/` (如果与 services 重复)
**Step 1: 检查是否需要删除**
Run: `ls -la src/app/\(marketing\)/solutions/`
如果 solutions 页面与 services 功能重复,考虑重定向或删除。
**Step 2: 如果决定保留,更新导航**
确保导航指向正确的页面。
---
### Task 5.2: 运行完整测试
**Step 1: 运行 lint 检查**
Run: `npm run lint`
Expected: 无错误
**Step 2: 运行类型检查**
Run: `npm run typecheck || npx tsc --noEmit`
Expected: 无类型错误
**Step 3: 运行构建**
Run: `npm run build`
Expected: 构建成功
**Step 4: 本地测试**
Run: `npm run dev`
手动测试以下页面:
- `/` - 首页
- `/services` - 服务列表
- `/services/[id]` - 服务详情
- `/products` - 产品列表
- `/products/[id]` - 产品详情
- `/cases` - 案例列表
- `/cases/[id]` - 案例详情
- `/about` - 关于我们
- `/news` - 新闻列表
- `/contact` - 联系我们
---
### Task 5.3: 最终提交
**Step 1: 检查所有更改**
Run: `git status`
**Step 2: 提交所有更改**
```bash
git add -A
git commit -m "refactor: complete website optimization - unified navigation, colors, and structure"
```
---
## 实施完成检查清单
- [ ] 服务详情页创建完成
- [ ] 服务列表页创建完成
- [ ] 服务模态框已删除
- [ ] 首页 About Section 已精简
- [ ] 首页 Cases Section 已添加
- [ ] 导航链接已统一
- [ ] 产品列表页已创建
- [ ] Footer 链接已更新
- [ ] 配色方案已统一
- [ ] Lint 检查通过
- [ ] 类型检查通过
- [ ] 构建成功
- [ ] 手动测试通过