1631 lines
53 KiB
Markdown
1631 lines
53 KiB
Markdown
# 网站优化重构实施计划
|
||
|
||
> **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 检查通过
|
||
- [ ] 类型检查通过
|
||
- [ ] 构建成功
|
||
- [ ] 手动测试通过
|