From 724a00f582ad92ef32794219aaad18617dbe6cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Sun, 7 Jun 2026 16:19:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20V2/V3=E7=BB=84=E4=BB=B6=E4=BD=93?= =?UTF-8?q?=E7=B3=BB=E4=B8=8E=E6=B0=B4=E5=A2=A8=E9=9B=85=E8=87=B4=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增detail-v2组件库(HeroV2/V3, TrustSectionV2, CTASectionV2等) - Hero组件水墨雅致改造:浅色宣纸底/深色墨色文字/墨韵纹理 - 方案卡片添加推荐组合徽标(Package图标) - 独立产品页从V1组件迁移到V2/V3 - 修复why-us-section未使用导入和ESLint引号转义错误 --- src/components/detail-v2/brand-elements.tsx | 188 ++++++++ .../detail-v2/detail-cta-section-v2.tsx | 98 ++++ src/components/detail-v2/detail-hero-v2.tsx | 182 ++++++++ src/components/detail-v2/detail-hero-v3.tsx | 318 +++++++++++++ .../detail-v2/detail-product-value-v2.tsx | 171 +++++++ .../detail-v2/detail-product-value-v3.tsx | 435 ++++++++++++++++++ .../detail-v2/detail-trust-section-v2.tsx | 180 ++++++++ .../detail-v2/list-page-hero-v3.tsx | 127 +++++ .../detail-v2/micro-interactions.tsx | 139 ++++++ src/components/detail-v2/product-card-v3.tsx | 87 ++++ src/components/detail-v2/service-value-v3.tsx | 191 ++++++++ .../detail-v2/solution-service-card-v3.tsx | 163 +++++++ .../detail-v2/solution-value-v3.tsx | 223 +++++++++ src/components/detail/detail-case-study.tsx | 38 ++ .../detail/detail-certification.tsx | 36 ++ .../detail/detail-cross-recommend.tsx | 103 +++++ src/components/detail/detail-cta-section.tsx | 60 +++ src/components/detail/detail-data-proof.tsx | 35 ++ src/components/detail/detail-hero.tsx | 173 +++++++ .../detail/detail-product-value.tsx | 91 ++++ .../detail/detail-service-value.tsx | 128 ++++++ .../detail/detail-solution-value.tsx | 158 +++++++ .../detail/detail-trust-section.tsx | 71 +++ src/components/layout/footer.tsx | 4 +- src/components/layout/header.tsx | 55 +-- src/components/layout/mega-dropdown.tsx | 123 +++-- src/components/layout/mobile-menu.tsx | 2 +- src/components/sections/cta-section.tsx | 34 +- .../sections/product-matrix-section.tsx | 14 +- src/components/sections/why-us-section.tsx | 119 +++++ src/components/ui/product-card.tsx | 11 +- src/components/ui/scroll-progress.tsx | 20 + 32 files changed, 3690 insertions(+), 87 deletions(-) create mode 100644 src/components/detail-v2/brand-elements.tsx create mode 100644 src/components/detail-v2/detail-cta-section-v2.tsx create mode 100644 src/components/detail-v2/detail-hero-v2.tsx create mode 100644 src/components/detail-v2/detail-hero-v3.tsx create mode 100644 src/components/detail-v2/detail-product-value-v2.tsx create mode 100644 src/components/detail-v2/detail-product-value-v3.tsx create mode 100644 src/components/detail-v2/detail-trust-section-v2.tsx create mode 100644 src/components/detail-v2/list-page-hero-v3.tsx create mode 100644 src/components/detail-v2/micro-interactions.tsx create mode 100644 src/components/detail-v2/product-card-v3.tsx create mode 100644 src/components/detail-v2/service-value-v3.tsx create mode 100644 src/components/detail-v2/solution-service-card-v3.tsx create mode 100644 src/components/detail-v2/solution-value-v3.tsx create mode 100644 src/components/detail/detail-case-study.tsx create mode 100644 src/components/detail/detail-certification.tsx create mode 100644 src/components/detail/detail-cross-recommend.tsx create mode 100644 src/components/detail/detail-cta-section.tsx create mode 100644 src/components/detail/detail-data-proof.tsx create mode 100644 src/components/detail/detail-hero.tsx create mode 100644 src/components/detail/detail-product-value.tsx create mode 100644 src/components/detail/detail-service-value.tsx create mode 100644 src/components/detail/detail-solution-value.tsx create mode 100644 src/components/detail/detail-trust-section.tsx create mode 100644 src/components/sections/why-us-section.tsx diff --git a/src/components/detail-v2/brand-elements.tsx b/src/components/detail-v2/brand-elements.tsx new file mode 100644 index 0000000..ab963e3 --- /dev/null +++ b/src/components/detail-v2/brand-elements.tsx @@ -0,0 +1,188 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { type LucideIcon } from 'lucide-react'; +import type { ReactNode } from 'react'; + +interface BrandSealProps { + text?: string; + variant?: 'red' | 'gold' | 'blue'; + size?: 'sm' | 'md' | 'lg'; +} + +export function BrandSeal({ text = '旗舰', variant = 'red', size = 'md' }: BrandSealProps) { + const sizes = { + sm: 'w-14 h-14 text-xs', + md: 'w-20 h-20 text-sm', + lg: 'w-28 h-28 text-base', + }; + + const colors = { + red: { + border: '#C41E3A', + bg: 'rgba(196, 30, 58, 0.06)', + text: '#C41E3A', + shadow: 'rgba(196, 30, 58, 0.2)', + }, + gold: { + border: '#d97706', + bg: 'rgba(217, 119, 6, 0.06)', + text: '#d97706', + shadow: 'rgba(217, 119, 6, 0.2)', + }, + blue: { + border: '#2563eb', + bg: 'rgba(37, 99, 235, 0.06)', + text: '#2563eb', + shadow: 'rgba(37, 99, 235, 0.2)', + }, + }; + + const color = colors[variant]; + + return ( + +
+ + {text} + +
+ + ); +} + +interface CalligraphyTextProps { + children: ReactNode; + className?: string; + as?: 'h1' | 'h2' | 'h3' | 'span'; +} + +export function CalligraphyText({ + children, + className = '', + as: Tag = 'span', +}: CalligraphyTextProps) { + return ( + + {children} + + ); +} + +interface InkDividerProps { + variant?: 'subtle' | 'bold' | 'decorative'; +} + +export function InkDivider({ variant = 'subtle' }: InkDividerProps) { + if (variant === 'subtle') { + return ( +
+
+
+
+
+ ); + } + + if (variant === 'bold') { + return ( +
+
+ +
+
+ ); + } + + return ( +
+ + + + + + +
+ ); +} + +interface SectionHeaderProps { + badge?: string; + title: ReactNode; + subtitle?: string; + align?: 'left' | 'center'; + icon?: LucideIcon; +} + +export function SectionHeader({ + badge, + title, + subtitle, + align = 'center', + icon: Icon, +}: SectionHeaderProps) { + return ( + + {(badge || Icon) && ( +
+ {Icon && } + {badge} +
+ )} + +

+ {title} +

+ + {subtitle && ( +

{subtitle}

+ )} +
+ ); +} diff --git a/src/components/detail-v2/detail-cta-section-v2.tsx b/src/components/detail-v2/detail-cta-section-v2.tsx new file mode 100644 index 0000000..2b213ea --- /dev/null +++ b/src/components/detail-v2/detail-cta-section-v2.tsx @@ -0,0 +1,98 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { ArrowRight, MessageCircle, Download } from 'lucide-react'; +import type { HeroTheme } from '@/lib/constants/hero-themes'; +import { DESIGN_SYSTEM } from '@/lib/constants/design-system'; + +interface DetailCTASectionV2Props { + theme: HeroTheme; + title?: string; + description?: string; + primaryAction: { label: string; href: string }; + secondaryAction?: { label: string; href: string }; +} + +export function DetailCTASectionV2({ + theme, + title = '准备好开始了吗?', + description = '联系我们,获取专属方案和报价', + primaryAction, + secondaryAction, +}: DetailCTASectionV2Props) { + return ( +
+
+
+
+
+
+ +
+ +
+ + 免费咨询 +
+ +

+ {title} +

+ +

+ {description} +

+ +
+ + {primaryAction.label} + +
+ + + {secondaryAction && ( + + + {secondaryAction.label} + + )} +
+ + + 平均响应时间 < 2小时 · 7×24小时技术支持 · 无需预付费用 + + +
+
+ ); +} diff --git a/src/components/detail-v2/detail-hero-v2.tsx b/src/components/detail-v2/detail-hero-v2.tsx new file mode 100644 index 0000000..58c122a --- /dev/null +++ b/src/components/detail-v2/detail-hero-v2.tsx @@ -0,0 +1,182 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { ArrowRight, Sparkles } from 'lucide-react'; +import type { HeroTheme, HeroTexture } from '@/lib/constants/hero-themes'; +import { DESIGN_SYSTEM } from '@/lib/constants/design-system'; + +interface DetailHeroV2Props { + theme: HeroTheme; + badge?: string; + title: string; + subtitle: string; + description?: string; + primaryAction?: { label: string; href: string }; + secondaryAction?: { label: string; href: string }; +} + +function TextureOverlay({ texture }: { texture: HeroTexture }) { + if (texture.type === 'none') return null; + + const textureStyles: Record = { + grid: { + backgroundImage: + 'linear-gradient(rgba(0,0,0,.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,.03) 1px, transparent 1px)', + backgroundSize: '60px 60px', + }, + 'data-flow': { + backgroundImage: + 'radial-gradient(circle at 20% 50%, rgba(0,0,0,.03) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(0,0,0,.02) 0%, transparent 40%)', + }, + layers: { + backgroundImage: + 'linear-gradient(180deg, transparent 0%, rgba(0,0,0,.01) 50%, transparent 100%)', + }, + 'shield-hex': { + backgroundImage: + 'repeating-linear-gradient(60deg, transparent, transparent 30px, rgba(0,0,0,.02) 30px, rgba(0,0,0,.02) 31px)', + }, + circuit: { + backgroundImage: + 'linear-gradient(90deg, transparent 49%, rgba(0,0,0,.02) 49%, rgba(0,0,0,.02) 51%, transparent 51%)', + backgroundSize: '40px 40px', + }, + }; + + return ( +
+ ); +} + +function InkWashBackground() { + return ( + <> +
+
+
+ + ); +} + +export function DetailHeroV2({ + theme, + badge, + title, + subtitle, + description, + primaryAction, + secondaryAction, +}: DetailHeroV2Props) { + const isCenterLayout = theme.layout === 'center' || theme.layout === 'wide'; + + const containerClasses = theme.layout === 'wide' + ? 'container-wide py-32 lg:py-40' + : 'container-wide py-28 lg:py-36'; + + return ( +
+ + + +
+ + {(badge || theme.badge) && ( + + + {badge || theme.badge} + + )} + + + {title} + + + + {subtitle} + + + {description && ( + + {description} + + )} + + {(primaryAction || secondaryAction) && ( + + {primaryAction && ( + + {primaryAction.label} + +
+ + )} + {secondaryAction && ( + + {secondaryAction.label} + + )} + + )} + +
+ +
+
+ ); +} diff --git a/src/components/detail-v2/detail-hero-v3.tsx b/src/components/detail-v2/detail-hero-v3.tsx new file mode 100644 index 0000000..b91f721 --- /dev/null +++ b/src/components/detail-v2/detail-hero-v3.tsx @@ -0,0 +1,318 @@ +'use client'; + +import { motion, useScroll, useTransform } from 'framer-motion'; +import { useRef } from 'react'; +import { ArrowRight, Sparkles, ChevronDown } from 'lucide-react'; +import type { HeroTheme, HeroTexture } from '@/lib/constants/hero-themes'; +import { PressableButton } from './micro-interactions'; + +interface DetailHeroV3Props { + theme: HeroTheme; + badge?: string; + title: string; + subtitle: string; + description?: string; + primaryAction?: { label: string; href: string }; + secondaryAction?: { label: string; href: string }; +} + +function InkWashBackgroundV3({ theme }: { theme: HeroTheme }) { + const ref = useRef(null); + const { scrollYProgress } = useScroll({ + target: ref, + offset: ['start start', 'end start'], + }); + const opacity = useTransform(scrollYProgress, [0, 0.8], [1, 0.3]); + const scale = useTransform(scrollYProgress, [0, 0.8], [1, 1.1]); + + return ( + + {/* 水墨雅致浅色渐变底 */} +
+ + {/* 墨韵光晕 — 品牌色淡染 */} +
+
+
+ + {/* 宣纸纹理 */} +
+ + {theme.texture.type !== 'none' && ( + + )} + + {/* 底部渐变融入页面背景 */} +
+ + ); +} + +function TextureOverlayV3({ texture }: { texture: HeroTexture }) { + if (texture.type === 'none') return null; + + const textures: Record = { + grid: { + backgroundImage: + 'linear-gradient(rgba(0,0,0,.03) 1px, transparent 1px), linear-gradient(90deg, rgba(0,0,0,.03) 1px, transparent 1px)', + backgroundSize: '80px 80px', + }, + 'data-flow': { + backgroundImage: + 'radial-gradient(circle at 15% 45%, rgba(0,0,0,.03) 0%, transparent 55%), radial-gradient(circle at 85% 25%, rgba(0,0,0,.02) 0%, transparent 45%)', + }, + layers: { + backgroundImage: + 'linear-gradient(180deg, transparent 0%, rgba(0,0,0,.01) 35%, rgba(0,0,0,.02) 65%, transparent 100%)', + }, + 'shield-hex': { + backgroundImage: + 'repeating-linear-gradient(55deg, transparent, transparent 28px, rgba(0,0,0,.02) 28px, rgba(0,0,0,.02) 29px)', + }, + circuit: { + backgroundImage: + 'linear-gradient(90deg, transparent 49.5%, rgba(0,0,0,.02) 49.5%, rgba(0,0,0,.02) 50.5%, transparent 50.5%)', + backgroundSize: '50px 50px', + }, + }; + + return ( +
+ ); +} + +function FloatingParticles() { + const particles = [ + { x: 15, y: 25, scale: 0.7, duration: 8, delay: 0, startY: 100, endY: -20 }, + { x: 75, y: 60, scale: 0.5, duration: 10, delay: 1.5, startY: 80, endY: 15 }, + { x: 45, y: 35, scale: 0.9, duration: 9, delay: 0.8, startY: 90, endY: -10 }, + { x: 85, y: 20, scale: 0.6, duration: 11, delay: 2.2, startY: 70, endY: 5 }, + { x: 25, y: 75, scale: 0.8, duration: 7.5, delay: 1.0, startY: 85, endY: -15 }, + { x: 65, y: 45, scale: 0.55, duration: 12, delay: 2.8, startY: 95, endY: 10 }, + ]; + + return ( +
+ {particles.map((particle, i) => ( + + ))} +
+ ); +} + +export function DetailHeroV3({ + theme, + badge, + title, + subtitle, + description, + primaryAction, + secondaryAction, +}: DetailHeroV3Props) { + const isCenterLayout = theme.layout === 'center' || theme.layout === 'wide'; + + return ( +
+ + + +
+ + {(badge || theme.badge) && ( + + + + + {badge || theme.badge} + + )} + + + {title} + + + + {subtitle} + + + {description && ( + + {description} + + )} + + {(primaryAction || secondaryAction) && ( + + {primaryAction && ( + + + {primaryAction.label} + +
+ + + )} + {secondaryAction && ( + + + {secondaryAction.label} + + + )} + + )} + + + + + + + +
+
+ ); +} diff --git a/src/components/detail-v2/detail-product-value-v2.tsx b/src/components/detail-v2/detail-product-value-v2.tsx new file mode 100644 index 0000000..82537f4 --- /dev/null +++ b/src/components/detail-v2/detail-product-value-v2.tsx @@ -0,0 +1,171 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { + CheckCircle2, + ArrowRight, + Zap, + Shield, + BarChart3, + Cpu, + type LucideIcon, +} from 'lucide-react'; +import { DESIGN_SYSTEM } from '@/lib/constants/design-system'; +import type { Product } from '@/lib/constants/products'; + +interface ProductValueSectionV2Props { + product: Product; +} + +const featureIcons: LucideIcon[] = [Zap, Shield, BarChart3, Cpu]; + +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: DESIGN_SYSTEM.animation.delay.stagger, + delayChildren: 0.1, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.4, + ease: [0.4, 0, 0.2, 1] as const, + }, + }, +}; + +export function ProductValueSectionV2({ product }: ProductValueSectionV2Props) { + return ( +
+
+ +
+ + + 核心能力 + +

+ 为企业打造的{product.title} +

+

+ {product.overview} +

+
+ +
+ +
+
+ +
+

功能模块

+
+ +
+ {product.features.map((feature, index) => { + const Icon = featureIcons[index % featureIcons.length]!; + const [title, ...descParts] = feature.split(':'); + const desc = descParts.join(':'); + + return ( + +
+
+ +
+
+

{title}

+ {desc &&

{desc}

} +
+
+
+ ); + })} +
+
+ + +
+
+ +
+

核心优势

+
+ +
+ {product.benefits.map((benefit, index) => ( + + +

{benefit}

+
+ ))} +
+ +
+

+ + 技术规格 +

+
    + {product.specs.slice(0, 4).map((spec, index) => ( + + + {spec} + + ))} +
+
+
+
+
+
+ ); +} diff --git a/src/components/detail-v2/detail-product-value-v3.tsx b/src/components/detail-v2/detail-product-value-v3.tsx new file mode 100644 index 0000000..ef8fb5e --- /dev/null +++ b/src/components/detail-v2/detail-product-value-v3.tsx @@ -0,0 +1,435 @@ +'use client'; + +import { motion, useInView } from 'framer-motion'; +import { useRef, useState } from 'react'; +import { + CheckCircle2, + ArrowRight, + Zap, + Shield, + BarChart3, + Cpu, + TrendingUp, + Clock, + Award, + Users, + type LucideIcon, +} from 'lucide-react'; +import { TiltCard, InkGlowCard } from './micro-interactions'; +import type { Product } from '@/lib/constants/products'; + +interface ProductValueSectionV3Props { + product: Product; +} + +const featureIcons: LucideIcon[] = [Zap, Shield, BarChart3, Cpu]; + +function FeatureTags({ text }: { text: string }) { + const [title, ...descParts] = text.split(':'); + const desc = descParts.join(':'); + + if (!desc) return {title}; + + const tags = desc.split('、').map((tag) => tag.trim()).filter(Boolean); + + return ( +
+

{title}

+
+ {tags.map((tag, i) => ( + + {tag} + + ))} +
+
+ ); +} + +function CircularProgress({ value, label, color = '#C41E3A' }: { value: number; label: string; color?: string }) { + const ref = useRef(null); + const isInView = useInView(ref, { once: true }); + const [progress, setProgress] = useState(0); + + if (isInView && progress === 0) { + setTimeout(() => setProgress(value), 300); + } + + const circumference = 2 * Math.PI * 45; + const strokeDashoffset = circumference - (progress / 100) * circumference; + + return ( +
+
+ + + + +
+ {value}% +
+
+

{label}

+
+ ); +} + +function MetricCard({ + icon: Icon, + value, + metric, + description, + index: _index, + accent = false, +}: { + icon: LucideIcon; + value: string; + metric: string; + description: string; + index: number; + accent?: boolean; +}) { + const ref = useRef(null); + const isInView = useInView(ref, { once: true }); + + const numericValue = parseFloat(value.replace(/[^0-9.]/g, '')); + const suffix = value.replace(/[0-9.]/g, ''); + const [displayValue, setDisplayValue] = useState('0'); + + if (isInView && displayValue === '0') { + let startTime: number; + const animate = (currentTime: number) => { + if (!startTime) startTime = currentTime; + const progress = Math.min((currentTime - startTime) / 2000, 1); + const easeOutQuart = 1 - Math.pow(1 - progress, 4); + setDisplayValue( + Number.isInteger(numericValue) + ? Math.floor(numericValue * easeOutQuart).toString() + suffix + : (numericValue * easeOutQuart).toFixed(1) + suffix + ); + if (progress < 1) requestAnimationFrame(animate); + }; + requestAnimationFrame(animate); + } + + return ( + + {accent && ( +
+ )} + +
+
+ +
+ +
+ {displayValue} +
+ +

{metric}

+

{description}

+
+ + ); +} + +export function ProductValueSectionV3({ product }: ProductValueSectionV3Props) { + return ( +
+
+ +
+ + + + 核心能力 + + +

+ 为企业打造的 + {product.title} + + +

+ +

+ {product.overview} +

+
+ +
+ +
+
+ +
+
+

功能模块

+

六大核心业务领域全覆盖

+
+
+ +
+ {product.features.map((feature, index) => { + const Icon = featureIcons[index % featureIcons.length]!; + + return ( + + +
+
+ +
+
+ +
+
+
+
+ ); + })} +
+
+ + +
+
+ +
+
+

核心优势

+

为什么选择我们

+
+
+ +
+ {product.benefits.map((benefit, index) => ( + +
+ +

{benefit}

+
+
+ ))} +
+ + +

+ + 技术规格 +

+
    + {product.specs.slice(0, 5).map((spec, index) => ( + + + {spec} + + ))} +
+
+
+
+ + {product.dataProofs && product.dataProofs.length > 0 && ( + +
+ + 数据说话 + +

+ 用真实效果证明价值 +

+
+ +
+ {product.dataProofs.map((proof, index) => { + const icons: LucideIcon[] = [TrendingUp, Clock, Award, Users]; + const Icon = icons[index % icons.length]!; + + return ( + + ); + })} +
+ + + + + + +
+ )} +
+
+ ); +} diff --git a/src/components/detail-v2/detail-trust-section-v2.tsx b/src/components/detail-v2/detail-trust-section-v2.tsx new file mode 100644 index 0000000..91c1d91 --- /dev/null +++ b/src/components/detail-v2/detail-trust-section-v2.tsx @@ -0,0 +1,180 @@ +'use client'; + +import { motion, useInView } from 'framer-motion'; +import { useRef, useEffect, useState } from 'react'; +import { CaseStudyCard } from '../detail/detail-case-study'; +import { CertificationList } from '../detail/detail-certification'; +import type { CaseStudy, DataProof, Certification } from '@/lib/constants/products'; +import { DESIGN_SYSTEM } from '@/lib/constants/design-system'; + +interface DetailTrustSectionV2Props { + caseStudies?: CaseStudy[]; + dataProofs?: DataProof[]; + certifications?: Certification[]; + accentColor?: string; +} + +function AnimatedCounter({ value, duration = 2000 }: { value: string; duration?: number }) { + const [displayValue, setDisplayValue] = useState('0'); + const ref = useRef(null); + const isInView = useInView(ref, { once: true }); + + useEffect(() => { + if (!isInView) return; + + const numericValue = parseFloat(value.replace(/[^0-9.]/g, '')); + const suffix = value.replace(/[0-9.]/g, ''); + let startTime: number; + let animationFrame: number; + + const animate = (currentTime: number) => { + if (!startTime) startTime = currentTime; + const progress = Math.min((currentTime - startTime) / duration, 1); + const easeOutQuart = 1 - Math.pow(1 - progress, 4); + const currentValue = numericValue * easeOutQuart; + + setDisplayValue( + Number.isInteger(numericValue) + ? Math.floor(currentValue).toString() + suffix + : currentValue.toFixed(1) + suffix + ); + + if (progress < 1) { + animationFrame = requestAnimationFrame(animate); + } + }; + + animationFrame = requestAnimationFrame(animate); + + return () => cancelAnimationFrame(animationFrame); + }, [isInView, value, duration]); + + return ( +
+ {displayValue} +
+ ); +} + +export function DetailTrustSectionV2({ + caseStudies = [], + dataProofs = [], + certifications = [], + accentColor = '#C41E3A', +}: DetailTrustSectionV2Props) { + const hasContent = caseStudies.length > 0 || dataProofs.length > 0 || certifications.length > 0; + if (!hasContent) return null; + + return ( +
+
+ +
+ {dataProofs.length > 0 && ( +
+ + + 数据说话 + +

+ 用真实效果证明价值 +

+

+ 来自真实客户的使用数据,让效果可衡量、可验证 +

+
+ +
+ {dataProofs.map((proof, index) => ( + + {index === 0 && ( +
+ )} + +

{proof.metric}

+

{proof.description}

+ + ))} +
+
+ )} + + {caseStudies.length > 0 && ( +
+ + + 客户案例 + +

+ 他们已经实现了转型突破 +

+
+ +
+ {caseStudies.map((study, index) => ( + + + + ))} +
+
+ )} + + {certifications.length > 0 && ( + +

资质认证

+ +
+ )} +
+
+ ); +} diff --git a/src/components/detail-v2/list-page-hero-v3.tsx b/src/components/detail-v2/list-page-hero-v3.tsx new file mode 100644 index 0000000..699c43b --- /dev/null +++ b/src/components/detail-v2/list-page-hero-v3.tsx @@ -0,0 +1,127 @@ +'use client'; + +import { motion } from 'framer-motion'; + +interface ListPageHeroV3Props { + title: string; + subtitle: string; + description?: string; + stats?: Array<{ value: string; label: string }>; +} + +export function ListPageHeroV3({ + title, + subtitle, + description, + stats, +}: ListPageHeroV3Props) { + return ( +
+
+
+
+
+ +
+ + + + 企业级解决方案 + + + + {title} + + + + {subtitle} + + + {description && ( + + {description} + + )} + + {stats && stats.length > 0 && ( + + {stats.map((stat, index) => ( + +
+ {stat.value} +
+
{stat.label}
+
+ ))} +
+ )} +
+
+
+ ); +} diff --git a/src/components/detail-v2/micro-interactions.tsx b/src/components/detail-v2/micro-interactions.tsx new file mode 100644 index 0000000..88db9a2 --- /dev/null +++ b/src/components/detail-v2/micro-interactions.tsx @@ -0,0 +1,139 @@ +'use client'; + +import { motion, useMotionValue, useTransform, useSpring } from 'framer-motion'; +import { useRef, ReactNode } from 'react'; + +interface TiltCardProps { + children: ReactNode; + className?: string; + tiltAmount?: number; + glareEnable?: boolean; + glareMaxOpacity?: number; +} + +export function TiltCard({ + children, + className = '', + tiltAmount = 10, + glareEnable = true, + glareMaxOpacity = 0.15, +}: TiltCardProps) { + const ref = useRef(null); + const x = useMotionValue(0.5); + const y = useMotionValue(0.5); + + const rotateX = useSpring(useTransform(y, [0, 1], [tiltAmount, -tiltAmount]), { + stiffness: 300, + damping: 30, + }); + const rotateY = useSpring(useTransform(x, [0, 1], [-tiltAmount, tiltAmount]), { + stiffness: 300, + damping: 30, + }); + + const handleMouseMove = (e: React.MouseEvent) => { + if (!ref.current) return; + const rect = ref.current.getBoundingClientRect(); + x.set((e.clientX - rect.left) / rect.width); + y.set((e.clientY - rect.top) / rect.height); + }; + + const handleMouseLeave = () => { + x.set(0.5); + y.set(0.5); + }; + + return ( + + {children} + {glareEnable && ( +
+ )} + + ); +} + +interface PressableButtonProps { + children: ReactNode; + className?: string; + onClick?: () => void; + variant?: 'primary' | 'secondary' | 'ghost'; +} + +export function PressableButton({ + children, + className = '', + onClick, + variant = 'primary', +}: PressableButtonProps) { + const baseStyles = { + primary: + 'bg-gradient-to-r from-[#C41E3A] to-[#99182d] text-white shadow-lg shadow-red-500/25 hover:shadow-xl hover:shadow-red-500/40', + secondary: + 'bg-white text-gray-700 border-2 border-gray-200 hover:border-gray-300 shadow-md hover:shadow-lg', + ghost: + 'bg-transparent text-[#C41E3A] hover:bg-red-50', + }; + + return ( + + {children} + + ); +} + +interface InkGlowCardProps { + children: ReactNode; + className?: string; + active?: boolean; +} + +export function InkGlowCard({ children, className = '', active = false }: InkGlowCardProps) { + return ( + + {active && ( +
+ )} +
+
+
+
{children}
+ + ); +} diff --git a/src/components/detail-v2/product-card-v3.tsx b/src/components/detail-v2/product-card-v3.tsx new file mode 100644 index 0000000..3bd0477 --- /dev/null +++ b/src/components/detail-v2/product-card-v3.tsx @@ -0,0 +1,87 @@ +'use client'; + +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import Image from 'next/image'; +import { ArrowRight, CheckCircle2 } from 'lucide-react'; + +interface ProductCardV3Props { + product: { + id: string; + title: string; + description: string; + image: string; + category: string; + status: string; + features?: string[]; + }; +} + +export function ProductCardV3({ product }: ProductCardV3Props) { + return ( + + +
+
+ {product.title} +
+ +
+ + {product.category} + + {product.status === '已发布' && ( + + + 已发布 + + )} +
+
+ +
+

+ {product.title} +

+ +

+ {product.description} +

+ + {product.features && product.features.length > 0 && ( +
+ {product.features.slice(0, 3).map((feature) => ( + + {feature} + + ))} +
+ )} + +
+ 了解详情 + +
+
+
+ + + ); +} diff --git a/src/components/detail-v2/service-value-v3.tsx b/src/components/detail-v2/service-value-v3.tsx new file mode 100644 index 0000000..9dd64da --- /dev/null +++ b/src/components/detail-v2/service-value-v3.tsx @@ -0,0 +1,191 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { + CheckCircle2, + Zap, + Clock, + Users, + Award, + TrendingUp, + type LucideIcon, +} from 'lucide-react'; +import { TiltCard, InkGlowCard } from './micro-interactions'; +import { SectionHeader, InkDivider } from './brand-elements'; +import type { Service } from '@/lib/constants/services'; + +interface ServiceValueSectionV3Props { + service: Service; +} + +export function ServiceValueSectionV3({ service }: ServiceValueSectionV3Props) { + const icons: LucideIcon[] = [Zap, Clock, Users, Award, TrendingUp]; + + return ( +
+
+ +
+ + +
+
+
+ +
+
+

核心能力

+

我们提供的服务特色

+
+
+ +
+ {service.features.map((feature, index) => { + const Icon = icons[index % icons.length]!; + + return ( + + +
+
+ +
+

+ {feature.split(':')[1] || feature} +

+
+
+
+ ); + })} +
+
+ + + + +
+
+ +
+
+

服务优势

+

选择我们的理由

+
+
+ +
+ {service.benefits.map((benefit, index) => ( + +
+ +

{benefit}

+
+
+ ))} +
+
+ + {service.process && service.process.length > 0 && ( + + + +
+

+ 服务流程 +

+

标准化流程保障服务质量

+
+ +
+
+ +
+ {service.process.map((step, index) => ( + +
+
+ {String(index + 1).padStart(2, '0')} +
+
+ + +

+ {step.split(':')[0]} +

+

+ {step.split(':')[1] || step} +

+
+
+ ))} +
+
+ + )} +
+
+ ); +} diff --git a/src/components/detail-v2/solution-service-card-v3.tsx b/src/components/detail-v2/solution-service-card-v3.tsx new file mode 100644 index 0000000..9235eec --- /dev/null +++ b/src/components/detail-v2/solution-service-card-v3.tsx @@ -0,0 +1,163 @@ +'use client'; + +import { motion } from 'framer-motion'; +import Link from 'next/link'; +import Image from 'next/image'; +import { ArrowRight, Building2, Users, Cpu, Package } from 'lucide-react'; + +interface SolutionCardV3Props { + solution: { + id: string; + title: string; + industry: string; + description: string; + image?: string; + challenges?: string[]; + suiteCombination?: { + primaryProducts: string[]; + complementaryServices: string[]; + rationale: string; + }; + }; +} + +export function SolutionCardV3({ solution }: SolutionCardV3Props) { + const industryIcons = { + 制造业: Building2, + 零售: Users, + 金融: Cpu, + 政务: Building2, + }; + + const Icon = industryIcons[solution.industry as keyof typeof industryIcons] || Building2; + + return ( + + +
+
+ {solution.image ? ( + {solution.title} + ) : ( + + )} +
+ +
+ + + {solution.industry} + + {solution.suiteCombination && solution.suiteCombination.primaryProducts.length > 0 && ( + + + {solution.suiteCombination.primaryProducts.length} 产品组合 + + )} +
+
+ +
+

+ {solution.title} +

+ +

+ {solution.description} +

+ + {solution.challenges && solution.challenges.length > 0 && ( +
+ {solution.challenges.slice(0, 2).map((challenge) => ( +
+ + {challenge} +
+ ))} +
+ )} + +
+ 查看方案 + +
+
+
+ + + ); +} + +interface ServiceCardV3Props { + service: { + id: string; + title: string; + description: string; + features?: string[]; + }; +} + +export function ServiceCardV3({ service }: ServiceCardV3Props) { + return ( + + +
+
+
+ +
+ +

+ {service.title} +

+ +

+ {service.description} +

+ + {service.features && service.features.length > 0 && ( +
+ {service.features.slice(0, 3).map((feature) => ( + + {feature.split(':')[0]} + + ))} +
+ )} + +
+ 了解服务 + +
+
+
+ +
+ ); +} diff --git a/src/components/detail-v2/solution-value-v3.tsx b/src/components/detail-v2/solution-value-v3.tsx new file mode 100644 index 0000000..0ad5970 --- /dev/null +++ b/src/components/detail-v2/solution-value-v3.tsx @@ -0,0 +1,223 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { + CheckCircle2, + Lightbulb, + Target, + TrendingUp, +} from 'lucide-react'; +import { TiltCard } from './micro-interactions'; +import { SectionHeader, InkDivider } from './brand-elements'; +import type { Solution } from '@/lib/constants/solutions'; + +interface SolutionValueSectionV3Props { + solution: Solution; +} + +export function SolutionValueSectionV3({ solution }: SolutionValueSectionV3Props) { + return ( +
+
+ +
+ + 为{solution.industry}量身定制 + + } + subtitle={solution.description} + icon={Lightbulb} + /> + +
+ +
+

+ + + + 行业痛点挑战 +

+ +
    + {solution.challenges.map((challenge, index) => ( + + + {challenge} + + ))} +
+
+
+ + +
+

+ + + + Novalon解决方案 +

+ +
    + {solution.solutions.map((solutionItem, index) => ( + + + {solutionItem} + + ))} +
+
+
+
+ + {solution.valueProposition && ( + + + +
+

+ {solution.valueProposition.headline} +

+
+ +
+ {solution.valueProposition.points.map((point, index) => ( + +
+ +
+

{point.title}

+

{point.description}

+
+ ))} +
+
+ )} + + {solution.suiteCombination && ( + + + +
+
+
+

+ + + + 核心产品组合 +

+
+ {solution.suiteCombination.primaryProducts.map((product, index) => ( + + {product} + + ))} +
+
+ +
+

+ + + + 配套专业服务 +

+
+ {solution.suiteCombination.complementaryServices.map((service, index) => ( + + {service} + + ))} +
+
+
+ + + 💡 组合逻辑:{solution.suiteCombination.rationale} + +
+
+ )} +
+
+ ); +} diff --git a/src/components/detail/detail-case-study.tsx b/src/components/detail/detail-case-study.tsx new file mode 100644 index 0000000..37afa76 --- /dev/null +++ b/src/components/detail/detail-case-study.tsx @@ -0,0 +1,38 @@ +'use client'; + +import { motion } from 'framer-motion'; +import type { CaseStudy } from '@/lib/constants/products'; + +interface CaseStudyCardProps { + study: CaseStudy; + index: number; +} + +export function CaseStudyCard({ study, index }: CaseStudyCardProps) { + return ( + +
+ + {study.industry} + + {study.client} +
+ +

挑战

+

{study.challenge}

+ +

解决方案

+

{study.solution}

+ +
+

{study.result}

+
+
+ ); +} diff --git a/src/components/detail/detail-certification.tsx b/src/components/detail/detail-certification.tsx new file mode 100644 index 0000000..507dcfa --- /dev/null +++ b/src/components/detail/detail-certification.tsx @@ -0,0 +1,36 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Award, ExternalLink } from 'lucide-react'; +import type { Certification } from '@/lib/constants/products'; + +interface CertificationListProps { + certifications: Certification[]; +} + +export function CertificationList({ certifications }: CertificationListProps) { + if (certifications.length === 0) return null; + + return ( +
+ {certifications.map((cert, index) => ( + + + {cert.name} + {cert.issuer} + {cert.link && cert.link !== '#' && ( + + )} + + ))} +
+ ); +} diff --git a/src/components/detail/detail-cross-recommend.tsx b/src/components/detail/detail-cross-recommend.tsx new file mode 100644 index 0000000..3b896e3 --- /dev/null +++ b/src/components/detail/detail-cross-recommend.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { ArrowRight, Package, Lightbulb, Wrench } from 'lucide-react'; +import type { CrossReference } from '@/lib/constants/cross-references'; + +interface CrossRecommendGridProps { + items: CrossReference[]; + title?: string; +} + +const typeConfig = { + product: { + icon: Package, + label: '产品', + color: '#3b82f6', + bg: '#eff6ff', + }, + solution: { + icon: Lightbulb, + label: '方案', + color: '#C41E3A', + bg: '#fef2f2', + }, + service: { + icon: Wrench, + label: '服务', + color: '#7c3aed', + bg: '#f5f3ff', + }, +}; + +export function CrossRecommendGrid({ items, title = '相关推荐' }: CrossRecommendGridProps) { + if (items.length === 0) return null; + + return ( +
+
+ + {title} + + + 探索更多可能,发现与您需求相关的产品、方案和服务 + + +
+ {items.map((item, index) => { + const config = typeConfig[item.type]; + const Icon = config.icon; + + return ( + +
+ +
+
+
+ + {config.label} + + +
+

{item.title}

+

+ {item.reason} +

+
+
+ ); + })} +
+
+
+ ); +} diff --git a/src/components/detail/detail-cta-section.tsx b/src/components/detail/detail-cta-section.tsx new file mode 100644 index 0000000..9e4a310 --- /dev/null +++ b/src/components/detail/detail-cta-section.tsx @@ -0,0 +1,60 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { ArrowRight } from 'lucide-react'; +import type { HeroTheme } from '@/lib/constants/hero-themes'; + +interface DetailCTASectionProps { + theme: HeroTheme; + title?: string; + description?: string; + primaryAction: { label: string; href: string }; + secondaryAction?: { label: string; href: string }; +} + +export function DetailCTASection({ + theme, + title = '准备好开始了吗?', + description = '联系我们,获取专属方案和报价', + primaryAction, + secondaryAction, +}: DetailCTASectionProps) { + return ( +
+
+ +

{title}

+

{description}

+ + +
+
+
+ ); +} diff --git a/src/components/detail/detail-data-proof.tsx b/src/components/detail/detail-data-proof.tsx new file mode 100644 index 0000000..da7dc1d --- /dev/null +++ b/src/components/detail/detail-data-proof.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { motion } from 'framer-motion'; +import type { DataProof } from '@/lib/constants/products'; + +interface DataProofGridProps { + proofs: DataProof[]; + accentColor?: string; +} + +export function DataProofGrid({ proofs, accentColor = '#C41E3A' }: DataProofGridProps) { + return ( +
+ {proofs.map((proof, index) => ( + +

+ {proof.value} +

+

{proof.metric}

+

{proof.description}

+
+ ))} +
+ ); +} diff --git a/src/components/detail/detail-hero.tsx b/src/components/detail/detail-hero.tsx new file mode 100644 index 0000000..92e9940 --- /dev/null +++ b/src/components/detail/detail-hero.tsx @@ -0,0 +1,173 @@ +'use client'; + +import { motion } from 'framer-motion'; +import type { HeroTheme, HeroTexture } from '@/lib/constants/hero-themes'; + +interface DetailHeroProps { + theme: HeroTheme; + badge?: string; + title: string; + subtitle: string; + description?: string; + primaryAction?: { label: string; href: string }; + secondaryAction?: { label: string; href: string }; +} + +function TextureOverlay({ texture }: { texture: HeroTexture }) { + if (texture.type === 'none') return null; + + const textureStyles: Record = { + grid: { + backgroundImage: + 'linear-gradient(rgba(255,255,255,.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.03) 1px, transparent 1px)', + backgroundSize: '60px 60px', + }, + 'data-flow': { + backgroundImage: + 'radial-gradient(circle at 20% 50%, rgba(255,255,255,.04) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(255,255,255,.03) 0%, transparent 40%)', + }, + layers: { + backgroundImage: + 'linear-gradient(180deg, transparent 0%, rgba(255,255,255,.02) 50%, transparent 100%)', + }, + 'shield-hex': { + backgroundImage: + 'repeating-linear-gradient(60deg, transparent, transparent 30px, rgba(255,255,255,.025) 30px, rgba(255,255,255,.025) 31px)', + }, + circuit: { + backgroundImage: + 'linear-gradient(90deg, transparent 49%, rgba(255,255,255,.02) 49%, rgba(255,255,255,.02) 51%, transparent 51%)', + backgroundSize: '40px 40px', + }, + }; + + return ( +
+ ); +} + +export function DetailHero({ + theme, + badge, + title, + subtitle, + description, + primaryAction, + secondaryAction, +}: DetailHeroProps) { + const isCenterLayout = theme.layout === 'center' || theme.layout === 'wide'; + const isIconLeft = theme.layout === 'icon-left'; + + const containerClasses = + theme.layout === 'wide' + ? 'container-wide py-24 lg:py-32' + : 'container-wide py-20 lg:py-28'; + + const contentClasses = [ + 'max-w-4xl', + isCenterLayout ? 'mx-auto text-center' : '', + isIconLeft ? 'flex items-center gap-8' : '', + ] + .filter(Boolean) + .join(' '); + + return ( +
+ + +
+ + {(badge || theme.badge) && ( + + {badge || theme.badge} + + )} + +

+ {title} +

+ +

+ {subtitle} +

+ + {description && ( +

+ {description} +

+ )} + + {(primaryAction || secondaryAction) && ( +
+ {primaryAction && ( + + {primaryAction.label} + + )} + {secondaryAction && ( + + {secondaryAction.label} + + )} +
+ )} +
+
+
+ ); +} diff --git a/src/components/detail/detail-product-value.tsx b/src/components/detail/detail-product-value.tsx new file mode 100644 index 0000000..20e0d16 --- /dev/null +++ b/src/components/detail/detail-product-value.tsx @@ -0,0 +1,91 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { CheckCircle2, Zap, Shield, TrendingUp, type LucideIcon } from 'lucide-react'; +import type { Product } from '@/lib/constants/products'; + +interface ProductValueSectionProps { + product: Product; +} + +const icons: LucideIcon[] = [CheckCircle2, Zap, Shield, TrendingUp]; + +export function ProductValueSection({ product }: ProductValueSectionProps) { + return ( +
+
+
+ +

+ 核心能力 +

+

+ {product.overview} +

+
    + {product.features.map((feature, index) => { + const Icon = icons[index % icons.length]!; + return ( + + + {feature} + + ); + })} +
+
+ + +

+ 为什么选择我们 +

+
+ {product.benefits.map((benefit, index) => ( + + +

{benefit}

+
+ ))} +
+ +
+

技术规格

+
    + {product.specs.slice(0, 4).map((spec) => ( +
  • + + {spec} +
  • + ))} +
+
+
+
+
+
+ ); +} diff --git a/src/components/detail/detail-service-value.tsx b/src/components/detail/detail-service-value.tsx new file mode 100644 index 0000000..6b3e0a5 --- /dev/null +++ b/src/components/detail/detail-service-value.tsx @@ -0,0 +1,128 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { + CheckCircle2, + Clock, + Target, + Rocket, + ClipboardCheck, + Headphones, + type LucideIcon, +} from 'lucide-react'; +import type { Service } from '@/lib/constants/services'; + +interface ServiceValueSectionProps { + service: Service; +} + +const processIcons: LucideIcon[] = [Target, Rocket, ClipboardCheck, Headphones]; + +export function ServiceValueSection({ service }: ServiceValueSectionProps) { + return ( +
+
+ +

{service.overview}

+
+ +
+ +

服务能力

+
    + {service.features.map((feature, index) => ( + + + {feature} + + ))} +
+ +

核心价值

+
+ {service.benefits.map((benefit, index) => ( + +

{benefit}

+
+ ))} +
+
+ + +

+ + 服务流程 +

+
+ {service.process.map((step, index) => { + const Icon = processIcons[index % processIcons.length]!; + const isLast = index === service.process.length - 1; + + return ( + + {!isLast && ( +
+ )} +
+ +
+
+
+ + 步骤 {index + 1} + +
+

+ {step.split(':')[0]} +

+

+ {step.split(':')[1] || step.split(':')[1] || ''} +

+
+ + ); + })} +
+
+
+
+
+ ); +} diff --git a/src/components/detail/detail-solution-value.tsx b/src/components/detail/detail-solution-value.tsx new file mode 100644 index 0000000..091dbaa --- /dev/null +++ b/src/components/detail/detail-solution-value.tsx @@ -0,0 +1,158 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { AlertTriangle, CheckCircle2, ArrowRight, Package, Wrench } from 'lucide-react'; +import type { Solution } from '@/lib/constants/solutions'; +import { PRODUCTS } from '@/lib/constants/products'; +import { SERVICES } from '@/lib/constants/services'; + +interface SolutionValueSectionProps { + solution: Solution; +} + +export function SolutionValueSection({ solution }: SolutionValueSectionProps) { + const primaryProducts = solution.suiteCombination.primaryProducts + .map((id) => PRODUCTS.find((p) => p.id === id)) + .filter(Boolean); + + const complementaryServices = solution.suiteCombination.complementaryServices + .map((id) => SERVICES.find((s) => s.id === id)) + .filter(Boolean); + + return ( +
+
+ +

+ {solution.valueProposition.headline} +

+
+ +
+ {solution.valueProposition.points.map((point, index) => ( + +
+ {point.icon} +
+

{point.title}

+

{point.description}

+
+ ))} +
+ +
+ +

+ + 行业挑战 +

+
    + {solution.challenges.map((challenge, index) => ( + + + {challenge} + + ))} +
+
+ + +

+ + 解决方案 +

+
    + {solution.solutions.map((sol, index) => ( + + + {sol} + + ))} +
+
+
+ + {(primaryProducts.length > 0 || complementaryServices.length > 0) && ( + +

推荐套装组合

+

+ {solution.suiteCombination.rationale} +

+ +
+ {primaryProducts.map((product) => + product ? ( + + + {product.title} + + + ) : null, + )} + {complementaryServices.map((service) => + service ? ( + + + {service.title} + + + ) : null, + )} +
+
+ )} +
+
+ ); +} diff --git a/src/components/detail/detail-trust-section.tsx b/src/components/detail/detail-trust-section.tsx new file mode 100644 index 0000000..04a9401 --- /dev/null +++ b/src/components/detail/detail-trust-section.tsx @@ -0,0 +1,71 @@ +'use client'; + +import { CaseStudyCard } from './detail-case-study'; +import { DataProofGrid } from './detail-data-proof'; +import { CertificationList } from './detail-certification'; +import type { CaseStudy, DataProof, Certification } from '@/lib/constants/products'; + +interface DetailTrustSectionProps { + caseStudies?: CaseStudy[]; + dataProofs?: DataProof[]; + certifications?: Certification[]; + accentColor?: string; +} + +export function DetailTrustSection({ + caseStudies = [], + dataProofs = [], + certifications = [], + accentColor = '#C41E3A', +}: DetailTrustSectionProps) { + const hasContent = caseStudies.length > 0 || dataProofs.length > 0 || certifications.length > 0; + if (!hasContent) return null; + + return ( +
+
+ {dataProofs.length > 0 && ( +
+

+ 关键数据 +

+

+ 用真实数据说话,让效果可衡量、可验证 +

+ +
+ )} + + {caseStudies.length > 0 && ( +
+

+ 客户案例 +

+

+ 来自真实客户的成功实践,看看他们如何实现转型突破 +

+
+ {caseStudies.map((study, index) => ( + + ))} +
+
+ )} + + {certifications.length > 0 && ( +
+

+ 资质认证 +

+

+ 通过权威机构认证,保障安全合规 +

+
+ +
+
+ )} +
+
+ ); +} diff --git a/src/components/layout/footer.tsx b/src/components/layout/footer.tsx index aacefed..b872041 100644 --- a/src/components/layout/footer.tsx +++ b/src/components/layout/footer.tsx @@ -56,7 +56,7 @@ export function Footer() {

产品与方案

    - {(MEGA_DROPDOWN_DATA.products ?? []).map((item) => ( + {(MEGA_DROPDOWN_DATA.products ?? []).flatMap(group => group.items).filter(item => item.href !== '#').map((item) => (
  • ))} - {(MEGA_DROPDOWN_DATA.solutions ?? []).map((item) => ( + {(MEGA_DROPDOWN_DATA.solutions ?? []).flatMap(group => group.items).map((item) => (
  • +
    {COMPANY_INFO.name} @@ -109,7 +111,7 @@ function HeaderContent() { { setOpenDropdown((prev) => prev === item.id ? null : item.id); @@ -118,32 +120,33 @@ function HeaderContent() { onClose={() => setOpenDropdown((prev) => prev === item.id ? null : prev)} /> ) : ( - handleNavClick(e, item)} - className={` - relative px-3 py-1.5 text-sm font-medium - transition-all duration-300 - ${isActive(item) - ? 'text-[var(--color-text-primary)]' - : 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]' - } - `} - aria-current={isActive(item) ? 'page' : undefined} - > - {item.label} - + handleNavClick(e, item)} className={` - absolute bottom-0 left-1/2 -translate-x-1/2 w-6 h-0.5 bg-[var(--color-brand-primary)] rounded-full - transition-all duration-200 ease-out + relative block px-3 py-1.5 text-sm font-medium + transition-all duration-300 rounded-md ${isActive(item) - ? 'opacity-100 scale-x-100' - : 'opacity-0 scale-x-0' + ? 'text-[var(--color-text-primary)] bg-[var(--color-brand-primary-bg)]' + : 'text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)] hover:bg-[var(--color-bg-hover)]' } `} - /> - + aria-current={isActive(item) ? 'page' : undefined} + > + {item.label} + + +
    ) ))} diff --git a/src/components/layout/mega-dropdown.tsx b/src/components/layout/mega-dropdown.tsx index c429d16..4d06bd2 100644 --- a/src/components/layout/mega-dropdown.tsx +++ b/src/components/layout/mega-dropdown.tsx @@ -1,27 +1,21 @@ 'use client'; import { useRef, useEffect } from 'react'; -import { ChevronDown } from 'lucide-react'; +import { ChevronDown, Lock, Sparkles } from 'lucide-react'; import { StaticLink } from '@/components/ui/static-link'; import { AnimatePresence, motion } from 'framer-motion'; - -interface MegaDropdownItem { - id: string; - title: string; - description: string; - href: string; -} +import type { MegaDropdownGroup } from '@/lib/constants/navigation'; interface MegaDropdownProps { label: string; - items: MegaDropdownItem[]; + groups: MegaDropdownGroup[]; isOpen: boolean; onToggle: () => void; onOpen: () => void; onClose: () => void; } -export function MegaDropdown({ label, items, isOpen, onToggle, onOpen, onClose }: MegaDropdownProps) { +export function MegaDropdown({ label, groups, isOpen, onToggle, onOpen, onClose }: MegaDropdownProps) { const dropdownRef = useRef(null); const timeoutRef = useRef>(undefined); @@ -40,24 +34,26 @@ export function MegaDropdown({ label, items, isOpen, onToggle, onOpen, onClose } return (
    - +
    + +
    {isOpen && ( @@ -66,25 +62,64 @@ export function MegaDropdown({ label, items, isOpen, onToggle, onOpen, onClose } animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -8 }} transition={{ duration: 0.15 }} - className="absolute top-full left-1/2 -translate-x-1/2 pt-2 w-[520px] z-50" + className="absolute top-full left-1/2 -translate-x-1/2 pt-2 w-[640px] z-50" > -
    -
    - {items.map((item) => ( - -
    -
    -
    -
    {item.title}
    -
    {item.description}
    -
    +
    + {groups.map((group, groupIndex) => ( +
    0 ? 'mt-6 pt-6 border-t border-[var(--color-border-primary)]' : ''}> +
    +

    {group.title}

    + {group.description && ( + {group.description} + )} + {group.highlight && ( + + )}
    - + +
    4 ? 'grid-cols-3' : 'grid-cols-2'}`}> + {group.items.map((item) => ( + +
    +
    +
    +
    {item.title}
    + {item.badge && ( + + {item.badge === '敬请期待' && } + {item.badge} + + )} +
    +
    {item.description}
    +
    +
    +
    + ))} +
    +
    ))} + +
    + + 查看全部{label} + +
    diff --git a/src/components/layout/mobile-menu.tsx b/src/components/layout/mobile-menu.tsx index bf1ef9b..b8037e9 100644 --- a/src/components/layout/mobile-menu.tsx +++ b/src/components/layout/mobile-menu.tsx @@ -94,7 +94,7 @@ export function MobileMenu({ className }: MobileMenuProps) { {expandedDropdown === item.dropdownKey && (
      - {(MEGA_DROPDOWN_DATA[item.dropdownKey] ?? []).map((sub) => ( + {(MEGA_DROPDOWN_DATA[item.dropdownKey] ?? []).flatMap(group => group.items).filter(sub => sub.href !== '#').map((sub) => (
    • (null); @@ -94,19 +93,36 @@ export function CTASection({ className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-white/5 border border-white/10 mb-8" > - 开始合作 + 开始对话

      {title}

      -

      +

      {description}

      + +
      +
      + + 免费咨询 +
      +
      + + 30 分钟内响应 +
      +
      + + 无销售压力 +
      +
      +
      - - {PRODUCTS.length} PRODUCTS - +
      + + + 研发中 · 即将上线 + + + {PRODUCTS.length} PRODUCTS + +
      diff --git a/src/components/sections/why-us-section.tsx b/src/components/sections/why-us-section.tsx new file mode 100644 index 0000000..e32234c --- /dev/null +++ b/src/components/sections/why-us-section.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { StaticLink } from '@/components/ui/static-link'; +import { Button } from '@/components/ui/button'; +import { Shield, Building2, Users, Code, Target, ArrowRight } from 'lucide-react'; +import { useReducedMotion } from '@/hooks/use-reduced-motion'; + +const TRUST_PILLARS = [ + { + icon: Shield, + title: '12年+ 行业深耕', + description: '核心团队长期从事技术咨询、企业数字化,服务覆盖金融、制造、零售、政务等多个行业', + stat: '12+', + statLabel: '年行业经验', + }, + { + icon: Building2, + title: '大型 IT 企业背景', + description: '开发团队来自多个大型传统 IT 企业,具备扎实的工程能力和规范化交付经验', + stat: '10+', + statLabel: '人核心团队', + }, + { + icon: Users, + title: '复合型技术团队', + description: '既懂技术又懂业务,深入理解客户真实场景,提供真正可落地的解决方案', + stat: '6', + statLabel: '款自研产品', + }, + { + icon: Code, + title: '全栈技术能力', + description: '从前端到后端、云原生到数据智能、移动端到物联网的全栈技术能力', + stat: '5+', + statLabel: '个覆盖行业', + }, + { + icon: Target, + title: '结果导向交付', + description: '不以"项目上线"为终点,以"客户业务是否真正改善"为衡量标准', + stat: '100%', + statLabel: '项目交付率', + }, +]; + +export function WhyUsSection() { + const shouldReduceMotion = useReducedMotion(); + + return ( +
      +
      + +
      +
      + WHY US +
      +
      +

      + 为什么选择我们 +

      +

      + 我们不是一家“做完就跑”的外包公司,而是愿意与您一起成长的数字化转型同行者 +

      + + +
      + {TRUST_PILLARS.slice(0, 5).map((pillar, idx) => { + const Icon = pillar.icon; + return ( + +
      +
      + +
      +
      +
      {pillar.stat}
      +
      {pillar.statLabel}
      +
      +
      +

      {pillar.title}

      +

      {pillar.description}

      +
      + ); + })} +
      + + +

      想深入了解我们的团队能力?

      + +
      +
      +
      + ); +} diff --git a/src/components/ui/product-card.tsx b/src/components/ui/product-card.tsx index c6af601..4713d38 100644 --- a/src/components/ui/product-card.tsx +++ b/src/components/ui/product-card.tsx @@ -83,8 +83,17 @@ export function ProductCard({ title, description, href, index, status }: Product {description}

      + {(status === '研发中' || status === '内测中') && ( +
      + + + + {status === '研发中' ? '正在积极开发中,欢迎提前交流需求' : '即将上线,欢迎预约内测体验'} +
      + )} +
      - 了解规划 + {status === '研发中' ? '了解规划' : status === '内测中' ? '申请内测' : '了解更多'}
      diff --git a/src/components/ui/scroll-progress.tsx b/src/components/ui/scroll-progress.tsx index 35bcb08..aa2b2fa 100644 --- a/src/components/ui/scroll-progress.tsx +++ b/src/components/ui/scroll-progress.tsx @@ -15,6 +15,12 @@ export function ScrollProgress() { restDelta: 0.001, }); + const glowX = useSpring(scrollYProgress, { + stiffness: 120, + damping: 25, + restDelta: 0.001, + }); + useEffect(() => { const handleScroll = () => { setIsVisible(window.scrollY > 80); @@ -45,6 +51,20 @@ export function ScrollProgress() { background: 'var(--color-brand-primary)', }} /> + {!shouldReduceMotion && ( + + )} ); }