From a6e1761b3dedf20b7ccaffbce3aa3d1ace04ae20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Thu, 30 Apr 2026 19:18:07 +0800 Subject: [PATCH] feat: add ProductCard, ProductMatrixSection, ChallengeCard, and ChallengeSection components --- src/components/sections/challenge-section.tsx | 90 +++++++++++++++++++ .../sections/product-matrix-section.tsx | 66 ++++++++++++++ src/components/ui/challenge-card.tsx | 64 +++++++++++++ src/components/ui/product-card.tsx | 41 +++++++++ 4 files changed, 261 insertions(+) create mode 100644 src/components/sections/challenge-section.tsx create mode 100644 src/components/sections/product-matrix-section.tsx create mode 100644 src/components/ui/challenge-card.tsx create mode 100644 src/components/ui/product-card.tsx diff --git a/src/components/sections/challenge-section.tsx b/src/components/sections/challenge-section.tsx new file mode 100644 index 0000000..36e1744 --- /dev/null +++ b/src/components/sections/challenge-section.tsx @@ -0,0 +1,90 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { motion } from 'framer-motion'; +import { ChallengeCard } from '@/components/ui/challenge-card'; +import { useReducedMotion } from '@/hooks/use-reduced-motion'; + +const CHALLENGES = [ + { + id: 'data-isolation', + title: '数据孤岛', + description: '各部门系统独立运行,数据无法互通共享,导致决策信息碎片化,影响整体运营效率。', + scenario: 'isolation' as const, + href: '/solutions/data-integration', + }, + { + id: 'growth-bottleneck', + title: '增长瓶颈', + description: '业务规模扩大但管理手段滞后,流程效率低下,难以支撑持续增长的业务需求。', + scenario: 'growth' as const, + href: '/solutions/growth-enablement', + }, + { + id: 'compliance-risk', + title: '合规风险', + description: '行业监管日趋严格,传统手工操作难以满足合规要求,数据安全和审计面临挑战。', + scenario: 'compliance' as const, + href: '/solutions/compliance-management', + }, +]; + +export function ChallengeSection() { + const [isVisible, setIsVisible] = useState(false); + const sectionRef = useRef(null); + const shouldReduceMotion = useReducedMotion(); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry?.isIntersecting) { + setIsVisible(true); + } + }, + { threshold: 0.1 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + return ( +
+
+ +

+ 您的挑战,我们的使命 +

+

+ 深入理解企业数字化进程中的核心痛点,提供针对性解决方案 +

+
+ +
+ {CHALLENGES.map((challenge, index) => ( + + ))} +
+
+
+ ); +} diff --git a/src/components/sections/product-matrix-section.tsx b/src/components/sections/product-matrix-section.tsx new file mode 100644 index 0000000..ab6b71d --- /dev/null +++ b/src/components/sections/product-matrix-section.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { useEffect, useRef, useState } from 'react'; +import { motion } from 'framer-motion'; +import { ProductCard } from '@/components/ui/product-card'; +import { PRODUCTS } from '@/lib/constants'; +import { useReducedMotion } from '@/hooks/use-reduced-motion'; + +export function ProductMatrixSection() { + const [isVisible, setIsVisible] = useState(false); + const sectionRef = useRef(null); + const shouldReduceMotion = useReducedMotion(); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry?.isIntersecting) { + setIsVisible(true); + } + }, + { threshold: 0.1 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + return ( +
+
+ +

+ 产品矩阵 +

+

+ 覆盖企业数字化全场景,从管理到决策,一站式解决方案 +

+
+ +
+ {PRODUCTS.map((product, index) => ( + + ))} +
+
+
+ ); +} diff --git a/src/components/ui/challenge-card.tsx b/src/components/ui/challenge-card.tsx new file mode 100644 index 0000000..c6b4514 --- /dev/null +++ b/src/components/ui/challenge-card.tsx @@ -0,0 +1,64 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { StaticLink } from '@/components/ui/static-link'; +import { ArrowRight } from 'lucide-react'; + +interface ChallengeCardProps { + title: string; + description: string; + scenario: 'isolation' | 'growth' | 'compliance'; + href: string; + index: number; +} + +const scenarioStyles = { + isolation: { + bg: 'bg-[var(--color-challenge-isolation)]', + hoverBg: 'hover:bg-[var(--color-challenge-isolation-hover)]', + accent: 'border-l-[#C41E3A]', + icon: '🔒', + }, + growth: { + bg: 'bg-[var(--color-challenge-growth)]', + hoverBg: 'hover:bg-[var(--color-challenge-growth-hover)]', + accent: 'border-l-[#D97706]', + icon: '📈', + }, + compliance: { + bg: 'bg-[var(--color-challenge-compliance)]', + hoverBg: 'hover:bg-[var(--color-challenge-compliance-hover)]', + accent: 'border-l-[#16A34A]', + icon: '🛡️', + }, +}; + +export function ChallengeCard({ title, description, scenario, href, index }: ChallengeCardProps) { + const style = scenarioStyles[scenario]; + + return ( + + +
{style.icon}
+

+ {title} +

+

+ {description} +

+ + 了解方案 + + +
+
+ ); +} diff --git a/src/components/ui/product-card.tsx b/src/components/ui/product-card.tsx new file mode 100644 index 0000000..af41e87 --- /dev/null +++ b/src/components/ui/product-card.tsx @@ -0,0 +1,41 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { StaticLink } from '@/components/ui/static-link'; +import { ArrowUpRight } from 'lucide-react'; + +interface ProductCardProps { + title: string; + description: string; + href: string; + index: number; +} + +export function ProductCard({ title, description, href, index }: ProductCardProps) { + return ( + + +
+ + {String(index + 1).padStart(2, '0')} + + +
+

+ {title} +

+

+ {description} +

+
+
+ ); +}