Files
xunchiProduct/website/src/pages/ProductPage.jsx
T
2026-06-11 16:53:18 +08:00

380 lines
21 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useParams, Link } from 'react-router-dom'
import { ChevronDown, ArrowRight, Shield, Clock, Headphones, AlertCircle } from 'lucide-react'
import { getProduct } from '../data/products'
import ScrollReveal from '../components/ScrollReveal'
import StatNumber from '../components/StatNumber'
import Carousel from '../components/Carousel'
export default function ProductPage() {
const { slug } = useParams()
const product = getProduct(slug)
if (!product) {
return (
<div className="min-h-screen flex items-center justify-center bg-white">
<div className="text-center">
<h1 className="text-4xl font-bold text-[#0f172a] mb-4">产品未找到</h1>
<p className="text-[#94a3b8] mb-8">请检查链接是否正确</p>
<Link to="/" className="text-[#004ec4] hover:underline">返回首页</Link>
</div>
</div>
)
}
// 将 hex 主题色转为 RGB 分量,用于 rgba() 可变透明度
const hex = product.themeColor.replace('#', '')
const r = parseInt(hex.slice(0, 2), 16)
const g = parseInt(hex.slice(2, 4), 16)
const b = parseInt(hex.slice(4, 6), 16)
// const Icon = product.icon
// const otherProducts = products.filter(p => p.slug !== slug)
return (
<main
className="bg-white"
style={{
'--theme': product.themeColor,
'--theme-r': r,
'--theme-g': g,
'--theme-b': b,
}}
>
{/* ── Hero ── */}
<div className="relative -mt-[70px]">
<section className="relative overflow-hidden" style={{ backgroundColor: product.themeColor }}>
{/* Desktop: left image + right card */}
<div className="hidden md:flex items-center justify-between mx-auto min-h-[760px]">
{/* Image - left */}
<div className="w-1/2 flex items-center justify-center">
{product.heroImage && (
<img src={product.heroImage} alt="" className={`w-full h-auto object-contain ${product.heroSize === 'small' ? 'max-h-[350px]' : 'max-h-[500px]'}`} />
)}
</div>
{/* Content - right */}
<div className="w-1/2">
<h1 className="text-5xl md:text-7xl font-extrabold mb-5 tracking-tight leading-[1.1] text-white hero-product-title">
{product.name}
</h1>
<p className="text-2xl md:text-3xl mb-8 leading-relaxed text-white/80 hero-product-slogan">
{product.slogan}
</p>
{product.stats && (
<div className="flex gap-4 max-w-[620px]">
{product.stats.map((s, i) => (
<div key={i} className="flex-1 rounded-2xl p-4 text-center border border-white/20 bg-white/10">
<div className="text-2xl md:text-3xl font-extrabold tracking-tight text-white">
<StatNumber target={s.num} suffix={s.suffix} />
</div>
<div className="text-xs text-white/60 mt-1.5 font-medium">{s.label}</div>
</div>
))}
</div>
)}
</div>
</div>
{/* Mobile: stacked layout */}
<div className="md:hidden flex flex-col items-center px-6 pt-32 pb-16">
{product.heroImage && (
<img src={product.heroImage} alt="" className={`w-4/5 max-w-xs h-auto object-contain mb-10 ${product.heroSize === 'small' ? 'max-h-[150px]' : 'max-h-[200px]'}`} />
)}
<div className="text-center w-full">
<span className="inline-block px-4 py-1.5 rounded-full text-xs font-bold tracking-wider uppercase mb-5"
style={{ backgroundColor: product.themeColor + '18', color: product.themeColor }}>
智慧教育解决方案
</span>
<h1 className="text-3xl font-extrabold mb-4 tracking-tight leading-[1.1] text-white hero-product-title">
{product.name}
</h1>
<p className="text-xl mb-6 leading-relaxed text-white/80 hero-product-slogan">
{product.slogan}
</p>
{product.stats && (
<div className="flex gap-3 mt-4">
{product.stats.map((s, i) => (
<div key={i} className="flex-1 rounded-2xl p-3 text-center border border-white/20 bg-white/10">
<div className="text-xl font-extrabold tracking-tight text-white">
<StatNumber target={s.num} suffix={s.suffix} />
</div>
<div className="text-[11px] text-white/60 mt-1 font-medium">{s.label}</div>
</div>
))}
</div>
)}
</div>
</div>
</section>
</div>
{/* ── Brief ── */}
<section className="py-20 px-6 bg-white">
<div className="max-w-3xl mx-auto">
<ScrollReveal>
<div className="relative bg-[#f8fafc] rounded-3xl p-10 md:p-14 overflow-hidden">
{/* Floating decorative shapes */}
<div className="absolute -top-6 -right-6 w-24 h-24 rounded-full bg-[var(--theme)]" />
<div className="absolute -bottom-8 -left-8 w-20 h-20 rounded-2xl rotate-12 bg-[var(--theme)]" />
<div className="absolute top-1/2 -right-4 w-8 h-8 rounded-full border-2 border-[var(--theme)]" />
<p className="text-[#334155] text-lg md:text-xl leading-loose relative z-10">
{product.brief}
</p>
</div>
{product.tags && (
<div className="flex flex-wrap items-center justify-center gap-3 mt-10">
{product.tags.map((tag, i) => (
<span key={i}
className="relative px-6 py-2 text-sm font-bold text-white transition-all duration-200 hover:scale-105 hover:-translate-y-0.5"
style={{ backgroundColor: product.themeColor }}>
<span className="relative z-10">{tag}</span>
<svg className="absolute -bottom-2 left-0 w-full h-2 text-white/20" viewBox="0 0 100 8" preserveAspectRatio="none">
<path d="M0 0 L35 8 L65 8 L100 0" fill="currentColor" />
</svg>
<span className="absolute top-1/2 -translate-y-1/2 -left-2 w-1 h-4 rounded-full" style={{ backgroundColor: product.themeColor }} />
</span>
))}
</div>
)}
</ScrollReveal>
</div>
</section>
{/* ── Pain Points ── */}
{product.painPoints && product.painPoints.length > 0 && (
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-14 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Pain Points</span>
</div>
<h2 className="text-2xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
{product.painPointsTitle || `${product.name}解决的核心问题`}
</h2>
<p className="text-[#475569] text-lg md:text-xl mt-6 max-w-xl leading-relaxed">深入理解管理难题提供经过实战验证的解决方案</p>
</div>
</ScrollReveal>
<div className="space-y-5">
{product.painPoints.map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-white rounded-2xl p-6 xs:p-8 md:p-10 border border-gray-100 hover:shadow-lg hover:shadow-gray-200/50 transition-all duration-300 group h-full">
<div className="absolute top-0 left-0 right-0 h-1 rounded-t-2 bg-[var(--theme)] opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
{/* Pain point */}
<div className="flex items-start gap-2 xs:gap-3 mb-4 xs:mb-5 pb-4 xs:pb-5 border-b border-dashed border-gray-200">
<div className="shrink-0 w-7 h-7 xs:w-9 xs:h-9 rounded-full bg-red-50 flex items-center justify-center">
<AlertCircle className="w-4 h-4 xs:w-5 xs:h-5 text-red-500" />
</div>
<h3 className="text-base xs:text-lg font-bold text-[#0f172a] leading-relaxed">{item.title}</h3>
</div>
{/* Solution */}
<div className="ml-0 sm:ml-6 md:ml-11 pl-3 xs:pl-5 border-l-2 border-[var(--theme)] bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.03)] rounded-r-lg py-3 xs:py-4 pr-3 xs:pr-4">
<span className="inline-block text-xs xs:text-sm font-semibold text-white bg-[var(--theme)] rounded px-2 xs:px-3 py-1 xs:py-1.5 mb-2 xs:mb-3">解决方案</span>
<p className="text-[#334155] text-sm xs:text-base md:text-lg xl:text-xl leading-[1.7] xs:leading-[1.9]">{item.desc}</p>
</div>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
)}
{/* ── Core Features ── */}
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Core Features</span>
</div>
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
核心功能
</h2>
{product.target && (
<div className="mt-5 flex flex-col items-center gap-3">
<span className="text-lg text-[#0f172a] font-bold">适用对象</span>
<div className="flex flex-wrap justify-center gap-3">
{product.target.split('、').map((t, i) => (
<span key={i} className="px-4 py-1.5 rounded-lg text-sm font-bold text-[var(--theme)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.3)]">{t.trim()}</span>
))}
</div>
</div>
)}
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{product.features.map((f, i) => (
<ScrollReveal key={i} delay={i * 0.05}>
<div className="relative bg-white rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.05)] transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)` }} />
<div className="absolute top-0 right-0 w-32 h-32 bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-20 h-20 border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] rounded-tr-3xl" />
<div className="relative z-10">
<div className="flex items-center gap-3 mb-4">
<span className="flex items-center justify-center w-8 h-8 rounded-md bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] text-xs font-black text-[var(--theme)]">0{i + 1}</span>
<div className="h-px flex-1 bg-gradient-to-r from-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] to-transparent" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{f.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{f.desc}</p>
</div>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ── Product Showcase ── */}
{product.screenshots && product.screenshots.length > 0 && (
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Product Showcase</span>
</div>
<h2 className="text-3xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
产品展示
</h2>
</div>
</ScrollReveal>
<Carousel images={product.screenshots} themeColor={product.themeColor} />
</div>
</section>
)}
{/* ── Advantages ── */}
<section className="py-24 px-6 bg-white">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-16 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">Why Choose Us</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
为什么选择我们
</h2>
</div>
</ScrollReveal>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[
{ icon: Shield, title: '安全可靠', desc: '等保三级认证,银行级加密存储,多机房容灾备份' },
{ icon: Clock, title: '快速部署', desc: 'SaaS 即开即用,私有化部署最快 3 天交付上线' },
{ icon: Headphones, title: '专属服务', desc: '7×24 小时响应,专属客户经理全程陪伴' },
].map((item, i) => (
<ScrollReveal key={i} delay={i * 0.08}>
<div className="relative bg-[#fafbfc] rounded-2xl p-8 md:p-10 h-full border border-gray-100 hover:shadow-xl hover:shadow-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.05)] transition-all duration-500 group overflow-hidden">
<div className="absolute top-0 left-0 right-0 h-[2px] opacity-0 group-hover:opacity-100 transition-opacity duration-500"
style={{ background: `linear-gradient(90deg, transparent, ${product.themeColor}, transparent)` }} />
<div className="absolute top-0 right-0 w-28 h-28 bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-bl-full" />
<div className="absolute bottom-0 left-0 w-16 h-16 border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] rounded-tr-3xl" />
<div className="relative z-10">
<div className="flex items-center gap-3 mb-5">
<div className="w-11 h-11 rounded-xl flex items-center justify-center bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)]">
<item.icon className="w-5 h-5" style={{ color: product.themeColor }} />
</div>
<div className="h-px flex-1 bg-gradient-to-r from-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] to-transparent" />
</div>
<h3 className="text-xl font-bold text-[#0f172a] mb-3">{item.title}</h3>
<p className="text-[#64748b] text-lg leading-relaxed">{item.desc}</p>
</div>
</div>
</ScrollReveal>
))}
</div>
</div>
</section>
{/* ── FAQ ── */}
{product.faq && product.faq.length > 0 && (
<section className="py-24 px-6 bg-[#fafbfc]">
<div className="max-w-5xl mx-auto">
<ScrollReveal className="mb-14 text-center">
<div className="flex flex-col items-center">
<div className="inline-flex items-center gap-3 mb-5">
<span className="w-5 h-[2px] rounded-full bg-[var(--theme)]" />
<span className="text-xs font-semibold uppercase tracking-[0.25em] text-[var(--theme)]">FAQ</span>
</div>
<h2 className="text-4xl md:text-[3.5rem] font-black text-[#0f172a] tracking-tight leading-[1.1]">
常见问题
</h2>
</div>
</ScrollReveal>
<div className="space-y-4 max-w-3xl mx-auto">
{product.faq.map((item, i) => (
<ScrollReveal key={i} delay={i * 0.04}>
<details className="group relative bg-white rounded-2xl border border-gray-100 hover:shadow-lg hover:shadow-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.05)] transition-all duration-300 overflow-hidden">
<div className="absolute top-0 left-0 w-1 h-full bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] group-open:bg-[var(--theme)] transition-colors duration-300" />
<div className="absolute top-0 right-0 w-24 h-24 bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-bl-full" />
<summary className="relative flex items-center justify-between px-8 py-6 cursor-pointer list-none select-none">
<div className="flex items-center gap-4 pr-4">
<span className="flex items-center justify-center w-8 h-8 rounded-lg bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] text-xs font-black text-[var(--theme)] shrink-0">Q{i + 1}</span>
<h3 className="text-lg font-bold text-[#0f172a]">{item.q}</h3>
</div>
<div className="w-8 h-8 rounded-full bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.06)] border border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] flex items-center justify-center shrink-0 group-open:rotate-180 transition-transform duration-300">
<ChevronDown className="w-4 h-4 text-[var(--theme)]" />
</div>
</summary>
<div className="relative px-8 pb-7">
<div className="ml-12 pl-5 border-l-2 border-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.1)] bg-[rgba(var(--theme-r),var(--theme-g),var(--theme-b),0.02)] rounded-r-lg py-4 pr-4">
<p className="text-[#64748b] text-lg leading-relaxed">{item.a}</p>
</div>
</div>
</details>
</ScrollReveal>
))}
</div>
</div>
</section>
)}
{/* ── CTA ── */}
<section className="py-40 px-4 relative overflow-hidden"
style={{ background: `linear-gradient(135deg, ${product.themeFrom || '#0a1628'}, ${product.themeTo || '#0f1f3a'})` }}>
<div className="absolute inset-0 opacity-[0.04]"
style={{
backgroundImage: `radial-gradient(${product.themeColor} 1px, transparent 1px)`,
backgroundSize: '24px 24px',
}}
/>
<div className="relative max-w-3xl mx-auto text-center">
<ScrollReveal>
<h2 className="text-3xl xs:text-4xl sm:text-5xl md:text-6xl font-bold text-white mb-8">{product.name}感兴趣</h2>
<p className="text-white text-sm xs:text-base sm:text-lg mb-14 max-w-xl mx-auto leading-relaxed">
获取产品演示与个性化方案迈出数字化转型第一步
</p>
<div className="flex flex-col sm:flex-row gap-3 xs:gap-4 justify-center mx-auto w-[90%] xs:w-[85%] sm:w-auto">
<Link
to="/contact"
className="inline-flex items-center justify-center gap-2 px-5 xs:px-6 py-2.5 xs:py-3 rounded-xl text-xs xs:text-sm font-semibold text-white border border-white hover:bg-white/10 transition-all"
>
联系我们
<ArrowRight className="w-3.5 h-3.5 xs:w-4 xs:h-4" />
</Link>
<a
href="tel:07713864294"
className="inline-flex items-center justify-center gap-2 px-5 xs:px-6 py-2.5 xs:py-3 rounded-xl text-xs xs:text-sm font-semibold text-white border border-white hover:bg-white/10 transition-all"
>
致电 0771-3864294
</a>
</div>
</ScrollReveal>
</div>
</section>
</main>
)
}