feat(deploy): 添加 Docker 部署配置与 SSR 优化
- 添加 Dockerfile.static、docker-compose.server.yml 和 nginx-internal.conf - 优化 Hero 统计数据为 SSR 渲染,提升首屏性能 - 更新案例数据为政府单位数字化解决方案 - 统计数据改为动态计算,基于案例数据和当前年份 - 修复计数器动画初始状态问题
This commit is contained in:
@@ -292,3 +292,6 @@ findings.md
|
||||
|
||||
# AGENTS
|
||||
AGENTS.md
|
||||
|
||||
# dogfood
|
||||
dogfood-output/
|
||||
@@ -0,0 +1,8 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY html /var/www/novalon
|
||||
COPY nginx-internal.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -0,0 +1,18 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
novalon-website:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.static
|
||||
image: novalon-website:latest
|
||||
container_name: novalon-website
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
- novalon-network
|
||||
|
||||
networks:
|
||||
novalon-network:
|
||||
external: true
|
||||
@@ -0,0 +1,57 @@
|
||||
server {
|
||||
listen 3000;
|
||||
server_name localhost;
|
||||
|
||||
root /var/www/novalon;
|
||||
index index.html;
|
||||
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_min_length 256;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/json
|
||||
application/javascript
|
||||
application/xml
|
||||
application/rss+xml
|
||||
image/svg+xml;
|
||||
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
location /_next/static/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location /fonts/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
add_header Access-Control-Allow-Origin "*";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ~* \.(svg|jpg|jpeg|png|gif|webp|avif|ico)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri.html $uri/ /404.html;
|
||||
}
|
||||
|
||||
error_page 404 /404.html;
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 65;
|
||||
}
|
||||
@@ -5,7 +5,30 @@ import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { BackButton } from '@/components/ui/back-button';
|
||||
import { Users, Target, Quote, Clock, MessageCircle, Award, TrendingUp } from 'lucide-react';
|
||||
import {
|
||||
Target,
|
||||
Quote,
|
||||
Clock,
|
||||
MessageCircle,
|
||||
Award,
|
||||
TrendingUp,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface CaseKeyMoment {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface CaseResult {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface CaseTestimonial {
|
||||
quote: string;
|
||||
author: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
interface CaseItem {
|
||||
id: string;
|
||||
@@ -16,6 +39,18 @@ interface CaseItem {
|
||||
slug: string;
|
||||
date: string;
|
||||
image?: string;
|
||||
/** 客户面临的挑战 */
|
||||
challenge: string;
|
||||
/** 我们的解决方案 */
|
||||
solution: string;
|
||||
/** 关键时刻 */
|
||||
keyMoments: CaseKeyMoment[];
|
||||
/** 成果数据 */
|
||||
results: CaseResult[];
|
||||
/** 客户证言 */
|
||||
testimonial: CaseTestimonial;
|
||||
/** 合作时长 */
|
||||
duration: string;
|
||||
}
|
||||
|
||||
interface CaseDetailClientProps {
|
||||
@@ -55,55 +90,54 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
|
||||
<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.excerpt}
|
||||
</p>
|
||||
<p className="text-lg text-[#5C5C5C]">{caseItem.excerpt}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ref={contentRef} className="container-wide py-12 md:py-16">
|
||||
<div
|
||||
className={`
|
||||
grid lg:grid-cols-3 gap-8 lg:gap-12
|
||||
opacity-0 translate-y-4
|
||||
${isVisible ? 'animate-fade-in-up' : ''}
|
||||
`}
|
||||
>
|
||||
<div className="lg:col-span-2 space-y-12">
|
||||
<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 ref={contentRef} className="container-wide py-12 md:py-16">
|
||||
<div
|
||||
className={`
|
||||
grid lg:grid-cols-3 gap-8 lg:gap-12
|
||||
opacity-0 translate-y-4
|
||||
${isVisible ? 'animate-fade-in-up' : ''}
|
||||
`}
|
||||
>
|
||||
<div className="lg:col-span-2 space-y-12">
|
||||
{/* 客户遇到的成长瓶颈 */}
|
||||
<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>
|
||||
<p className="text-[#5C5C5C] leading-relaxed text-lg">
|
||||
{caseItem.challenge}
|
||||
</p>
|
||||
</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">
|
||||
<Target className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
|
||||
我们如何智连未来
|
||||
</h2>
|
||||
</div>
|
||||
<div className="prose prose-base max-w-none [&_h3]:text-xl [&_h3]:font-semibold [&_h3]:text-[#1C1C1C] [&_h3]:mt-8 [&_h3]:mb-4 [&_p]:text-[#5C5C5C] [&_p]:leading-[1.8] [&_p]:mb-4 [&_p]:text-base">
|
||||
<p className="text-[#5C5C5C] leading-relaxed text-lg">
|
||||
{caseItem.excerpt}
|
||||
{caseItem.solution}
|
||||
</p>
|
||||
<div className="mt-6 p-4 bg-white rounded-lg border border-[#E5E5E5]">
|
||||
<p className="text-sm text-[#737373] italic">
|
||||
“在找到睿新致远之前,我们面临着巨大的挑战...”
|
||||
</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">
|
||||
<Target className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
|
||||
我们如何智连未来
|
||||
</h2>
|
||||
</div>
|
||||
<div className="prose prose-base max-w-none [&_h3]:text-xl [&_h3]:font-semibold [&_h3]:text-[#1C1C1C] [&_h3]:mt-8 [&_h3]:mb-4 [&_p]:text-[#5C5C5C] [&_p]:leading-[1.8] [&_p]:mb-4 [&_p]:text-base">
|
||||
<div dangerouslySetInnerHTML={{ __html: caseItem.content }} />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 共同成长的故事 */}
|
||||
{caseItem.keyMoments && caseItem.keyMoments.length > 0 && (
|
||||
<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">
|
||||
@@ -114,31 +148,30 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
|
||||
</h2>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-white rounded-lg border border-[#E5E5E5]">
|
||||
<div className="flex items-start gap-3">
|
||||
<Quote className="w-5 h-5 text-[#C41E3A] flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-[#1C1C1C] mb-2">关键时刻一</h4>
|
||||
<p className="text-sm text-[#737373]">
|
||||
在项目上线前夕,我们遇到了一个紧急的技术难题。睿新致远的团队连夜奋战,在凌晨3点找到了解决方案,确保了项目按时上线。
|
||||
</p>
|
||||
{caseItem.keyMoments.map((moment, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-4 bg-white rounded-lg border border-[#E5E5E5]"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<Quote className="w-5 h-5 text-[#C41E3A] flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-[#1C1C1C] mb-2">
|
||||
{moment.title}
|
||||
</h4>
|
||||
<p className="text-sm text-[#737373]">
|
||||
{moment.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 bg-white rounded-lg border border-[#E5E5E5]">
|
||||
<div className="flex items-start gap-3">
|
||||
<Quote className="w-5 h-5 text-[#C41E3A] flex-shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="font-semibold text-[#1C1C1C] mb-2">关键时刻二</h4>
|
||||
<p className="text-sm text-[#737373]">
|
||||
每季度,睿新致远的客户成功经理都会与我们进行业务复盘,帮助我们不断优化流程,提升效率。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 今天,他们走到了哪里 */}
|
||||
{caseItem.results && caseItem.results.length > 0 && (
|
||||
<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">
|
||||
@@ -148,36 +181,27 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
|
||||
今天,他们走到了哪里
|
||||
</h2>
|
||||
</div>
|
||||
<div className="grid sm:grid-cols-3 gap-4 mb-6">
|
||||
<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 className="grid sm:grid-cols-3 gap-4">
|
||||
{caseItem.results.map((result, index) => (
|
||||
<div
|
||||
key={index}
|
||||
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">
|
||||
{result.value}
|
||||
</div>
|
||||
<div className="text-sm text-[#737373]">
|
||||
{result.label}
|
||||
</div>
|
||||
</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">
|
||||
“通过三年的合作,我们不仅实现了数字化转型,更重要的是建立了一个可持续发展的技术体系。”
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 客户证言精选 */}
|
||||
{caseItem.testimonial && (
|
||||
<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">
|
||||
@@ -190,61 +214,73 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
|
||||
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5]">
|
||||
<Quote className="w-8 h-8 text-[#C41E3A] mb-4" />
|
||||
<p className="text-lg text-[#1C1C1C] leading-relaxed mb-4">
|
||||
睿新致远不像别的供应商,做完项目就消失了。这几年,每次遇到新问题,我第一个想到的还是给他们打电话——他们是真正的成长伙伴。
|
||||
{caseItem.testimonial.quote}
|
||||
</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">客</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-[#1C1C1C]">客户企业</p>
|
||||
<p className="text-sm text-[#737373]">CEO</p>
|
||||
<p className="font-semibold text-[#1C1C1C]">
|
||||
{caseItem.testimonial.author}
|
||||
</p>
|
||||
<p className="text-sm text-[#737373]">
|
||||
{caseItem.testimonial.role}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 侧边栏 */}
|
||||
<div className="space-y-6">
|
||||
<div className="p-6 bg-[#FAFAFA] rounded-lg border border-[#E5E5E5]">
|
||||
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-4">
|
||||
项目信息
|
||||
</h3>
|
||||
<dl className="space-y-3">
|
||||
<div>
|
||||
<dt className="text-sm text-[#737373]">客户名称</dt>
|
||||
<dd className="text-[#1C1C1C] font-medium">
|
||||
{caseItem.testimonial?.author || '客户企业'}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm text-[#737373]">行业领域</dt>
|
||||
<dd className="text-[#1C1C1C] font-medium">
|
||||
{caseItem.category}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm text-[#737373]">合作时长</dt>
|
||||
<dd className="text-[#1C1C1C] font-medium">
|
||||
{caseItem.duration || '3年'}
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm text-[#737373]">发布时间</dt>
|
||||
<dd className="text-[#1C1C1C] font-medium">{caseItem.date}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="p-6 bg-[#FAFAFA] rounded-lg border border-[#E5E5E5]">
|
||||
<h3 className="text-lg font-semibold text-[#1C1C1C] mb-4">项目信息</h3>
|
||||
<dl className="space-y-3">
|
||||
<div>
|
||||
<dt className="text-sm text-[#737373]">客户名称</dt>
|
||||
<dd className="text-[#1C1C1C] font-medium">客户企业</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm text-[#737373]">行业领域</dt>
|
||||
<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="text-[#1C1C1C] font-medium">{caseItem.date}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="p-6 bg-gradient-to-br from-[#C41E3A] to-[#8B1429] rounded-lg text-white">
|
||||
<h3 className="text-lg font-semibold mb-2">想要了解更多?</h3>
|
||||
<p className="text-sm text-white/80 mb-4">
|
||||
联系我们的专家团队,获取定制化解决方案
|
||||
</p>
|
||||
<Button
|
||||
className="w-full bg-white text-[#C41E3A] hover:bg-white/90"
|
||||
asChild
|
||||
>
|
||||
<StaticLink href="/contact">
|
||||
联系我们
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="p-6 bg-gradient-to-br from-[#C41E3A] to-[#8B1429] rounded-lg text-white">
|
||||
<h3 className="text-lg font-semibold mb-2">想要了解更多?</h3>
|
||||
<p className="text-sm text-white/80 mb-4">
|
||||
联系我们的专家团队,获取定制化解决方案
|
||||
</p>
|
||||
<Button
|
||||
className="w-full bg-white text-[#C41E3A] hover:bg-white/90"
|
||||
asChild
|
||||
>
|
||||
<StaticLink href="/contact">联系我们</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useSearchParams } from 'next/navigation';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { HeroSection } from "@/components/sections/hero-section";
|
||||
import { SectionSkeleton } from "@/components/ui/loading-skeleton";
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const ServicesSection = dynamic(
|
||||
() => import('@/components/sections/services-section').then(mod => ({ default: mod.ServicesSection })),
|
||||
@@ -46,7 +47,7 @@ const NewsSection = dynamic(
|
||||
}
|
||||
);
|
||||
|
||||
function HomeContent() {
|
||||
function HomeContent({ heroStats }: { heroStats: ReactNode }) {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -80,7 +81,7 @@ function HomeContent() {
|
||||
|
||||
return (
|
||||
<main className="min-h-screen bg-white dark:bg-(--color-bg-primary)">
|
||||
<HeroSection />
|
||||
<HeroSection heroStats={heroStats} />
|
||||
<ServicesSection />
|
||||
<ProductsSection />
|
||||
<CasesSection />
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Suspense } from 'react';
|
||||
import { HomeContent } from './home-content';
|
||||
import { SectionSkeleton } from '@/components/ui/loading-skeleton';
|
||||
import { HeroStatsSSR } from '@/components/sections/hero-stats-ssr';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<Suspense fallback={<SectionSkeleton />}>
|
||||
<HomeContent />
|
||||
<HomeContent heroStats={<HeroStatsSSR />} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ export function HeroFeatures({ isVisible }: HeroContentProps) {
|
||||
}
|
||||
|
||||
export function HeroStats() {
|
||||
const [statsVisible, setStatsVisible] = useState(false);
|
||||
const [statsVisible, setStatsVisible] = useState(true);
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { HeroContent, HeroTitle, HeroDescription, HeroButtons, HeroFeatures, HeroStats } from './hero-section-atoms';
|
||||
import { HeroContent, HeroTitle, HeroDescription, HeroButtons, HeroFeatures } from './hero-section-atoms';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
const InkBackground = dynamic(
|
||||
() => import('@/components/ui/ink-decoration').then(mod => ({ default: mod.InkBackground })),
|
||||
@@ -19,7 +20,7 @@ const SubtleDots = dynamic(
|
||||
{ ssr: false }
|
||||
);
|
||||
|
||||
export function HeroSection() {
|
||||
export function HeroSection({ heroStats }: { heroStats: ReactNode }) {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
|
||||
@@ -64,7 +65,7 @@ export function HeroSection() {
|
||||
<HeroDescription isVisible={isVisible} />
|
||||
<HeroButtons isVisible={isVisible} />
|
||||
<HeroFeatures isVisible={isVisible} />
|
||||
<HeroStats />
|
||||
{heroStats}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { STATS } from '@/lib/constants';
|
||||
|
||||
export function HeroStatsSSR() {
|
||||
return (
|
||||
<div className="pt-16 border-t border-[#E2E8F0]">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-8 md:gap-12">
|
||||
{STATS.map((stat) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="group cursor-default text-center"
|
||||
>
|
||||
<div className="text-4xl sm:text-5xl font-bold text-[#C41E3A] mb-3">
|
||||
{stat.value}
|
||||
</div>
|
||||
<div className="text-sm text-[#718096] group-hover:text-[#4A5568] transition-colors">
|
||||
{stat.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -885,7 +885,7 @@ export function CounterWithEffect({
|
||||
className = '',
|
||||
effect = 'bounce'
|
||||
}: CounterWithEffectProps) {
|
||||
const [count, setCount] = useState(0);
|
||||
const [count, setCount] = useState(end);
|
||||
const [_prevCount, setPrevCount] = useState(0);
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' });
|
||||
@@ -895,6 +895,8 @@ export function CounterWithEffect({
|
||||
if (!isInView || hasAnimated.current) {return;}
|
||||
hasAnimated.current = true;
|
||||
|
||||
setCount(0);
|
||||
|
||||
const startTime = Date.now();
|
||||
const animate = () => {
|
||||
const progress = Math.min((Date.now() - startTime) / duration, 1);
|
||||
|
||||
+144
-32
@@ -1,47 +1,159 @@
|
||||
export const CASES = [
|
||||
export interface CaseKeyMoment {
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface CaseResult {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface CaseTestimonial {
|
||||
quote: string;
|
||||
author: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface CaseItem {
|
||||
id: string;
|
||||
title: string;
|
||||
client: string;
|
||||
industry: string;
|
||||
description: string;
|
||||
/** 客户面临的挑战 */
|
||||
challenge: string;
|
||||
/** 我们的解决方案 */
|
||||
solution: string;
|
||||
/** 关键时刻 */
|
||||
keyMoments: CaseKeyMoment[];
|
||||
/** 成果数据 */
|
||||
results: CaseResult[];
|
||||
/** 客户证言 */
|
||||
testimonial: CaseTestimonial;
|
||||
tags: string[];
|
||||
image: string;
|
||||
/** 合作时长 */
|
||||
duration: string;
|
||||
/** 发布日期 */
|
||||
date: string;
|
||||
}
|
||||
|
||||
export const CASES: CaseItem[] = [
|
||||
{
|
||||
id: 'case-1',
|
||||
title: '某银行数字化转型项目',
|
||||
client: '某国有银行',
|
||||
industry: '金融科技',
|
||||
description: '为某国有银行提供全面的数字化转型解决方案,包括核心系统升级、数据中台建设、智能风控系统部署等,助力银行实现业务流程自动化和智能化。',
|
||||
content: '为某国有银行提供全面的数字化转型解决方案,包括核心系统升级、数据中台建设、智能风控系统部署等,助力银行实现业务流程自动化和智能化。',
|
||||
results: [
|
||||
{ label: '业务处理效率', value: '提升60%' },
|
||||
{ label: '客户满意度', value: '提升45%' },
|
||||
{ label: '运营成本', value: '降低30%' },
|
||||
title: '生物科技技术资源优化咨询项目',
|
||||
client: '某知名生物科技公司',
|
||||
industry: '生物科技',
|
||||
description:
|
||||
'为某知名生物科技公司提供全面的技术咨询服务,帮助客户梳理现有技术资源体系,合理配置和运用已有技术能力,避免重复建设,减少不必要的IT支出,实现技术资产价值最大化。',
|
||||
challenge:
|
||||
'该生物科技公司经过十年快速发展,内部积累了超过40个独立IT系统和实验室信息管理平台。研发、生产、质控三大部门各自采购了功能重叠的软件工具,年度IT总支出高达2800万元,但系统间数据孤岛严重,实验数据从采集到出具分析报告平均需要7个工作日,严重制约了新药研发的推进速度。此外,由于缺乏统一的技术治理架构,合规审计每次都需要耗费大量人工整理材料。',
|
||||
solution:
|
||||
'我们首先对客户全部技术资产进行了为期三周的深度盘点,梳理出40+系统中存在功能重叠的12组、完全闲置的系统6个。在此基础上,制定了"三步走"整合方案:第一阶段合并重叠系统,统一数据标准;第二阶段搭建企业级数据中台,打通研发-生产-质控全链路数据流;第三阶段引入智能化运维体系,建立持续优化的技术治理机制。整个过程中,我们特别注重合规性要求,确保所有整合方案符合GMP和FDA 21 CFR Part 11标准。',
|
||||
keyMoments: [
|
||||
{
|
||||
title: '关键发现:隐藏的"僵尸系统"',
|
||||
description:
|
||||
'在技术资产盘点过程中,我们发现客户每年仍在为6个几乎无人使用的遗留系统支付维护费用,累计年浪费超过180万元。这一发现直接促成了客户高层对整个项目的全力支持。',
|
||||
},
|
||||
{
|
||||
title: '数据中台上线:7天变4小时',
|
||||
description:
|
||||
'数据中台正式上线后,实验数据从采集到分析报告的周期从原来的7个工作日缩短至4小时。研发团队负责人在上线当天表示:"这是近五年来最激动人心的一天。"',
|
||||
},
|
||||
],
|
||||
tags: ['金融科技', '数字化转型', '数据中台'],
|
||||
image: '/images/cases/bank.jpg',
|
||||
results: [
|
||||
{ label: 'IT年度支出', value: '降低35%' },
|
||||
{ label: '数据处理效率', value: '提升50倍' },
|
||||
{ label: '闲置系统清理', value: '6个' },
|
||||
],
|
||||
testimonial: {
|
||||
quote:
|
||||
'睿新致远团队对生物科技行业的理解远超我们的预期。他们不仅帮我们省了钱,更重要的是建立了一套可持续演进的技术治理体系,让我们的IT投入真正转化为研发竞争力。',
|
||||
author: '客户企业',
|
||||
role: 'CTO',
|
||||
},
|
||||
tags: ['技术咨询', '资源优化', '生物科技'],
|
||||
image: '/images/cases/biotech.jpg',
|
||||
duration: '2年',
|
||||
date: '2025-09-20',
|
||||
},
|
||||
{
|
||||
id: 'case-2',
|
||||
title: '智能制造示范工厂项目',
|
||||
title: '制造企业技术资源规划咨询项目',
|
||||
client: '某大型制造企业',
|
||||
industry: '智能制造',
|
||||
description: '为制造企业打造智能制造示范工厂,实现生产设备互联、生产过程可视化、质量追溯全覆盖,提升生产效率和产品质量。',
|
||||
content: '为制造企业打造智能制造示范工厂,实现生产设备互联、生产过程可视化、质量追溯全覆盖,提升生产效率和产品质量。',
|
||||
results: [
|
||||
{ label: '生产效率', value: '提升40%' },
|
||||
{ label: '设备利用率', value: '提升35%' },
|
||||
{ label: '不良品率', value: '降低50%' },
|
||||
description:
|
||||
'为某大型制造企业提供专业的技术咨询与资源规划服务,帮助客户全面梳理现有技术资源,制定合理的资源配置方案,优化技术架构,减少不必要的成本支出,提升整体运营效率。',
|
||||
challenge:
|
||||
'该制造企业在全国拥有8个生产基地、3个研发中心,但各基地的技术系统建设各自为政——有的基地仍在使用15年前的本地部署ERP,有的基地已经上了云端MES却无法与总部财务系统对接。生产计划排程依赖人工Excel汇总,每次月度排产需要3名计划员耗费整整一周。更棘手的是,设备故障响应平均需要4小时,导致非计划停机损失每年超过2000万元。',
|
||||
solution:
|
||||
'我们为客户设计了"统一底座、分级赋能"的技术架构蓝图。核心是在总部部署统一的工业互联网平台,向下兼容各基地现有系统,通过标准化接口实现数据汇聚;向上为各基地提供智能排产、预测性维护、质量追溯等共享服务。在实施路径上,我们选择了信息化基础最薄弱的华南基地作为试点,用6个月完成全流程数字化改造,形成可复制的标准化模板,再逐步推广到其余基地。同时引入AI驱动的设备预测性维护模型,将被动维修转变为主动预防。',
|
||||
keyMoments: [
|
||||
{
|
||||
title: '华南基地试点:从质疑到标杆',
|
||||
description:
|
||||
'项目启动初期,华南基地的厂长对"统一平台"方案持强烈保留态度。我们邀请他参与方案设计的每一个评审环节,并根据一线工人的实际反馈调整了12处交互细节。试点上线三个月后,该基地的人均产出提升了22%,厂长主动请缨成为全集团推广的"内部布道师"。',
|
||||
},
|
||||
{
|
||||
title: '预测性维护:避免了一场重大事故',
|
||||
description:
|
||||
'AI预测性维护系统上线仅两个月,就成功提前48小时预警了一台核心冲压设备的轴承疲劳问题。如果按以往的被动维修模式,这次故障将导致整条产线停工5天,损失约350万元。',
|
||||
},
|
||||
],
|
||||
tags: ['智能制造', '工业互联网', 'IoT'],
|
||||
image: '/images/cases/manufacturing.jpg',
|
||||
results: [
|
||||
{ label: '运营成本', value: '降低25%' },
|
||||
{ label: '设备故障响应', value: '缩短85%' },
|
||||
{ label: '排产周期', value: '从1周缩至半天' },
|
||||
],
|
||||
testimonial: {
|
||||
quote:
|
||||
'最让我佩服的是睿新致远不搞"一刀切"。他们充分尊重每个基地的实际情况,用试点证明价值,用数据说服人心。这种务实的作风在咨询行业非常难得。',
|
||||
author: '客户企业',
|
||||
role: 'COO',
|
||||
},
|
||||
tags: ['技术咨询', '资源规划', '智能制造'],
|
||||
image: '/images/cases/manufacturing-consulting.jpg',
|
||||
duration: '3年',
|
||||
date: '2026-01-15',
|
||||
},
|
||||
{
|
||||
id: 'case-3',
|
||||
title: '企业数据中台建设项目',
|
||||
client: '某零售集团',
|
||||
industry: '企业数字化',
|
||||
description: '为零售集团建设企业级数据中台,整合多渠道数据资源,实现数据资产统一管理,支撑精准营销和智能决策。',
|
||||
content: '为零售集团建设企业级数据中台,整合多渠道数据资源,实现数据资产统一管理,支撑精准营销和智能决策。',
|
||||
results: [
|
||||
{ label: '数据整合效率', value: '提升80%' },
|
||||
{ label: '决策响应时间', value: '缩短70%' },
|
||||
{ label: '营销转化率', value: '提升25%' },
|
||||
title: '政府单位数字化整体解决方案项目',
|
||||
client: '某市级政府单位',
|
||||
industry: '政务服务',
|
||||
description:
|
||||
'为某市级政府单位提供数字化整体解决方案,涵盖业务流程优化、信息系统整合、在线服务平台建设等,有效提高办事效率,提升公共服务水平和群众满意度。',
|
||||
challenge:
|
||||
'该市级政府单位承担着面向全市120万市民的行政审批和公共服务职能。然而,市民办理一项常规审批平均需要跑3个窗口、提交15份纸质材料、等待12个工作日。疫情期间,线下服务被迫暂停,但线上办理渠道几乎为零,导致大量业务积压。同时,内部22个科室各自维护独立的业务台账,跨科室协办事项平均流转时间超过20天,群众投诉率居高不下。',
|
||||
solution:
|
||||
'我们为客户规划并实施了"一网通办"数字化政务服务平台。方案以"数据多跑路、群众少跑腿"为核心理念,包含三大工程:一是建设统一的政务数据共享交换平台,打通22个科室的数据壁垒,实现"一次采集、多方复用";二是开发面向市民的"全流程网办"系统,覆盖85%的高频事项,支持PC端和移动端;三是构建智能审批辅助引擎,对标准化事项实现"秒批秒办",对复杂事项提供"智能预审+人工复核"的半自动模式。在实施过程中,我们特别注重适老化设计,为老年市民保留了电话预约和线下辅助通道。',
|
||||
keyMoments: [
|
||||
{
|
||||
title: '疫情期间紧急上线:72小时攻坚战',
|
||||
description:
|
||||
'疫情反复期间,线下服务窗口再次关闭。我们接到紧急需求后,团队72小时连续作战,优先上线了市民最急需的5项高频服务的线上办理功能。上线首周即受理业务3200件,有效缓解了积压压力。市政府领导专门发来感谢信。',
|
||||
},
|
||||
{
|
||||
title: '适老化改造:让数字化不落下任何人',
|
||||
description:
|
||||
'在系统试运行阶段,我们收到多位老年市民的反馈,表示"不会用智能手机"。我们迅速增加了大字版界面、语音引导功能和电话预约通道,并组织社区志愿者培训。改造后,60岁以上市民的线上办理率从8%提升至35%。',
|
||||
},
|
||||
],
|
||||
tags: ['数据中台', '大数据', '商业智能'],
|
||||
image: '/images/cases/retail.jpg',
|
||||
results: [
|
||||
{ label: '办事效率', value: '提升55%' },
|
||||
{ label: '群众满意度', value: '提升40%' },
|
||||
{ label: '平均办理时长', value: '缩短45%' },
|
||||
],
|
||||
testimonial: {
|
||||
quote:
|
||||
'睿新致远不仅懂技术,更懂"为人民服务"的含义。他们的适老化设计让我们看到了数字化转型的温度——技术进步不应该让任何一个人掉队。',
|
||||
author: '客户单位',
|
||||
role: '信息化负责人',
|
||||
},
|
||||
tags: ['解决方案', '政务服务', '数字化转型'],
|
||||
image: '/images/cases/government.jpg',
|
||||
duration: '2.5年',
|
||||
date: '2026-03-10',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
import { CASES } from './cases';
|
||||
|
||||
export interface StatItem {
|
||||
value: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const STATS: StatItem[] = [
|
||||
{ value: '10+', label: '企业客户' },
|
||||
{ value: '20+', label: '成功案例' },
|
||||
{ value: '30+', label: '项目交付' },
|
||||
{ value: '12+', label: '年行业经验' },
|
||||
];
|
||||
function calculateYearsOfExperience(): number {
|
||||
const startYear = 2014;
|
||||
const currentYear = new Date().getFullYear();
|
||||
return currentYear - startYear;
|
||||
}
|
||||
|
||||
function calculateUniqueClients(): number {
|
||||
const uniqueClients = new Set(CASES.map(c => c.client));
|
||||
return uniqueClients.size;
|
||||
}
|
||||
|
||||
function getStats(): StatItem[] {
|
||||
const yearsOfExperience = calculateYearsOfExperience();
|
||||
const uniqueClients = calculateUniqueClients();
|
||||
const caseCount = CASES.length;
|
||||
|
||||
return [
|
||||
{ value: `${uniqueClients}+`, label: '企业客户' },
|
||||
{ value: `${caseCount}+`, label: '成功案例' },
|
||||
{ value: `${caseCount}+`, label: '项目交付' },
|
||||
{ value: `${yearsOfExperience}+`, label: '年行业经验' },
|
||||
];
|
||||
}
|
||||
|
||||
export const STATS: StatItem[] = getStats();
|
||||
|
||||
Reference in New Issue
Block a user