refactor: 页面组件优化与墨韵分割线组件化
- 提取 AnimatedInkDivider 组件替代硬编码 ink-divider div - 重构各营销页面组件代码结构优化 - 修正统计数据:自研产品 4 -> 6 - 更新 about 页面测试用例
This commit is contained in:
@@ -1,253 +1,147 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useRef, useMemo } from 'react';
|
||||
import { COMPANY_INFO, STATS } from '@/lib/constants';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Lightbulb, Users, Target, MapPin, Mail, ArrowRight } from 'lucide-react';
|
||||
import { RippleButton } from '@/components/ui/ripple-button';
|
||||
import { COMPANY_INFO } from '@/lib/constants';
|
||||
import { ArrowRight, Target, HeartHandshake, Award, Shield, Building2, Users, Code, TrendingUp } from 'lucide-react';
|
||||
import { useReducedMotion } from '@/hooks/use-reduced-motion';
|
||||
import { InkReveal, BlurReveal, StaggerContainer, StaggerItem, CountUp } from '@/lib/animations';
|
||||
import { TextReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const VALUES = [
|
||||
{ title: '务实', description: '不追逐风口,只做真正为客户创造价值的事。', icon: Target },
|
||||
{ title: '陪伴', description: '交付只是开始,长期陪跑才是我们的承诺。', icon: HeartHandshake },
|
||||
{ title: '专业', description: '用扎实的工程能力和行业经验赢得信任。', icon: Award },
|
||||
];
|
||||
|
||||
const TEAM_PILLARS = [
|
||||
{ icon: Shield, title: '12+ 年行业深耕', description: '核心团队长期从事技术咨询、企业数字化等领域,服务覆盖金融、制造、零售、政务、农业等多个行业。' },
|
||||
{ icon: Building2, title: '大型 IT 企业背景', description: '开发团队成员来自多个大型传统 IT 企业,具备扎实的工程能力和规范化的交付经验。' },
|
||||
{ icon: Users, title: '复合型技术团队', description: '团队成员既懂技术又懂业务,能够深入理解客户的真实场景和痛点。' },
|
||||
{ icon: Code, title: '全栈技术能力', description: '掌握从前端到后端、从云原生到数据智能的全栈技术能力。' },
|
||||
{ icon: TrendingUp, title: '结果导向交付', description: '以"客户业务是否真正改善"为衡量标准,追求可量化的业务价值。' },
|
||||
];
|
||||
|
||||
const COMPANY_STATS = [
|
||||
{ value: 12, label: '年+核心成员行业经验', suffix: '' },
|
||||
{ value: 5, label: '覆盖行业', suffix: '+' },
|
||||
{ value: 6, label: '自研产品', suffix: '款' },
|
||||
{ value: 98, label: '客户满意度', suffix: '%' },
|
||||
];
|
||||
|
||||
export function AboutClient() {
|
||||
const contentRef = useRef(null);
|
||||
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
|
||||
|
||||
const values = useMemo(() => [
|
||||
{
|
||||
icon: Lightbulb,
|
||||
title: '务实',
|
||||
description: '不追逐风口,只做真正为客户创造价值的事。每一个方案都源于对业务场景的深入洞察。',
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: '陪伴',
|
||||
description: '交付只是开始,长期陪跑才是我们的承诺。我们关注的不只是项目是否上线,更是您的业务是否真正改善。',
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: '专业',
|
||||
description: '用扎实的工程能力和行业经验赢得信任。既懂技术又懂业务,提供真正可落地的解决方案。',
|
||||
},
|
||||
], []);
|
||||
|
||||
const milestones = useMemo(() => [
|
||||
{
|
||||
date: '2026年1月',
|
||||
title: '公司成立',
|
||||
description: '四川睿新致远科技有限公司在成都龙泉驿区正式成立,专注于企业数字化转型解决方案',
|
||||
},
|
||||
{
|
||||
date: '2026年1月',
|
||||
title: '团队组建',
|
||||
description: '核心团队到位,技术团队拥有丰富的行业经验和专业技能',
|
||||
},
|
||||
{
|
||||
date: '2026年2月',
|
||||
title: '业务启动',
|
||||
description: '推出企业数字化转型解决方案,开始服务首批客户',
|
||||
},
|
||||
{
|
||||
date: '2026年2月',
|
||||
title: '产品研发',
|
||||
description: '启动ERP、CRM等自研产品的研发工作,致力于为企业提供一站式数字化服务',
|
||||
},
|
||||
], []);
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' });
|
||||
const shouldReduceMotion = useReducedMotion();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
<PageHeader
|
||||
title="关于我们"
|
||||
description="了解睿新致远的品牌故事。我们不只是技术供应商,更是您数字化转型的成长伙伴。以智慧连接数字趋势,以伙伴身份陪您成长。"
|
||||
/>
|
||||
{/* Hero - InkReveal */}
|
||||
<div className="relative overflow-hidden bg-gradient-to-b from-[#FAFAFA] to-white">
|
||||
<div className="container-wide relative z-10 pt-32 pb-20">
|
||||
<InkReveal className="max-w-4xl">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[var(--color-brand-primary)]" />
|
||||
<span className="text-sm text-[#5C5C5C] tracking-wide">关于我们</span>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
|
||||
关于 <span className="tracking-tight text-[var(--color-brand-primary)]" style={{ fontFamily: "var(--font-aoyagi-reisho), 'Ma Shan Zheng', 'ZCOOL XiaoWei', 'STKaiti', 'KaiTi', serif", fontWeight: 'normal', WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale', textRendering: 'optimizeLegibility' }}>{COMPANY_INFO.shortName}</span>
|
||||
</h1>
|
||||
<p className="text-lg text-[#5C5C5C] max-w-2xl">{COMPANY_INFO.slogan}</p>
|
||||
</InkReveal>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ref={contentRef} className="container-wide py-12 md:py-16">
|
||||
<div ref={ref} className="container-wide py-12 md:py-16">
|
||||
{/* 品牌理念 - TextReveal 逐词揭示 */}
|
||||
<TextReveal
|
||||
text="企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。"
|
||||
className="text-center text-lg text-[#5C5C5C] leading-relaxed mb-8 max-w-3xl mx-auto"
|
||||
delay={0.1}
|
||||
/>
|
||||
|
||||
{/* 核心理念 - BlurReveal */}
|
||||
<BlurReveal delay={0.2} className="bg-[#FFFBF5] rounded-2xl p-8 mb-12 border border-[var(--color-brand-primary)]/20">
|
||||
<p className="text-[#1C1C1C] font-medium text-center text-lg">
|
||||
我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。
|
||||
</p>
|
||||
</BlurReveal>
|
||||
|
||||
{/* 数据指标 - CountUp */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="max-w-4xl mx-auto space-y-8"
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.1 }}
|
||||
className="grid grid-cols-2 md:grid-cols-4 gap-4 max-w-4xl mx-auto mb-16"
|
||||
>
|
||||
<div className="bg-[#FFFBF5] rounded-2xl p-8 border border-[#E5E5E5]">
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6">睿新的选择</h2>
|
||||
<p className="text-[#5C5C5C] mb-6 leading-relaxed">
|
||||
所以我们选择了一条不同的路。
|
||||
</p>
|
||||
|
||||
<div className="mb-6">
|
||||
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3">智连未来</h3>
|
||||
<p className="text-[#5C5C5C] mb-3 leading-relaxed">
|
||||
我们坚持对行业趋势的深度研究,不追逐昙花一现的概念。
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] mb-3 leading-relaxed">
|
||||
每一次方案,都源于对您业务场景的洞察;
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] leading-relaxed">
|
||||
每一次连接,都为了让技术真正服务于您的未来。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-[#1C1C1C] mb-3">成长伙伴</h3>
|
||||
<p className="text-[#5C5C5C] mb-3 leading-relaxed">
|
||||
我们不把“项目交付”当作终点。
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] mb-3 leading-relaxed">
|
||||
您的业务增长了吗?您的团队能力提升了吗?
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] leading-relaxed">
|
||||
您下一次遇到难题时,还会第一个想到我们吗?
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] mt-3 leading-relaxed">
|
||||
这些问题,比“项目是否按时交付”更让我们在意。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-[#FFFBF5] rounded-2xl p-8 border border-[#E5E5E5]">
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6">品牌承诺</h2>
|
||||
<p className="text-[#5C5C5C] mb-6 leading-relaxed">
|
||||
我们承诺:
|
||||
</p>
|
||||
<ul className="space-y-3 mb-6">
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="text-green-600 font-bold">✅</span>
|
||||
<span className="text-[#5C5C5C]">不卖您用不上的技术</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="text-green-600 font-bold">✅</span>
|
||||
<span className="text-[#5C5C5C]">不说不懂业务的术语</span>
|
||||
</li>
|
||||
<li className="flex items-start gap-3">
|
||||
<span className="text-green-600 font-bold">✅</span>
|
||||
<span className="text-[#5C5C5C]">不做路过就忘的“一锤子买卖”</span>
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-[#5C5C5C] leading-relaxed font-medium">
|
||||
我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.2 }}
|
||||
className="grid grid-cols-2 md:grid-cols-4 gap-6"
|
||||
>
|
||||
{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-[var(--color-brand-primary)] 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={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.3 }}
|
||||
className="mb-16"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">核心价值观</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{values.map((value, idx) => (
|
||||
<motion.div
|
||||
key={value.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: 0.4 + idx * 0.1 }}
|
||||
className="flex items-start gap-4 p-6 bg-[#FFFBF5] rounded-xl border border-[#E5E5E5] hover:border-[#1C1C1C] transition-all duration-300"
|
||||
>
|
||||
<div className="w-12 h-12 rounded-lg bg-[var(--color-brand-primary)] flex items-center justify-center shrink-0">
|
||||
<value.icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-lg mb-2 text-[#1C1C1C]">{value.title}</h3>
|
||||
<p className="text-[#5C5C5C] text-sm">{value.description}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
className="mb-16"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">发展历程</h2>
|
||||
<div className="space-y-6">
|
||||
{milestones.map((milestone, idx) => (
|
||||
<motion.div
|
||||
key={milestone.title}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={isContentInView ? { opacity: 1, x: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: 0.6 + idx * 0.1 }}
|
||||
className="flex flex-col md:flex-row md:items-start gap-4 p-6 bg-[#FFFBF5] rounded-xl border border-[#E5E5E5]"
|
||||
>
|
||||
<div className="md:w-32 shrink-0">
|
||||
<span className="text-sm font-medium text-[var(--color-brand-primary)]">{milestone.date}</span>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-semibold text-[#1A1A2E] mb-1">{milestone.title}</h3>
|
||||
<p className="text-[#718096] text-sm">{milestone.description}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.7 }}
|
||||
className="bg-[#FFFBF5] rounded-2xl p-8 border border-[#E5E5E5]"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">联系我们</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-white rounded-lg">
|
||||
<MapPin className="w-5 h-5 text-[var(--color-brand-primary)]" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C]">公司地址</p>
|
||||
<p className="text-sm font-medium text-[#1C1C1C]">{COMPANY_INFO.address}</p>
|
||||
</div>
|
||||
{COMPANY_STATS.map((stat) => (
|
||||
<div key={stat.label} className="text-center py-6 px-4 bg-[#F5F5F5] rounded-2xl border border-[#E5E5E5]">
|
||||
<div className="text-3xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent mb-1">
|
||||
<CountUp end={stat.value} duration={2000} />
|
||||
{stat.suffix}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-white rounded-lg">
|
||||
<Mail className="w-5 h-5 text-[var(--color-brand-primary)]" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C]">电子邮箱</p>
|
||||
<a href={`mailto:${COMPANY_INFO.email}`} className="text-sm font-medium text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors">
|
||||
{COMPANY_INFO.email}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-[#5C5C5C]">{stat.label}</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* Bottom CTA */}
|
||||
<div className="mt-16 text-center py-16 bg-[#F5F5F5] rounded-2xl">
|
||||
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
|
||||
想了解更多?
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
|
||||
我们期待与您交流,共同探讨数字化转型之路
|
||||
</p>
|
||||
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
|
||||
<StaticLink href="/contact">
|
||||
立即咨询
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
{/* 核心价值观 - StaggerContainer */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16" staggerDelay={0.12}>
|
||||
{VALUES.map((value) => {
|
||||
const Icon = value.icon;
|
||||
return (
|
||||
<StaggerItem key={value.title}>
|
||||
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] text-center hover:border-[var(--color-brand-primary)]/20 hover:shadow-md transition-all duration-300">
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<Icon className="w-5 h-5 text-[var(--color-brand-primary)]" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{value.title}</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed">{value.description}</p>
|
||||
</div>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</StaggerContainer>
|
||||
|
||||
{/* 团队优势 - StaggerContainer */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16" staggerDelay={0.1}>
|
||||
{TEAM_PILLARS.map((item, idx) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<StaggerItem key={item.title}>
|
||||
<div className={`bg-white rounded-xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-md transition-all duration-300 h-full ${idx >= 3 ? 'md:col-span-1 lg:col-start-1' : ''}`}>
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{item.title}</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed">{item.description}</p>
|
||||
</div>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</StaggerContainer>
|
||||
|
||||
{/* CTA */}
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.3 }}
|
||||
className="text-center py-16 bg-[#F5F5F5] rounded-2xl"
|
||||
>
|
||||
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
|
||||
想了解更多?
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
|
||||
我们期待与您交流,共同探讨数字化转型之路
|
||||
</p>
|
||||
<StaticLink href="/contact">
|
||||
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white rounded-lg text-sm font-medium transition-colors">
|
||||
立即咨询
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</RippleButton>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +97,7 @@ jest.mock('@/lib/constants', () => ({
|
||||
{ value: '10+', label: '企业客户' },
|
||||
{ value: '20+', label: '成功案例' },
|
||||
{ value: '30+', label: '项目交付' },
|
||||
{ value: '12+', label: '年行业经验' },
|
||||
{ value: '12+', label: '年核心成员行业经验' },
|
||||
],
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -13,6 +12,8 @@ import {
|
||||
Award,
|
||||
TrendingUp,
|
||||
} from 'lucide-react';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { ScrollReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
interface CaseKeyMoment {
|
||||
title: string;
|
||||
@@ -39,17 +40,11 @@ interface CaseItem {
|
||||
slug: string;
|
||||
date: string;
|
||||
image?: string;
|
||||
/** 客户面临的挑战 */
|
||||
challenge: string;
|
||||
/** 我们的解决方案 */
|
||||
solution: string;
|
||||
/** 关键时刻 */
|
||||
keyMoments: CaseKeyMoment[];
|
||||
/** 成果数据 */
|
||||
results: CaseResult[];
|
||||
/** 客户证言 */
|
||||
testimonial?: CaseTestimonial;
|
||||
/** 合作时长 */
|
||||
duration: string;
|
||||
}
|
||||
|
||||
@@ -58,32 +53,13 @@ interface CaseDetailClientProps {
|
||||
}
|
||||
|
||||
export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
|
||||
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();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-white">
|
||||
{/* Hero - InkReveal */}
|
||||
<div className="relative overflow-hidden bg-gradient-to-b from-[#FAFAFA] to-white">
|
||||
<div className="container-wide relative z-10 pt-32 pb-20">
|
||||
<BackButton />
|
||||
<div className="max-w-4xl mt-8">
|
||||
<InkReveal className="max-w-4xl mt-8">
|
||||
<Badge className="mb-4 bg-[var(--color-brand-primary)]/10 text-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary)]/20">
|
||||
{caseItem.category}
|
||||
</Badge>
|
||||
@@ -91,193 +67,199 @@ export function CaseDetailClient({ caseItem }: CaseDetailClientProps) {
|
||||
{caseItem.title}
|
||||
</h1>
|
||||
<p className="text-lg text-[#5C5C5C]">{caseItem.excerpt}</p>
|
||||
</div>
|
||||
</InkReveal>
|
||||
</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="container-wide py-12 md:py-16">
|
||||
<div className="grid lg:grid-cols-3 gap-8 lg:gap-12">
|
||||
<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-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
|
||||
<MessageCircle className="w-6 h-6 text-white" />
|
||||
{/* 客户遇到的成长瓶颈 - ScrollReveal */}
|
||||
<ScrollReveal>
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] 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>
|
||||
<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-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] 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.solution}
|
||||
{caseItem.challenge}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 共同成长的故事 */}
|
||||
{caseItem.keyMoments && caseItem.keyMoments.length > 0 && (
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] 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">
|
||||
{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-[var(--color-brand-primary)] 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>
|
||||
</section>
|
||||
)}
|
||||
</ScrollReveal>
|
||||
|
||||
{/* 今天,他们走到了哪里 */}
|
||||
{caseItem.results && caseItem.results.length > 0 && (
|
||||
{/* 我们如何智连未来 - ScrollReveal */}
|
||||
<ScrollReveal delay={0.1}>
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
|
||||
<Award className="w-6 h-6 text-white" />
|
||||
<Target 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">
|
||||
{caseItem.results.map((result, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[var(--color-brand-primary)] transition-colors"
|
||||
>
|
||||
<TrendingUp className="w-8 h-8 text-[var(--color-brand-primary)] mb-3" />
|
||||
<div className="text-2xl font-semibold text-[var(--color-brand-primary)] mb-1">
|
||||
{result.value}
|
||||
</div>
|
||||
<div className="text-sm text-[#737373]">
|
||||
{result.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{/* 客户证言精选 */}
|
||||
{caseItem.testimonial && (
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
|
||||
<Quote className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
|
||||
客户证言精选
|
||||
</h2>
|
||||
</div>
|
||||
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5]">
|
||||
<Quote className="w-8 h-8 text-[var(--color-brand-primary)] mb-4" />
|
||||
<p className="text-lg text-[#1C1C1C] leading-relaxed mb-4">
|
||||
{caseItem.testimonial.quote}
|
||||
<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.solution}
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center">
|
||||
<span className="text-white font-semibold">客</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-[#1C1C1C]">
|
||||
{caseItem.testimonial.author}
|
||||
</p>
|
||||
<p className="text-sm text-[#737373]">
|
||||
{caseItem.testimonial.role}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ScrollReveal>
|
||||
|
||||
{/* 共同成长的故事 - StaggerContainer */}
|
||||
{caseItem.keyMoments && caseItem.keyMoments.length > 0 && (
|
||||
<ScrollReveal delay={0.15}>
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] 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>
|
||||
<StaggerContainer className="space-y-4" staggerDelay={0.1}>
|
||||
{caseItem.keyMoments.map((moment, index) => (
|
||||
<StaggerItem key={index}>
|
||||
<div className="p-4 bg-white rounded-lg border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-sm transition-all duration-300">
|
||||
<div className="flex items-start gap-3">
|
||||
<Quote className="w-5 h-5 text-[var(--color-brand-primary)] 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>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</StaggerContainer>
|
||||
</section>
|
||||
</ScrollReveal>
|
||||
)}
|
||||
|
||||
{/* 今天,他们走到了哪里 - StaggerContainer 数据指标 */}
|
||||
{caseItem.results && caseItem.results.length > 0 && (
|
||||
<ScrollReveal delay={0.2}>
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
|
||||
<Award className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
|
||||
今天,他们走到了哪里
|
||||
</h2>
|
||||
</div>
|
||||
<StaggerContainer className="grid sm:grid-cols-3 gap-4" staggerDelay={0.12}>
|
||||
{caseItem.results.map((result, index) => (
|
||||
<StaggerItem key={index}>
|
||||
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5] hover:border-[var(--color-brand-primary)] hover:shadow-md transition-all duration-300 text-center">
|
||||
<TrendingUp className="w-8 h-8 text-[var(--color-brand-primary)] mb-3 mx-auto" />
|
||||
<div className="text-2xl font-semibold text-[var(--color-brand-primary)] mb-1">
|
||||
{result.value}
|
||||
</div>
|
||||
<div className="text-sm text-[#737373]">
|
||||
{result.label}
|
||||
</div>
|
||||
</div>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</StaggerContainer>
|
||||
</section>
|
||||
</ScrollReveal>
|
||||
)}
|
||||
|
||||
{/* 客户证言精选 - ScrollReveal */}
|
||||
{caseItem.testimonial && (
|
||||
<ScrollReveal delay={0.25}>
|
||||
<section className="bg-gradient-to-br from-[#FFFBF5] to-white rounded-2xl p-8 border border-[var(--color-brand-primary)]/20">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center">
|
||||
<Quote className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-semibold text-[#1C1C1C]">
|
||||
客户证言精选
|
||||
</h2>
|
||||
</div>
|
||||
<div className="p-6 bg-white rounded-lg border border-[#E5E5E5]">
|
||||
<Quote className="w-8 h-8 text-[var(--color-brand-primary)] mb-4" />
|
||||
<p className="text-lg text-[#5C5C5C] leading-relaxed mb-4">
|
||||
{caseItem.testimonial.quote}
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center">
|
||||
<span className="text-white font-semibold">客</span>
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-semibold text-[#1C1C1C]">
|
||||
{caseItem.testimonial.author}
|
||||
</p>
|
||||
<p className="text-sm text-[#737373]">
|
||||
{caseItem.testimonial.role}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</ScrollReveal>
|
||||
)}
|
||||
</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>
|
||||
<ScrollReveal delay={0.1}>
|
||||
<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>
|
||||
</ScrollReveal>
|
||||
|
||||
<div className="p-6 bg-gradient-to-br from-[var(--color-brand-primary)] 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-[var(--color-brand-primary)] hover:bg-white/90"
|
||||
asChild
|
||||
>
|
||||
<StaticLink href="/contact">联系我们</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
<ScrollReveal delay={0.2}>
|
||||
<div className="p-6 bg-gradient-to-br from-[var(--color-brand-primary)] 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-[var(--color-brand-primary)] hover:bg-white/90"
|
||||
asChild
|
||||
>
|
||||
<StaticLink href="/contact">联系我们</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo, useRef, ChangeEvent } from 'react';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useState, useMemo, ChangeEvent } from 'react';
|
||||
import { CASES } from '@/lib/constants';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -9,8 +8,9 @@ import { Button } from '@/components/ui/button';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { Search, ArrowLeft, Building2, Calendar, TrendingUp, Filter, SearchX } from 'lucide-react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Pagination } from '@/components/ui/pagination';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { ScrollReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const industries = ['全部', ...Array.from(new Set(CASES.map((c) => c.industry)))];
|
||||
const ITEMS_PER_PAGE = 6;
|
||||
@@ -19,8 +19,6 @@ export default function CasesPage() {
|
||||
const [selectedIndustry, setSelectedIndustry] = useState('全部');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const contentRef = useRef(null);
|
||||
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
|
||||
|
||||
const filteredCases = useMemo(() => {
|
||||
return CASES.filter((caseItem) => {
|
||||
@@ -60,14 +58,10 @@ export default function CasesPage() {
|
||||
description="我们与优秀的企业同行,共同成长,共创未来"
|
||||
/>
|
||||
|
||||
<div className="container-wide relative z-10 py-16" ref={contentRef} id="page-content">
|
||||
<div className="container-wide relative z-10 py-16" ref={undefined} id="page-content">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-8 space-y-4"
|
||||
>
|
||||
{/* 筛选区 - InkReveal */}
|
||||
<InkReveal className="mb-8 space-y-4">
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start md:items-center">
|
||||
<div className="flex items-center gap-2 text-[#1C1C1C]">
|
||||
<Filter className="w-5 h-5" />
|
||||
@@ -102,7 +96,7 @@ export default function CasesPage() {
|
||||
aria-label="搜索案例"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{paginatedCases.length === 0 ? (
|
||||
<div className="text-center py-20">
|
||||
@@ -119,14 +113,10 @@ export default function CasesPage() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
{paginatedCases.map((caseItem, index) => (
|
||||
<motion.div
|
||||
key={caseItem.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
>
|
||||
{/* 卡片网格 - StaggerContainer */}
|
||||
<StaggerContainer className="grid md:grid-cols-2 gap-8" staggerDelay={0.1}>
|
||||
{paginatedCases.map((caseItem) => (
|
||||
<StaggerItem key={caseItem.id}>
|
||||
<StaticLink
|
||||
href={`/cases/${caseItem.id}`}
|
||||
className="group bg-white rounded-2xl border border-[#E5E5E5] overflow-hidden hover:shadow-xl transition-all duration-300 block"
|
||||
@@ -175,9 +165,9 @@ export default function CasesPage() {
|
||||
</div>
|
||||
</div>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
|
||||
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
|
||||
|
||||
@@ -189,20 +179,17 @@ export default function CasesPage() {
|
||||
</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
|
||||
{/* CTA - ScrollReveal */}
|
||||
<ScrollReveal>
|
||||
<div 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-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
|
||||
asChild
|
||||
@@ -212,8 +199,9 @@ export default function CasesPage() {
|
||||
<ArrowLeft className="ml-2 w-4 h-4 rotate-180" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useRef, Suspense } from 'react';
|
||||
import { useState, Suspense } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { z } from 'zod';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -10,6 +10,8 @@ import { Toast } from '@/components/ui/toast';
|
||||
import { Mail, MapPin, Send, Loader2, Clock, HeadphonesIcon, CheckCircle2 } from 'lucide-react';
|
||||
import { COMPANY_INFO } from '@/lib/constants';
|
||||
import { trackContactForm, trackConversion } from '@/lib/analytics';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { ScrollReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const contactFormSchema = z.object({
|
||||
name: z.string().min(2, '姓名至少需要2个字符'),
|
||||
@@ -32,7 +34,6 @@ interface FormErrors {
|
||||
function ContactFormContent() {
|
||||
const searchParams = useSearchParams();
|
||||
const isSuccessFromRedirect = searchParams.get('success') === 'true';
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const [showToast, setShowToast] = useState(isSuccessFromRedirect);
|
||||
const [toastMessage, setToastMessage] = useState(
|
||||
isSuccessFromRedirect ? '表单提交成功!我们会尽快与您联系。' : ''
|
||||
@@ -50,13 +51,6 @@ function ContactFormContent() {
|
||||
message: '',
|
||||
});
|
||||
const [errors, setErrors] = useState<FormErrors>({});
|
||||
const sectionRef = useRef<HTMLElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
setIsVisible(true);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const validateField = (field: keyof ContactFormData, value: string) => {
|
||||
try {
|
||||
@@ -162,18 +156,14 @@ function ContactFormContent() {
|
||||
/>
|
||||
)}
|
||||
|
||||
<section className="py-24 relative overflow-hidden" ref={sectionRef}>
|
||||
<section className="py-24 relative overflow-hidden">
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute inset-0" style={{ background: 'radial-gradient(ellipse at center, rgba(196,30,58,0.03) 0%, transparent 70%)' }} />
|
||||
</div>
|
||||
|
||||
<div className="container-wide relative z-10">
|
||||
<div
|
||||
className={`
|
||||
mb-16 opacity-0 translate-y-4
|
||||
${isVisible ? 'animate-fade-in-up' : ''}
|
||||
`}
|
||||
>
|
||||
{/* 标题区 - InkReveal */}
|
||||
<InkReveal className="mb-16">
|
||||
<div className="flex items-center gap-3 mb-4">
|
||||
<div className="w-8 h-px bg-linear-to-r from-[#1C1C1C] to-[var(--color-brand-primary)]" />
|
||||
<span className="text-sm text-[#5C5C5C] tracking-wide" data-testid="page-badge">联系我们</span>
|
||||
@@ -184,91 +174,87 @@ function ContactFormContent() {
|
||||
<p className="mt-4 text-[#5C5C5C] max-w-2xl" data-testid="page-description">
|
||||
无论您有任何问题或合作意向,我们都很乐意与您交流
|
||||
</p>
|
||||
</div>
|
||||
</InkReveal>
|
||||
|
||||
<div className="grid lg:grid-cols-5 gap-12 lg:gap-16">
|
||||
<div
|
||||
className={`
|
||||
lg:col-span-2 space-y-8 flex flex-col
|
||||
opacity-0 translate-y-4
|
||||
${isVisible ? 'animate-fade-in-up stagger-1' : ''}
|
||||
`}
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-[#1C1C1C] mb-6">联系方式</h2>
|
||||
<div className="space-y-4" data-testid="contact-info">
|
||||
<div className="flex items-start gap-4 group" data-testid="email-info">
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<Mail className="w-5 h-5 text-white" />
|
||||
{/* 左侧联系信息 - StaggerContainer */}
|
||||
<StaggerContainer className="lg:col-span-2 space-y-8 flex flex-col" staggerDelay={0.12}>
|
||||
<StaggerItem>
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-[#1C1C1C] mb-6">联系方式</h2>
|
||||
<div className="space-y-4" data-testid="contact-info">
|
||||
<div className="flex items-start gap-4 group" data-testid="email-info">
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<Mail className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C] mb-1">邮箱</p>
|
||||
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors duration-200" data-testid="email-link">
|
||||
{COMPANY_INFO.email}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C] mb-1">邮箱</p>
|
||||
<a href={`mailto:${COMPANY_INFO.email}`} className="text-[#1C1C1C] hover:text-[var(--color-brand-primary)] transition-colors duration-200" data-testid="email-link">
|
||||
{COMPANY_INFO.email}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-4 group" data-testid="address-info">
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<MapPin className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C] mb-1">地址</p>
|
||||
<p className="text-[#1C1C1C]" data-testid="address-text">{COMPANY_INFO.address}</p>
|
||||
<div className="flex items-start gap-4 group" data-testid="address-info">
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)] rounded-md flex items-center justify-center shrink-0 transition-transform duration-200 group-hover:scale-105">
|
||||
<MapPin className="w-5 h-5 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-[#5C5C5C] mb-1">地址</p>
|
||||
<p className="text-[#1C1C1C]" data-testid="address-text">{COMPANY_INFO.address}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</StaggerItem>
|
||||
|
||||
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]" data-testid="work-hours-card">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Clock className="w-4 h-4 text-[var(--color-brand-primary)]" />
|
||||
<h2 className="text-sm font-medium text-[#1C1C1C]">工作时间</h2>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between text-sm" data-testid="work-hours-row">
|
||||
<span className="text-[#5C5C5C]">周一至周五</span>
|
||||
<span className="text-[var(--color-brand-primary)]">9:00 - 18:00</span>
|
||||
<StaggerItem>
|
||||
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]" data-testid="work-hours-card">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Clock className="w-4 h-4 text-[var(--color-brand-primary)]" />
|
||||
<h2 className="text-sm font-medium text-[#1C1C1C]">工作时间</h2>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="flex justify-between text-sm" data-testid="work-hours-row">
|
||||
<span className="text-[#5C5C5C]">周一至周五</span>
|
||||
<span className="text-[var(--color-brand-primary)]">9:00 - 18:00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</StaggerItem>
|
||||
|
||||
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<HeadphonesIcon className="w-4 h-4 text-[var(--color-brand-primary)]" />
|
||||
<h2 className="text-sm font-medium text-[#1C1C1C]">我们的承诺</h2>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">工作日 2 小时内快速响应您的咨询</p>
|
||||
<StaggerItem>
|
||||
<div className="bg-[#FFFBF5] p-5 rounded-lg border border-[#E5E5E5]">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<HeadphonesIcon className="w-4 h-4 text-[var(--color-brand-primary)]" />
|
||||
<h2 className="text-sm font-medium text-[#1C1C1C]">我们的承诺</h2>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">提供免费的业务咨询和方案评估服务</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">根据您的需求量身定制最优解决方案</p>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">工作日 2 小时内快速响应您的咨询</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">提供免费的业务咨询和方案评估服务</p>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<div className="w-1.5 h-1.5 bg-[var(--color-brand-primary)] rounded-full mt-2 shrink-0" />
|
||||
<p className="text-sm text-[#5C5C5C]">根据您的需求量身定制最优解决方案</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</StaggerItem>
|
||||
</StaggerContainer>
|
||||
|
||||
<div
|
||||
className={`
|
||||
lg:col-span-3 flex flex-col
|
||||
opacity-0 translate-y-4
|
||||
${isVisible ? 'animate-fade-in-up stagger-2' : ''}
|
||||
`}
|
||||
>
|
||||
{/* 右侧表单 - ScrollReveal */}
|
||||
<ScrollReveal delay={0.15}>
|
||||
<div className="bg-[#F5F7FA] p-6 sm:p-8 rounded-lg border border-[#E2E8F0] flex-1 flex flex-col">
|
||||
<h2 className="text-lg font-semibold text-[#1A1A2E] mb-6">发送消息</h2>
|
||||
|
||||
{isSubmitted ? (
|
||||
<div className="text-center py-12 flex-1 flex items-center justify-center">
|
||||
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center mx-auto mb-4 animate-stamp-in">
|
||||
<div className="w-16 h-16 bg-[var(--color-brand-primary)] rounded-full flex items-center justify-center mx-auto mb-4">
|
||||
<CheckCircle2 className="w-8 h-8 text-white" />
|
||||
</div>
|
||||
<h4 className="text-xl font-semibold text-[#1A1A2E] mb-2">消息已发送</h4>
|
||||
@@ -314,9 +300,9 @@ function ContactFormContent() {
|
||||
required
|
||||
value={formData.email}
|
||||
onChange={(e) => handleChange('email', e.target.value)}
|
||||
onBlur={(e) => handleBlur('email', e.target.value)}
|
||||
error={errors.email}
|
||||
/>
|
||||
onBlur={(e) => handleBlur('email', e.target.value)}
|
||||
error={errors.email}
|
||||
/>
|
||||
<Input
|
||||
name="subject"
|
||||
data-testid="subject-input"
|
||||
@@ -325,10 +311,10 @@ function ContactFormContent() {
|
||||
placeholder="请输入消息主题"
|
||||
required
|
||||
value={formData.subject}
|
||||
onChange={(e) => handleChange('subject', e.target.value)}
|
||||
onBlur={(e) => handleBlur('subject', e.target.value)}
|
||||
error={errors.subject}
|
||||
/>
|
||||
onChange={(e) => handleChange('subject', e.target.value)}
|
||||
onBlur={(e) => handleBlur('subject', e.target.value)}
|
||||
error={errors.subject}
|
||||
/>
|
||||
<Textarea
|
||||
name="message"
|
||||
data-testid="message-input"
|
||||
@@ -338,10 +324,10 @@ function ContactFormContent() {
|
||||
rows={5}
|
||||
required
|
||||
value={formData.message}
|
||||
onChange={(e) => handleChange('message', e.target.value)}
|
||||
onBlur={(e) => handleBlur('message', e.target.value)}
|
||||
error={errors.message}
|
||||
/>
|
||||
onChange={(e) => handleChange('message', e.target.value)}
|
||||
onBlur={(e) => handleBlur('message', e.target.value)}
|
||||
error={errors.message}
|
||||
/>
|
||||
<Button
|
||||
type="submit"
|
||||
data-testid="submit-button"
|
||||
@@ -364,7 +350,7 @@ function ContactFormContent() {
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -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 { AnimatedInkDivider } from "@/components/ui/animated-ink-divider";
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
declare global {
|
||||
@@ -102,23 +103,17 @@ function HomeContent({ heroStats }: { heroStats: ReactNode }) {
|
||||
return (
|
||||
<main id="main-content" className="min-h-screen bg-white dark:bg-(--color-bg-primary)">
|
||||
<HeroSection heroStats={heroStats} />
|
||||
{/* 墨韵分割线 */}
|
||||
<div className="ink-divider" />
|
||||
<AnimatedInkDivider />
|
||||
<ServicesSection />
|
||||
{/* 墨韵分割线 */}
|
||||
<div className="ink-divider" />
|
||||
<AnimatedInkDivider />
|
||||
<HomeSolutionsSection />
|
||||
{/* 墨韵分割线 */}
|
||||
<div className="ink-divider" />
|
||||
<AnimatedInkDivider />
|
||||
<ProductsSection />
|
||||
{/* 墨韵分割线 */}
|
||||
<div className="ink-divider" />
|
||||
<AnimatedInkDivider />
|
||||
<AboutSection />
|
||||
{/* 墨韵分割线 */}
|
||||
<div className="ink-divider" />
|
||||
<AnimatedInkDivider />
|
||||
<TeamSection />
|
||||
{/* 墨韵分割线 */}
|
||||
<div className="ink-divider" />
|
||||
<AnimatedInkDivider />
|
||||
<NewsSection />
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo, useRef, ChangeEvent } from 'react';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useState, useMemo, ChangeEvent } from 'react';
|
||||
import { NEWS } from '@/lib/constants';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -10,8 +9,9 @@ import { Button } from '@/components/ui/button';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { Search, Calendar, Filter, ArrowRight, SearchX } from 'lucide-react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Pagination } from '@/components/ui/pagination';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { ScrollReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const categories = ['全部', '公司新闻', '产品发布', '研发动态'];
|
||||
const ITEMS_PER_PAGE = 9;
|
||||
@@ -20,8 +20,7 @@ export default function NewsListPage() {
|
||||
const [selectedCategory, setSelectedCategory] = useState('全部');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const contentRef = useRef(null);
|
||||
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
|
||||
|
||||
const filteredNews = useMemo(() => {
|
||||
return NEWS.filter((newsItem) => {
|
||||
const matchesCategory = selectedCategory === '全部' || newsItem.category === selectedCategory;
|
||||
@@ -60,13 +59,9 @@ export default function NewsListPage() {
|
||||
description="了解睿新致远最新动态,把握行业发展脉搏"
|
||||
/>
|
||||
|
||||
<div className="container-wide relative z-10 py-12" ref={contentRef} id="page-content">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-8 space-y-4"
|
||||
>
|
||||
<div className="container-wide relative z-10 py-12" id="page-content">
|
||||
{/* 筛选区 - InkReveal */}
|
||||
<InkReveal className="mb-8 space-y-4">
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start md:items-center">
|
||||
<div className="flex items-center gap-2 text-[#1C1C1C]">
|
||||
<Filter className="w-5 h-5" />
|
||||
@@ -101,7 +96,7 @@ export default function NewsListPage() {
|
||||
aria-label="搜索新闻"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{paginatedNews.length === 0 ? (
|
||||
<div className="text-center py-20">
|
||||
@@ -118,14 +113,10 @@ export default function NewsListPage() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{paginatedNews.map((newsItem, index) => (
|
||||
<motion.div
|
||||
key={newsItem.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: 0.2 + index * 0.1 }}
|
||||
>
|
||||
{/* 卡片网格 - StaggerContainer */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" staggerDelay={0.08}>
|
||||
{paginatedNews.map((newsItem) => (
|
||||
<StaggerItem key={newsItem.id}>
|
||||
<StaticLink href={`/news/${newsItem.id}`}>
|
||||
<Card className="h-full hover:shadow-lg transition-shadow cursor-pointer border-[#E5E5E5] hover:border-[var(--color-brand-primary)]">
|
||||
<CardContent className="p-0">
|
||||
@@ -164,9 +155,9 @@ export default function NewsListPage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
|
||||
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
|
||||
|
||||
@@ -176,21 +167,23 @@ export default function NewsListPage() {
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Bottom CTA */}
|
||||
<div className="mt-12 text-center py-16 bg-[#F5F5F5] rounded-2xl">
|
||||
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
|
||||
想获取最新行业资讯?
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
|
||||
关注我们的公众号,第一时间了解数字化转型趋势和公司动态
|
||||
</p>
|
||||
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
|
||||
<StaticLink href="/contact">
|
||||
联系我们
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
{/* CTA - ScrollReveal */}
|
||||
<ScrollReveal>
|
||||
<div className="mt-12 text-center py-16 bg-[#F5F5F5] rounded-2xl">
|
||||
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
|
||||
想获取最新行业资讯?
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
|
||||
关注我们的公众号,第一时间了解数字化转型趋势和公司动态
|
||||
</p>
|
||||
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
|
||||
<StaticLink href="/contact">
|
||||
联系我们
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo, useRef, ChangeEvent } from 'react';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useState, useMemo, ChangeEvent } from 'react';
|
||||
import { SERVICES } from '@/lib/constants';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
@@ -9,8 +8,9 @@ import { Input } from '@/components/ui/input';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { Search, ArrowLeft, Code, BarChart3, Lightbulb, Puzzle, Filter, SearchX } from 'lucide-react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { motion } from 'framer-motion';
|
||||
import { Pagination } from '@/components/ui/pagination';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { ScrollReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||
Code,
|
||||
@@ -26,8 +26,7 @@ export default function ServicesPage() {
|
||||
const [selectedCategory, setSelectedCategory] = useState('全部');
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const contentRef = useRef(null);
|
||||
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
|
||||
|
||||
const filteredServices = useMemo(() => {
|
||||
return SERVICES.filter((service) => {
|
||||
const matchesCategory = selectedCategory === '全部' || service.title.includes(selectedCategory);
|
||||
@@ -66,14 +65,10 @@ export default function ServicesPage() {
|
||||
description="专业技术团队,为您提供全方位的数字化解决方案"
|
||||
/>
|
||||
|
||||
<div className="container-wide relative z-10 py-16" ref={contentRef} id="page-content">
|
||||
<div className="container-wide relative z-10 py-16" id="page-content">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="mb-8 space-y-4"
|
||||
>
|
||||
{/* 筛选区 - InkReveal */}
|
||||
<InkReveal className="mb-8 space-y-4">
|
||||
<div className="flex flex-col md:flex-row gap-4 items-start md:items-center">
|
||||
<div className="flex items-center gap-2 text-[#1C1C1C]">
|
||||
<Filter className="w-5 h-5" />
|
||||
@@ -108,7 +103,7 @@ export default function ServicesPage() {
|
||||
aria-label="搜索服务"
|
||||
/>
|
||||
</div>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{paginatedServices.length === 0 ? (
|
||||
<div className="text-center py-20">
|
||||
@@ -125,16 +120,12 @@ export default function ServicesPage() {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
{paginatedServices.map((service, index) => {
|
||||
{/* 卡片网格 - StaggerContainer */}
|
||||
<StaggerContainer className="grid md:grid-cols-2 gap-8" staggerDelay={0.1}>
|
||||
{paginatedServices.map((service) => {
|
||||
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 }}
|
||||
>
|
||||
<StaggerItem key={service.id}>
|
||||
<StaticLink
|
||||
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"
|
||||
@@ -169,10 +160,10 @@ export default function ServicesPage() {
|
||||
</div>
|
||||
</div>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
|
||||
<Pagination currentPage={currentPage} totalPages={totalPages} onPageChange={handlePageChange} scrollTargetId="page-content" />
|
||||
|
||||
@@ -184,31 +175,29 @@ export default function ServicesPage() {
|
||||
</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-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
|
||||
asChild
|
||||
>
|
||||
<StaticLink href="/contact">
|
||||
立即咨询
|
||||
<ArrowLeft className="ml-2 w-4 h-4 rotate-180" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
{/* CTA - ScrollReveal */}
|
||||
<ScrollReveal>
|
||||
<div 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-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white"
|
||||
asChild
|
||||
>
|
||||
<StaticLink href="/contact">
|
||||
立即咨询
|
||||
<ArrowLeft className="ml-2 w-4 h-4 rotate-180" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</ScrollReveal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,41 +1,55 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { motion, useInView, useMotionValue, useSpring, useTransform } from 'framer-motion';
|
||||
import { useRef, type MouseEvent } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { RippleButton } from '@/components/ui/ripple-button';
|
||||
import { Shield, Building2, Users, Code, Target, ArrowRight } from 'lucide-react';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { TextReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const TEAM_PILLARS = [
|
||||
{
|
||||
icon: Shield,
|
||||
title: '12+ 年行业深耕',
|
||||
description: '核心团队长期从事技术咨询、企业数字化等领域,服务覆盖金融、制造、零售、政务、农业等多个行业,积累了丰富的跨行业经验和最佳实践。',
|
||||
},
|
||||
{
|
||||
icon: Building2,
|
||||
title: '大型 IT 企业背景',
|
||||
description: '开发团队成员来自多个大型传统 IT 企业,具备扎实的工程能力、规范化的交付流程和严格的质量意识,确保每一个项目都经得起考验。',
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: '复合型技术团队',
|
||||
description: '我们的团队成员既懂技术又懂业务,能够深入理解客户的真实场景和痛点,提供真正可落地的解决方案,而非纸上谈兵。',
|
||||
},
|
||||
{
|
||||
icon: Code,
|
||||
title: '全栈技术能力',
|
||||
description: '团队掌握从前端到后端、从云原生到数据智能、从移动端到物联网的全栈技术能力,能够应对各种复杂的技术挑战。',
|
||||
},
|
||||
{
|
||||
icon: Target,
|
||||
title: '结果导向交付',
|
||||
description: '我们不以"项目上线"为终点,而是以"客户业务是否真正改善"为衡量标准。每一个交付成果,都追求可量化的业务价值。',
|
||||
},
|
||||
{ icon: Shield, title: '12+ 年行业深耕', description: '核心团队长期从事技术咨询、企业数字化等领域,服务覆盖金融、制造、零售、政务、农业等多个行业,积累了丰富的跨行业经验和最佳实践。' },
|
||||
{ icon: Building2, title: '大型 IT 企业背景', description: '开发团队成员来自多个大型传统 IT 企业,具备扎实的工程能力、规范化的交付流程和严格的质量意识,确保每一个项目都经得起考验。' },
|
||||
{ icon: Users, title: '复合型技术团队', description: '我们的团队成员既懂技术又懂业务,能够深入理解客户的真实场景和痛点,提供真正可落地的解决方案,而非纸上谈兵。' },
|
||||
{ icon: Code, title: '全栈技术能力', description: '团队掌握从前端到后端、从云原生到数据智能、从移动端到物联网的全栈技术能力,能够应对各种复杂的技术挑战。' },
|
||||
{ icon: Target, title: '结果导向交付', description: '我们不以"项目上线"为终点,而是以"客户业务是否真正改善"为衡量标准。每一个交付成果,都追求可量化的业务价值。' },
|
||||
];
|
||||
|
||||
/** 3D Tilt 卡片组件 */
|
||||
function TiltCard({ children, className = '' }: { children: React.ReactNode; className?: string }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const x = useMotionValue(0);
|
||||
const y = useMotionValue(0);
|
||||
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [5, -5]), { stiffness: 300, damping: 30 });
|
||||
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-5, 5]), { stiffness: 300, damping: 30 });
|
||||
|
||||
function handleMouse(e: MouseEvent<HTMLDivElement>) {
|
||||
if (!ref.current) {return;}
|
||||
const rect = ref.current.getBoundingClientRect();
|
||||
x.set((e.clientX - rect.left) / rect.width - 0.5);
|
||||
y.set((e.clientY - rect.top) / rect.height - 0.5);
|
||||
}
|
||||
|
||||
function handleMouseLeave() {
|
||||
x.set(0);
|
||||
y.set(0);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
onMouseMove={handleMouse}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
style={{ rotateX, rotateY, transformPerspective: 800 }}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TeamClient() {
|
||||
const contentRef = useRef(null);
|
||||
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
|
||||
@@ -48,81 +62,57 @@ export function TeamClient() {
|
||||
/>
|
||||
|
||||
<div ref={contentRef} className="container-wide py-12 md:py-16">
|
||||
{/* 团队概述 - TextReveal + InkReveal */}
|
||||
<InkReveal className="max-w-5xl mx-auto mb-12">
|
||||
<div className="bg-[#FFFBF5] rounded-2xl p-8 md:p-12 border border-[#E5E5E5]">
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">关于我们的团队</h2>
|
||||
<TextReveal
|
||||
text="我们的核心团队长期从事技术咨询、企业数字化等行业,拥有 12 年以上的深厚积累。开发团队成员来自于多个大型传统 IT 企业,具备扎实的工程能力和规范化的交付经验。我们相信,优秀的技术咨询不仅需要过硬的技术能力,更需要深入理解客户的业务场景和真实需求。"
|
||||
className="text-[#5C5C5C] leading-relaxed max-w-3xl mx-auto text-center"
|
||||
delay={0.1}
|
||||
/>
|
||||
</div>
|
||||
</InkReveal>
|
||||
|
||||
{/* 团队优势 - StaggerContainer + TiltCard */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16" staggerDelay={0.1}>
|
||||
{TEAM_PILLARS.map((item, idx) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<StaggerItem key={item.title}>
|
||||
<TiltCard className={idx >= 3 ? 'md:col-span-1 lg:col-start-1' : ''}>
|
||||
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{item.title}</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed">{item.description}</p>
|
||||
</div>
|
||||
</TiltCard>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</StaggerContainer>
|
||||
|
||||
{/* Bottom CTA */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6 }}
|
||||
className="max-w-5xl mx-auto"
|
||||
transition={{ duration: 0.6, delay: 0.4 }}
|
||||
className="text-center py-16 bg-[#F5F5F5] rounded-2xl"
|
||||
>
|
||||
{/* 团队概述 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.1 }}
|
||||
className="bg-[#FFFBF5] rounded-2xl p-8 md:p-12 border border-[#E5E5E5] mb-16"
|
||||
>
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-6 text-center">关于我们的团队</h2>
|
||||
<div className="space-y-4 max-w-3xl mx-auto text-center">
|
||||
<p className="text-[#5C5C5C] leading-relaxed">
|
||||
我们的核心团队长期从事<span className="text-[var(--color-brand-primary)] font-medium">技术咨询</span>、<span className="text-[var(--color-brand-primary)] font-medium">企业数字化</span>等行业,拥有 12 年以上的深厚积累。
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] leading-relaxed">
|
||||
开发团队成员来自于多个<span className="text-[var(--color-brand-primary)] font-medium">大型传统 IT 企业</span>,具备扎实的工程能力和规范化的交付经验。
|
||||
</p>
|
||||
<p className="text-[#5C5C5C] leading-relaxed">
|
||||
我们相信,优秀的技术咨询不仅需要过硬的技术能力,更需要深入理解客户的业务场景和真实需求。
|
||||
每一位成员都是既懂技术又懂业务的复合型人才。
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
{/* 团队优势 */}
|
||||
<div className="mb-16">
|
||||
<h2 className="text-2xl font-bold text-[#1C1C1C] mb-8 text-center">团队优势</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{TEAM_PILLARS.map((item, idx) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<motion.div
|
||||
key={item.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: 0.2 + idx * 0.1 }}
|
||||
className={idx >= 3 ? 'md:col-span-1 lg:col-start-1' : ''}
|
||||
>
|
||||
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-md transition-all duration-300 h-full">
|
||||
<div className="w-12 h-12 bg-[var(--color-brand-primary)] rounded-xl flex items-center justify-center mb-4">
|
||||
<Icon className="w-6 h-6 text-white" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{item.title}</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed">{item.description}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom CTA */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isContentInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
className="mt-16 text-center py-16 bg-[#F5F5F5] rounded-2xl"
|
||||
>
|
||||
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
|
||||
想了解更多?
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
|
||||
我们期待与您交流,共同探讨数字化转型之路
|
||||
</p>
|
||||
<Button size="lg" className="bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white" asChild>
|
||||
<StaticLink href="/contact">
|
||||
立即咨询
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</StaticLink>
|
||||
</Button>
|
||||
</motion.div>
|
||||
<h3 className="text-xl md:text-2xl font-semibold text-[#1C1C1C] mb-3">
|
||||
想了解更多?
|
||||
</h3>
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-lg mx-auto">
|
||||
我们期待与您交流,共同探讨数字化转型之路
|
||||
</p>
|
||||
<StaticLink href="/contact">
|
||||
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 bg-[var(--color-brand-primary)] hover:bg-[var(--color-brand-primary-hover)] text-white rounded-lg text-sm font-medium transition-colors">
|
||||
立即咨询
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</RippleButton>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { RippleButton } from '@/components/ui/ripple-button';
|
||||
import { COMPANY_INFO } from '@/lib/constants';
|
||||
import { ArrowRight, Target, HeartHandshake, Award } from 'lucide-react';
|
||||
import { useReducedMotion } from '@/hooks/use-reduced-motion';
|
||||
import { InkReveal, BlurReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { TextReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const VALUES = [
|
||||
{ title: '务实', description: '不追逐风口,只做真正为客户创造价值的事。', icon: Target },
|
||||
@@ -26,13 +27,8 @@ export function AboutSection() {
|
||||
<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={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6 }}
|
||||
className="max-w-4xl mx-auto"
|
||||
>
|
||||
{/* 标题 */}
|
||||
{/* 标题 - InkReveal 墨迹揭示 */}
|
||||
<InkReveal className="max-w-4xl mx-auto">
|
||||
<div className="text-center mb-12">
|
||||
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
|
||||
<h2 id="about-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
|
||||
@@ -42,60 +38,53 @@ export function AboutSection() {
|
||||
{COMPANY_INFO.slogan}
|
||||
</p>
|
||||
</div>
|
||||
</InkReveal>
|
||||
|
||||
{/* 品牌理念引用 */}
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.1 }}
|
||||
className="bg-white rounded-2xl p-8 mb-12 border border-[#E5E5E5]"
|
||||
>
|
||||
<blockquote className="text-lg text-[#5C5C5C] leading-relaxed text-center mb-6 italic">
|
||||
<p>“企业需要的,不是一个高高在上的‘专家’,也不是一个做完就跑的‘卖家’,而是一个能坐下来、一起想办法的同行者。”</p>
|
||||
</blockquote>
|
||||
<p className="text-[#1C1C1C] font-medium text-center">
|
||||
我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。
|
||||
</p>
|
||||
</motion.div>
|
||||
{/* 品牌理念 - TextReveal 逐词揭示 */}
|
||||
<TextReveal
|
||||
text="企业需要的,不是一个高高在上的专家,也不是一个做完就跑的卖家,而是一个能坐下来、一起想办法的同行者。"
|
||||
className="text-center text-lg text-[#5C5C5C] leading-relaxed mb-8 max-w-3xl mx-auto"
|
||||
delay={0.1}
|
||||
/>
|
||||
|
||||
{/* 核心价值观 */}
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.15 }}
|
||||
className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16"
|
||||
>
|
||||
{VALUES.map((value) => {
|
||||
const Icon = value.icon;
|
||||
return (
|
||||
<div
|
||||
key={value.title}
|
||||
className="bg-white rounded-xl p-6 border border-[#E5E5E5] text-center"
|
||||
>
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<Icon className="w-5 h-5 text-[var(--color-brand-primary)]" />
|
||||
{/* 核心理念强调 - BlurReveal */}
|
||||
<BlurReveal delay={0.3} className="bg-white rounded-2xl p-8 mb-12 border border-[#E5E5E5]">
|
||||
<p className="text-[#1C1C1C] font-medium text-center text-lg">
|
||||
我们只做一件事:成为您数字化转型路上,信得过的成长伙伴。
|
||||
</p>
|
||||
</BlurReveal>
|
||||
|
||||
{/* 核心价值观 - StaggerContainer 交错入场 */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-16" staggerDelay={0.15}>
|
||||
{VALUES.map((value) => {
|
||||
const Icon = value.icon;
|
||||
return (
|
||||
<StaggerItem key={value.title}>
|
||||
<div className="bg-white rounded-xl p-6 border border-[#E5E5E5] text-center hover:border-[var(--color-brand-primary)]/20 hover:shadow-md transition-all duration-300">
|
||||
<div className="w-10 h-10 bg-[var(--color-brand-primary)]/10 rounded-full flex items-center justify-center mx-auto mb-3">
|
||||
<Icon className="w-5 h-5 text-[var(--color-brand-primary)]" />
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{value.title}</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed">{value.description}</p>
|
||||
</div>
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2">{value.title}</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed">{value.description}</p>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</motion.div>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</StaggerContainer>
|
||||
|
||||
{/* CTA */}
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.5 }}
|
||||
className="text-center"
|
||||
>
|
||||
<StaticLink href="/about">
|
||||
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary)] transition-colors">
|
||||
了解更多关于我们
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</RippleButton>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
{/* CTA */}
|
||||
<motion.div
|
||||
initial={shouldReduceMotion ? {} : { opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={shouldReduceMotion ? { duration: 0 } : { duration: 0.6, delay: 0.5 }}
|
||||
className="text-center"
|
||||
>
|
||||
<StaticLink href="/about">
|
||||
<RippleButton className="inline-flex items-center gap-2 px-6 py-2.5 border border-[#E5E5E5] rounded-lg text-sm font-medium text-[#1C1C1C] hover:border-[var(--color-brand-primary)] hover:text-[var(--color-brand-primary)] transition-colors">
|
||||
了解更多关于我们
|
||||
<ArrowRight className="w-4 h-4" />
|
||||
</RippleButton>
|
||||
</StaticLink>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowRight, Lightbulb, Cpu, Users } from 'lucide-react';
|
||||
import { InkReveal, StaggerContainer, StaggerItem, CountUp } from '@/lib/animations';
|
||||
import { ScrollReveal } from '@/components/ui/scroll-animations';
|
||||
|
||||
const SOLUTIONS_OVERVIEW = [
|
||||
{
|
||||
@@ -31,6 +32,13 @@ const SOLUTIONS_OVERVIEW = [
|
||||
},
|
||||
];
|
||||
|
||||
const SOLUTION_STATS = [
|
||||
{ value: 5, label: '覆盖行业', suffix: '+' },
|
||||
{ value: 6, label: '自研产品', suffix: '款' },
|
||||
{ value: 12, label: '年核心成员行业经验', suffix: '' },
|
||||
{ value: 98, label: '客户满意度', suffix: '%' },
|
||||
];
|
||||
|
||||
export function HomeSolutionsSection() {
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' });
|
||||
@@ -38,12 +46,8 @@ export function HomeSolutionsSection() {
|
||||
return (
|
||||
<section id="solutions" role="region" aria-labelledby="solutions-heading" className="py-24 bg-[#F5F5F5] relative overflow-hidden" ref={ref}>
|
||||
<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"
|
||||
>
|
||||
{/* 标题 - InkReveal 墨迹揭示 */}
|
||||
<InkReveal className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
|
||||
<h2 id="solutions-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
|
||||
三种角色,一种<span className="text-[var(--color-brand-primary)] font-calligraphy">身份</span>
|
||||
@@ -51,18 +55,30 @@ export function HomeSolutionsSection() {
|
||||
<p className="text-lg text-[#5C5C5C]">
|
||||
您的数字化转型成长伙伴——从战略咨询到技术落地,再到长期陪跑
|
||||
</p>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{SOLUTIONS_OVERVIEW.map((item, idx) => {
|
||||
{/* 数据指标 - ScrollReveal 滚动驱动 + CountUp 数字滚动 */}
|
||||
<ScrollReveal className="grid grid-cols-2 md:grid-cols-4 gap-4 max-w-4xl mx-auto mb-16">
|
||||
{SOLUTION_STATS.map((stat) => (
|
||||
<div
|
||||
key={stat.label}
|
||||
className="text-center py-6 px-4 bg-white rounded-2xl border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/20 hover:shadow-md transition-all duration-300"
|
||||
>
|
||||
<div className="text-3xl sm:text-4xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent mb-1">
|
||||
<CountUp end={stat.value} duration={2000} />
|
||||
{stat.suffix}
|
||||
</div>
|
||||
<div className="text-sm text-[#5C5C5C]">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</ScrollReveal>
|
||||
|
||||
{/* 卡片 - StaggerContainer 交错入场 */}
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-3 gap-8" staggerDelay={0.15}>
|
||||
{SOLUTIONS_OVERVIEW.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<motion.div
|
||||
key={item.title}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: 0.1 + idx * 0.15 }}
|
||||
>
|
||||
<StaggerItem key={item.title}>
|
||||
<div className="bg-white rounded-2xl p-8 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col">
|
||||
<div className="w-14 h-14 bg-[var(--color-brand-primary)] rounded-2xl flex items-center justify-center mb-6">
|
||||
<Icon className="w-7 h-7 text-white" />
|
||||
@@ -79,10 +95,10 @@ export function HomeSolutionsSection() {
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</motion.div>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowRight, Calendar } from 'lucide-react';
|
||||
import { NEWS } from '@/lib/constants';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
|
||||
export function NewsSection() {
|
||||
const ref = useRef(null);
|
||||
@@ -20,12 +20,8 @@ export function NewsSection() {
|
||||
return (
|
||||
<section id="news" role="region" aria-labelledby="news-heading" className="py-24 bg-[#F5F5F5]" ref={ref}>
|
||||
<div className="container-wide">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="text-center max-w-3xl mx-auto mb-16"
|
||||
>
|
||||
{/* 标题 - InkReveal 墨迹揭示 */}
|
||||
<InkReveal className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
|
||||
<h2 id="news-heading" className="text-3xl sm:text-4xl lg:text-5xl font-bold text-[#1C1C1C] mb-6">
|
||||
最新<span className="text-[var(--color-brand-primary)] font-calligraphy">资讯</span>
|
||||
@@ -33,17 +29,12 @@ export function NewsSection() {
|
||||
<p className="text-lg text-[#5C5C5C]">
|
||||
了解公司最新动态、行业资讯和技术分享
|
||||
</p>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{displayedNews.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-5xl mx-auto">
|
||||
{displayedNews.map((newsItem, idx) => (
|
||||
<motion.div
|
||||
key={newsItem.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.4, delay: idx * 0.08 }}
|
||||
>
|
||||
<StaggerContainer className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-5xl mx-auto" staggerDelay={0.1}>
|
||||
{displayedNews.map((newsItem) => (
|
||||
<StaggerItem key={newsItem.id}>
|
||||
<Card className="h-full flex flex-col group cursor-pointer border-[#E5E5E5] hover:border-[#1C1C1C]">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
@@ -70,9 +61,9 @@ export function NewsSection() {
|
||||
</StaticLink>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
</StaggerItem>
|
||||
))}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-lg text-[#5C5C5C]">暂无新闻信息</p>
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { RippleButton } from '@/components/ui/ripple-button';
|
||||
import { InkCard } from '@/lib/animations';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { ArrowRight, Check, TrendingUp } from 'lucide-react';
|
||||
import { InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { ArrowRight, Check, Database, Users, FileText, BarChart3, Layers, MessageSquare } from 'lucide-react';
|
||||
import { PRODUCTS } from '@/lib/constants';
|
||||
|
||||
const productIcons: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||
erp: Layers,
|
||||
crm: Users,
|
||||
cms: FileText,
|
||||
bi: BarChart3,
|
||||
dss: Database,
|
||||
oa: MessageSquare,
|
||||
};
|
||||
|
||||
export function ProductsSection() {
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' });
|
||||
@@ -20,103 +26,101 @@ export function ProductsSection() {
|
||||
<div className="absolute top-1/2 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-left max-w-3xl mx-auto mb-16"
|
||||
>
|
||||
{/* 标题 - InkReveal 墨迹揭示 */}
|
||||
<InkReveal className="text-left max-w-3xl mx-auto mb-16">
|
||||
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
|
||||
<h2 id="products-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
|
||||
我们的<span className="text-[var(--color-brand-primary)] font-calligraphy">产品</span>
|
||||
</h2>
|
||||
<p className="text-lg text-[#5C5C5C]">
|
||||
自主研发的企业级产品,助力企业高效运营,实现数字化转型
|
||||
自主研发的企业级产品矩阵,覆盖 ERP、CRM、OA 等核心场景,支持信创环境与私有化部署
|
||||
</p>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{PRODUCTS.length > 0 ? (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 items-stretch">
|
||||
{PRODUCTS.map((product) => (
|
||||
<InkCard
|
||||
key={product.id}
|
||||
className="group cursor-pointer rounded-xl border border-[#E5E5E5] bg-white p-0 overflow-hidden hover:border-[var(--color-brand-primary)] transition-colors"
|
||||
>
|
||||
<StaticLink href={`/products/${product.id}`}>
|
||||
<Card className="h-full flex flex-col border-0 shadow-none bg-transparent">
|
||||
<CardHeader>
|
||||
<Badge variant="secondary" className="w-fit mb-3">
|
||||
{product.category}
|
||||
</Badge>
|
||||
<CardTitle>{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]"
|
||||
<StaggerContainer className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6" staggerDelay={0.08}>
|
||||
{PRODUCTS.map((product) => {
|
||||
const Icon = productIcons[product.id] || Layers;
|
||||
return (
|
||||
<StaggerItem key={product.id}>
|
||||
<StaticLink href={`/products/${product.id}`} className="block group">
|
||||
<div className="relative rounded-2xl border border-[#E5E5E5] bg-white p-6 hover:border-[var(--color-brand-primary)]/30 hover:shadow-xl transition-all duration-500 overflow-hidden h-full">
|
||||
{/* 悬停光泽效果 */}
|
||||
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 pointer-events-none">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-[var(--color-brand-primary)]/[0.02] to-transparent" />
|
||||
</div>
|
||||
|
||||
<div className="relative z-10">
|
||||
{/* 图标 + 分类 */}
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] group-hover:bg-[var(--color-brand-primary)] flex items-center justify-center transition-colors duration-300">
|
||||
<Icon className="w-6 h-6 text-[#5C5C5C] group-hover:text-white transition-colors duration-300" />
|
||||
</div>
|
||||
<span className="text-xs text-[#5C5C5C] bg-[#F5F5F5] px-2.5 py-1 rounded-full group-hover:bg-[var(--color-brand-primary)]/10 group-hover:text-[var(--color-brand-primary)] transition-colors duration-300">
|
||||
{product.category}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* 标题 + 描述 */}
|
||||
<h3 className="text-lg font-bold text-[#1C1C1C] mb-2 group-hover:text-[var(--color-brand-primary)] transition-colors duration-300">
|
||||
{product.title}
|
||||
</h3>
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed mb-4 line-clamp-2">
|
||||
{product.description}
|
||||
</p>
|
||||
|
||||
{/* 核心功能标签 */}
|
||||
<div className="flex flex-wrap gap-1.5 mb-4">
|
||||
{product.features.slice(0, 3).map((feature, idx) => (
|
||||
<span
|
||||
key={idx}
|
||||
className="inline-flex items-center text-xs px-2 py-0.5 bg-[#FAFAFA] text-[#3D3D3D] rounded border border-[#E5E5E5] group-hover:border-[var(--color-brand-primary)]/15 transition-colors duration-300"
|
||||
>
|
||||
<Check className="w-3 h-3 mr-1 text-[var(--color-brand-primary)]" />
|
||||
{feature}
|
||||
</span>
|
||||
))}
|
||||
{product.features.length > 3 && (
|
||||
<span className="text-xs text-[#5C5C5C] px-1 py-0.5">
|
||||
+{product.features.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* CTA */}
|
||||
<div className="flex items-center text-sm font-medium text-[var(--color-brand-primary)] opacity-0 translate-y-2 group-hover:opacity-100 group-hover:translate-y-0 transition-all duration-300">
|
||||
了解详情
|
||||
<ArrowRight className="ml-1.5 w-4 h-4 transition-transform group-hover:translate-x-1" />
|
||||
</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-[var(--color-brand-primary)]" />
|
||||
核心价值
|
||||
</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-[var(--color-brand-primary)] mr-1.5">•</span>
|
||||
{benefit}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="w-full mt-auto px-4 py-2 text-center text-sm font-medium border border-[#E5E5E5] rounded-md group-hover:bg-[var(--color-brand-primary-hover)] group-hover:text-white group-hover:border-[var(--color-brand-primary-hover)] transition-colors">
|
||||
了解详情
|
||||
<ArrowRight className="ml-2 w-4 h-4 inline" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</StaticLink>
|
||||
</InkCard>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</StaticLink>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</StaggerContainer>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-lg text-[#5C5C5C]">暂无产品信息</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
{/* 定制化 CTA */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.6, delay: 0.5 }}
|
||||
className="mt-20 text-center"
|
||||
className="mt-16 text-center"
|
||||
>
|
||||
<div className="bg-white rounded-2xl p-12 border border-[#E2E8F0] relative overflow-hidden">
|
||||
<div className="bg-[#F5F5F5] rounded-2xl p-10 border border-[#E5E5E5] relative overflow-hidden">
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute top-0 right-0 w-64 h-64 bg-[rgba(79,70,229,0.03)] rounded-full blur-3xl" />
|
||||
<div className="absolute bottom-0 left-0 w-48 h-48 bg-[rgba(196,30,58,0.02)] rounded-full blur-3xl" />
|
||||
<div className="absolute top-0 right-0 w-48 h-48 bg-[rgba(196,30,58,0.03)] rounded-full blur-3xl" />
|
||||
</div>
|
||||
<div className="relative z-10">
|
||||
<h3 className="text-2xl sm:text-3xl font-bold text-[#1A1A2E] mb-4">
|
||||
<h3 className="text-2xl font-bold text-[#1C1C1C] mb-3">
|
||||
需要定制化解决方案?
|
||||
</h3>
|
||||
<p className="text-[#718096] mb-8 max-w-2xl mx-auto">
|
||||
<p className="text-[#5C5C5C] mb-6 max-w-xl mx-auto">
|
||||
我们的专业团队可以根据您的业务需求,提供量身定制的产品开发和系统集成服务
|
||||
</p>
|
||||
<StaticLink href="/contact">
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { Code, BarChart3, Lightbulb, Puzzle, ArrowRight } from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { RippleButton } from '@/components/ui/ripple-button';
|
||||
import { InkCard } from '@/lib/animations';
|
||||
import { InkCard, InkReveal, StaggerContainer, StaggerItem } from '@/lib/animations';
|
||||
import { SERVICES } from '@/lib/constants';
|
||||
|
||||
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||
@@ -27,12 +26,8 @@ export function ServicesSection() {
|
||||
<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"
|
||||
>
|
||||
{/* 标题 - InkReveal 墨迹揭示 */}
|
||||
<InkReveal className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
|
||||
<h2 id="services-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-4">
|
||||
我们的 <span className="text-[var(--color-brand-primary)] font-calligraphy">核心业务</span>
|
||||
@@ -40,36 +35,37 @@ export function ServicesSection() {
|
||||
<p className="text-lg text-[#5C5C5C] max-w-2xl">
|
||||
专业技术团队,为您提供全方位的数字化解决方案
|
||||
</p>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{SERVICES.length > 0 ? (
|
||||
<div className="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<StaggerContainer className="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-6" staggerDelay={0.12}>
|
||||
{SERVICES.map((service) => {
|
||||
const Icon = iconMap[service.icon];
|
||||
return (
|
||||
<InkCard
|
||||
key={service.id}
|
||||
className="rounded-xl border border-[#E5E5E5] bg-white p-6 hover:border-[var(--color-brand-primary)] transition-colors"
|
||||
>
|
||||
<StaticLink href={`/services/${service.id}`}>
|
||||
<Card className="p-0 h-full border-0 shadow-none bg-transparent group cursor-pointer">
|
||||
<CardContent className="p-0">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] flex items-center justify-center mb-4 group-hover:bg-[var(--color-brand-primary)] 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-[var(--color-brand-primary)] transition-colors">{service.title}</h3>
|
||||
<p className="text-[#5C5C5C] text-sm leading-relaxed">{service.description}</p>
|
||||
<div className="mt-4 flex items-center text-[var(--color-brand-primary)] text-sm font-medium opacity-0 md:group-hover:opacity-100 md:opacity-0 transition-opacity">
|
||||
了解详情
|
||||
<ArrowRight className="ml-1 w-4 h-4" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</StaticLink>
|
||||
</InkCard>
|
||||
<StaggerItem key={service.id}>
|
||||
<InkCard
|
||||
className="rounded-xl border border-[#E5E5E5] bg-white p-6 hover:border-[var(--color-brand-primary)] transition-colors"
|
||||
>
|
||||
<StaticLink href={`/services/${service.id}`}>
|
||||
<Card className="p-0 h-full border-0 shadow-none bg-transparent group cursor-pointer">
|
||||
<CardContent className="p-0">
|
||||
<div className="w-12 h-12 rounded-xl bg-[#F5F5F5] flex items-center justify-center mb-4 group-hover:bg-[var(--color-brand-primary)] 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-[var(--color-brand-primary)] transition-colors">{service.title}</h3>
|
||||
<p className="text-[#5C5C5C] text-sm leading-relaxed">{service.description}</p>
|
||||
<div className="mt-4 flex items-center text-[var(--color-brand-primary)] text-sm font-medium opacity-0 md:group-hover:opacity-100 md:opacity-0 transition-opacity">
|
||||
了解详情
|
||||
<ArrowRight className="ml-1 w-4 h-4" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</StaticLink>
|
||||
</InkCard>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
) : (
|
||||
<div className="text-center py-12">
|
||||
<p className="text-lg text-[#5C5C5C]">暂无服务信息</p>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { motion } from 'framer-motion';
|
||||
import { useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { motion, useInView, useMotionValue, useSpring, useTransform } from 'framer-motion';
|
||||
import { useRef, type MouseEvent } from 'react';
|
||||
import { StaticLink } from '@/components/ui/static-link';
|
||||
import { RippleButton } from '@/components/ui/ripple-button';
|
||||
import { ArrowRight, Briefcase, GraduationCap, Target, Users } from 'lucide-react';
|
||||
import { InkReveal, StaggerContainer, StaggerItem, CountUp } from '@/lib/animations';
|
||||
|
||||
const TEAM_MEMBERS = [
|
||||
{
|
||||
@@ -43,12 +43,46 @@ const TEAM_MEMBERS = [
|
||||
];
|
||||
|
||||
const TEAM_STATS = [
|
||||
{ value: '12+', label: '年团队经验' },
|
||||
{ value: '80%', label: '本科及以上学历' },
|
||||
{ value: '4', label: '核心服务' },
|
||||
{ value: '5+', label: '行业覆盖' },
|
||||
{ value: 12, label: '年+核心成员行业经验', suffix: '' },
|
||||
{ value: 80, label: '%本科及以上学历', suffix: '' },
|
||||
{ value: 4, label: '核心服务', suffix: '' },
|
||||
{ value: 5, label: '行业+', suffix: '' },
|
||||
];
|
||||
|
||||
/** 3D Tilt 卡片组件 */
|
||||
function TiltCard({ children, className = '' }: { children: React.ReactNode; className?: string }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const x = useMotionValue(0);
|
||||
const y = useMotionValue(0);
|
||||
|
||||
const rotateX = useSpring(useTransform(y, [-0.5, 0.5], [6, -6]), { stiffness: 300, damping: 30 });
|
||||
const rotateY = useSpring(useTransform(x, [-0.5, 0.5], [-6, 6]), { stiffness: 300, damping: 30 });
|
||||
|
||||
function handleMouse(e: MouseEvent<HTMLDivElement>) {
|
||||
if (!ref.current) {return;}
|
||||
const rect = ref.current.getBoundingClientRect();
|
||||
x.set((e.clientX - rect.left) / rect.width - 0.5);
|
||||
y.set((e.clientY - rect.top) / rect.height - 0.5);
|
||||
}
|
||||
|
||||
function handleMouseLeave() {
|
||||
x.set(0);
|
||||
y.set(0);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
onMouseMove={handleMouse}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
style={{ rotateX, rotateY, transformPerspective: 800 }}
|
||||
className={className}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
export function TeamSection() {
|
||||
const ref = useRef(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-100px' });
|
||||
@@ -59,13 +93,8 @@ export function TeamSection() {
|
||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-[600px] h-[600px] 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"
|
||||
>
|
||||
{/* 标题区 - InkReveal 墨迹揭示 */}
|
||||
<InkReveal className="text-center max-w-3xl mx-auto mb-16">
|
||||
<div className="w-16 h-1 bg-[var(--color-brand-primary)] rounded-full mb-6" />
|
||||
<h2 id="team-heading" className="text-4xl md:text-5xl font-bold text-[#1C1C1C] mb-6">
|
||||
核心<span className="text-[var(--color-brand-primary)] font-calligraphy">团队</span>
|
||||
@@ -73,9 +102,9 @@ export function TeamSection() {
|
||||
<p className="text-lg text-[#5C5C5C] leading-relaxed">
|
||||
来自大型IT企业的核心团队,既懂技术又懂业务,能深入理解客户场景,提供真正落地的解决方案。
|
||||
</p>
|
||||
</motion.div>
|
||||
</InkReveal>
|
||||
|
||||
{/* 团队数据概览 */}
|
||||
{/* 团队数据概览 - CountUp 数字滚动 */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
@@ -88,55 +117,53 @@ export function TeamSection() {
|
||||
className="text-center py-4 px-3 bg-white rounded-xl border border-[#E5E5E5]"
|
||||
>
|
||||
<div className="text-2xl sm:text-3xl font-bold bg-gradient-to-r from-[var(--color-brand-primary)] to-[#E85D75] bg-clip-text text-transparent mb-1">
|
||||
{stat.value}
|
||||
<CountUp end={stat.value} duration={2000} />
|
||||
{stat.suffix}
|
||||
</div>
|
||||
<div className="text-xs sm:text-sm text-[#5C5C5C]">{stat.label}</div>
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* 团队成员卡片 */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 max-w-6xl mx-auto mb-12">
|
||||
{TEAM_MEMBERS.map((member, idx) => {
|
||||
{/* 团队成员卡片 - StaggerContainer + 3D Tilt */}
|
||||
<StaggerContainer className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 max-w-6xl mx-auto mb-12" staggerDelay={0.12}>
|
||||
{TEAM_MEMBERS.map((member) => {
|
||||
const Icon = member.icon;
|
||||
return (
|
||||
<motion.div
|
||||
key={member.name}
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={isInView ? { opacity: 1, y: 0 } : {}}
|
||||
transition={{ duration: 0.5, delay: 0.2 + idx * 0.12 }}
|
||||
>
|
||||
<div className="bg-white rounded-2xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col group">
|
||||
{/* 头像区 */}
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className={`w-14 h-14 rounded-2xl bg-gradient-to-br ${member.accentColor} flex items-center justify-center shrink-0 group-hover:scale-105 transition-transform duration-300`}>
|
||||
<Icon className="w-7 h-7 text-white" />
|
||||
<StaggerItem key={member.name}>
|
||||
<TiltCard className="h-full">
|
||||
<div className="bg-white rounded-2xl p-6 border border-[#E5E5E5] hover:border-[var(--color-brand-primary)]/30 hover:shadow-lg transition-all duration-300 h-full flex flex-col group">
|
||||
{/* 头像区 */}
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
<div className={`w-14 h-14 rounded-2xl bg-gradient-to-br ${member.accentColor} flex items-center justify-center shrink-0 group-hover:scale-105 transition-transform duration-300`}>
|
||||
<Icon className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h3 className="text-base font-bold text-[#1C1C1C] truncate">{member.name}</h3>
|
||||
<span className="text-xs text-[var(--color-brand-primary)] font-medium">{member.initials}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h3 className="text-base font-bold text-[#1C1C1C] truncate">{member.name}</h3>
|
||||
<span className="text-xs text-[var(--color-brand-primary)] font-medium">{member.initials}</span>
|
||||
|
||||
{/* 简介 */}
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed mb-4 flex-1">{member.bio}</p>
|
||||
|
||||
{/* 专业领域标签 */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{member.specialties.map((spec) => (
|
||||
<span
|
||||
key={spec}
|
||||
className="inline-flex items-center text-xs px-2.5 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded-full border border-[#E5E5E5] group-hover:border-[var(--color-brand-primary)]/20 transition-colors"
|
||||
>
|
||||
{spec}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 简介 */}
|
||||
<p className="text-sm text-[#5C5C5C] leading-relaxed mb-4 flex-1">{member.bio}</p>
|
||||
|
||||
{/* 专业领域标签 */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{member.specialties.map((spec) => (
|
||||
<span
|
||||
key={spec}
|
||||
className="inline-flex items-center text-xs px-2.5 py-1 bg-[#FAFAFA] text-[#3D3D3D] rounded-full border border-[#E5E5E5] group-hover:border-[var(--color-brand-primary)]/20 transition-colors"
|
||||
>
|
||||
{spec}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</TiltCard>
|
||||
</StaggerItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</StaggerContainer>
|
||||
|
||||
{/* CTA */}
|
||||
<motion.div
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
'use client';
|
||||
|
||||
import { motion, useInView } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
|
||||
/**
|
||||
* AnimatedInkDivider - 墨韵分割线(滚动展开动画版)
|
||||
*
|
||||
* 替代静态的 .ink-divider CSS 类。
|
||||
* 线条从中心向两侧展开,中心圆点弹性出现。
|
||||
*
|
||||
* @param className - 额外的 CSS 类名
|
||||
* @param delay - 动画延迟(秒)
|
||||
*/
|
||||
export function AnimatedInkDivider({ className = '', delay = 0 }: { className?: string; delay?: number }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const isInView = useInView(ref, { once: true, margin: '-80px' });
|
||||
|
||||
return (
|
||||
<div ref={ref} className={`relative py-6 flex items-center justify-center ${className}`}>
|
||||
{/* 左侧线条 - 从中心向左展开 */}
|
||||
<motion.div
|
||||
className="h-px flex-1 max-w-[200px] bg-gradient-to-r from-transparent to-[#1C1C1C]/20"
|
||||
initial={{ scaleX: 0, originX: 1 }}
|
||||
animate={isInView ? { scaleX: 1 } : {}}
|
||||
transition={{ duration: 0.8, delay, ease: [0.16, 1, 0.3, 1] }}
|
||||
/>
|
||||
{/* 中心装饰 - 圆点 + 外圈 */}
|
||||
<motion.div
|
||||
className="relative mx-4 flex items-center justify-center"
|
||||
initial={{ scale: 0 }}
|
||||
animate={isInView ? { scale: 1 } : {}}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 300,
|
||||
damping: 20,
|
||||
delay: delay + 0.2,
|
||||
}}
|
||||
>
|
||||
{/* 外圈光晕 */}
|
||||
<motion.div
|
||||
className="absolute w-5 h-5 rounded-full bg-[var(--color-brand-primary)]/10"
|
||||
initial={{ scale: 0 }}
|
||||
animate={isInView ? { scale: 1 } : {}}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 200,
|
||||
damping: 15,
|
||||
delay: delay + 0.35,
|
||||
}}
|
||||
/>
|
||||
{/* 中心实心圆 */}
|
||||
<div className="w-2 h-2 rounded-full bg-[var(--color-brand-primary)]" />
|
||||
</motion.div>
|
||||
{/* 右侧线条 - 从中心向右展开 */}
|
||||
<motion.div
|
||||
className="h-px flex-1 max-w-[200px] bg-gradient-to-l from-transparent to-[#1C1C1C]/20"
|
||||
initial={{ scaleX: 0, originX: 0 }}
|
||||
animate={isInView ? { scaleX: 1 } : {}}
|
||||
transition={{ duration: 0.8, delay, ease: [0.16, 1, 0.3, 1] }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,6 @@ export interface StatItem {
|
||||
export const STATS: StatItem[] = [
|
||||
{ value: '12+', label: '年核心成员行业经验' },
|
||||
{ value: '4', label: '核心服务' },
|
||||
{ value: '4', label: '自研产品' },
|
||||
{ value: '6', label: '自研产品' },
|
||||
{ value: '5+', label: '行业覆盖' },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user