feat: 实现动态详情页面和性能优化

- 添加案例、新闻、产品详情页面的E2E测试
- 优化详情页面的客户端组件和页面逻辑
- 添加高性能Docker配置和Nginx配置
- 更新API服务和常量配置
- 添加性能优化文档和任务进度更新
- 修复ESLint错误和类型问题
This commit is contained in:
张翔
2026-03-26 12:53:58 +08:00
parent 498bb3a3c8
commit 14448af731
18 changed files with 2244 additions and 913 deletions
+41 -99
View File
@@ -5,24 +5,17 @@ import Link from 'next/link';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { BackButton } from '@/components/ui/back-button';
import { CheckCircle2, TrendingUp, Users, Target, Quote, Clock, MessageCircle, Award } from 'lucide-react';
import { CASES } from '@/lib/constants';
import type { StaticImageData } from 'next/image';
interface CaseResult {
label: string;
value: string;
}
import { Users, Target, Quote, Clock, MessageCircle, Award, TrendingUp } from 'lucide-react';
interface CaseItem {
id: string;
title: string;
client: string;
industry: string;
description: string;
results: readonly CaseResult[];
tags: readonly string[];
image?: string | StaticImageData;
excerpt: string;
content: string;
category: string;
slug: string;
publishedAt?: string;
createdAt: string;
}
interface CaseDetailClientProps {
@@ -50,20 +43,6 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
return () => observer.disconnect();
}, []);
const relatedCases = CASES.filter((c) => c.id !== caseItem.id).slice(0, 2);
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
'业务处理效率': TrendingUp,
'客户满意度': Users,
'运营成本': Target,
'生产效率': TrendingUp,
'设备利用率': Target,
'不良品率': CheckCircle2,
'数据整合效率': TrendingUp,
'决策响应时间': Target,
'营销转化率': Users,
};
return (
<main className="min-h-screen bg-white">
<div className="relative overflow-hidden bg-gradient-to-b from-[#FAFAFA] to-white">
@@ -71,13 +50,13 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
<BackButton />
<div className="max-w-4xl mt-8">
<Badge className="mb-4 bg-[#C41E3A]/10 text-[#C41E3A] hover:bg-[#C41E3A]/20">
{caseItem.industry}
{caseItem.category}
</Badge>
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-semibold text-[#1C1C1C] mb-2">
{caseItem.title}
</h1>
<p className="text-lg text-[#5C5C5C]">
{caseItem.client}
{caseItem.excerpt}
</p>
</div>
</div>
@@ -102,11 +81,11 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</h2>
</div>
<p className="text-[#5C5C5C] leading-relaxed text-lg">
{caseItem.description}
{caseItem.excerpt}
</p>
<div className="mt-6 p-4 bg-white rounded-lg border border-[#E5E5E5]">
<p className="text-sm text-[#737373] italic">
"在找到睿新致远之前,我们面临着巨大的挑战..."
&ldquo;...&rdquo;
</p>
</div>
</section>
@@ -120,20 +99,8 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</h2>
</div>
<div className="space-y-4">
{caseItem.tags.map((tag, index) => (
<div key={tag} className="flex items-start gap-3">
<div className="w-8 h-8 bg-[#C41E3A]/10 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
<span className="text-[#C41E3A] font-semibold">{index + 1}</span>
</div>
<div>
<h3 className="font-semibold text-[#1C1C1C] mb-1">{tag}</h3>
<p className="text-sm text-[#737373]">
{tag}
</p>
</div>
</div>
))}
<div className="prose prose-sm max-w-none">
<div dangerouslySetInnerHTML={{ __html: caseItem.content }} />
</div>
</section>
@@ -182,25 +149,31 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</h2>
</div>
<div className="grid sm:grid-cols-3 gap-4 mb-6">
{caseItem.results.map((result) => {
const Icon = iconMap[result.label] || TrendingUp;
return (
<div
key={result.label}
className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors"
>
<Icon className="w-8 h-8 text-[#C41E3A] mb-3" />
<div className="text-2xl font-semibold text-[#C41E3A] mb-1">
{result.value}
</div>
<div className="text-sm text-[#737373]">{result.label}</div>
</div>
);
})}
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors">
<TrendingUp className="w-8 h-8 text-[#C41E3A] mb-3" />
<div className="text-2xl font-semibold text-[#C41E3A] mb-1">
300%
</div>
<div className="text-sm text-[#737373]"></div>
</div>
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors">
<Users className="w-8 h-8 text-[#C41E3A] mb-3" />
<div className="text-2xl font-semibold text-[#C41E3A] mb-1">
95%
</div>
<div className="text-sm text-[#737373]"></div>
</div>
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors">
<Target className="w-8 h-8 text-[#C41E3A] mb-3" />
<div className="text-2xl font-semibold text-[#C41E3A] mb-1">
-40%
</div>
<div className="text-sm text-[#737373]"></div>
</div>
</div>
<div className="p-4 bg-white rounded-lg border border-[#E5E5E5]">
<p className="text-sm text-[#737373] italic">
"通过三年的合作,我们不仅实现了数字化转型,更重要的是建立了一个可持续发展的技术体系。"
&ldquo;&rdquo;
</p>
</div>
</section>
@@ -221,10 +194,10 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</p>
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-[#C41E3A] rounded-full flex items-center justify-center">
<span className="text-white font-semibold">{caseItem.client[0]}</span>
<span className="text-white font-semibold"></span>
</div>
<div>
<p className="font-semibold text-[#1C1C1C]">{caseItem.client}</p>
<p className="font-semibold text-[#1C1C1C]"></p>
<p className="text-sm text-[#737373]">CEO</p>
</div>
</div>
@@ -238,25 +211,19 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
<dl className="space-y-3">
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">{caseItem.client}</dd>
<dd className="text-[#1C1C1C] font-medium"></dd>
</div>
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">{caseItem.industry}</dd>
<dd className="text-[#1C1C1C] font-medium">{caseItem.category}</dd>
</div>
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">3</dd>
</div>
<div>
<dt className="text-sm text-[#737373]"></dt>
<dd className="flex flex-wrap gap-2 mt-1">
{caseItem.tags.map((tag) => (
<Badge key={tag} variant="secondary" className="text-xs">
{tag}
</Badge>
))}
</dd>
<dt className="text-sm text-[#737373]"></dt>
<dd className="text-[#1C1C1C] font-medium">{caseItem.publishedAt || caseItem.createdAt}</dd>
</div>
</dl>
</div>
@@ -277,31 +244,6 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
</div>
</div>
</div>
{relatedCases.length > 0 && (
<section className="mt-16 pt-16 border-t border-[#E5E5E5]">
<h2 className="text-2xl font-semibold text-[#171717] mb-8"></h2>
<div className="grid md:grid-cols-2 gap-6">
{relatedCases.map((relatedCase) => (
<Link
key={relatedCase.id}
href={`/cases/${relatedCase.id}`}
className="group p-6 bg-[#FAFAFA] rounded-lg border border-[#E5E5E5] hover:border-[#C41E3A] transition-colors"
>
<Badge variant="secondary" className="mb-2">
{relatedCase.industry}
</Badge>
<h3 className="text-lg font-semibold text-[#171717] group-hover:text-[#C41E3A] transition-colors">
{relatedCase.title}
</h3>
<p className="text-sm text-[#737373] mt-2 line-clamp-2">
{relatedCase.description}
</p>
</Link>
))}
</div>
</section>
)}
</div>
</main>
);