195 lines
9.3 KiB
TypeScript
195 lines
9.3 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Card, CardContent } from "@/components/ui/card"
|
|
import {
|
|
Monitor,
|
|
SplitSquareVertical,
|
|
BarChart3,
|
|
Check,
|
|
ArrowRight,
|
|
Cpu,
|
|
Network,
|
|
Eye,
|
|
} from "lucide-react"
|
|
import homeData from "@/data/home.json"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
const productIcons: Record<string, React.ReactNode> = {
|
|
pos: <Monitor className="w-7 h-7" />,
|
|
split: <SplitSquareVertical className="w-7 h-7" />,
|
|
monitor: <Eye className="w-7 h-7" />,
|
|
}
|
|
|
|
const productBgSvg: Record<string, React.ReactNode> = {
|
|
pos: (
|
|
<svg className="absolute inset-0 w-full h-full opacity-[0.04]" viewBox="0 0 400 280" fill="none" aria-hidden="true">
|
|
<rect x="60" y="40" width="280" height="160" rx="12" stroke="currentColor" strokeWidth="2" />
|
|
<rect x="80" y="60" width="240" height="120" rx="6" stroke="currentColor" strokeWidth="1.5" />
|
|
<line x1="160" y1="200" x2="240" y2="200" stroke="currentColor" strokeWidth="2" />
|
|
<rect x="140" y="200" width="120" height="30" rx="4" stroke="currentColor" strokeWidth="1.5" />
|
|
{[0,1,2,3].map(i => <rect key={i} x={90 + i * 56} y="70" width="44" height="28" rx="4" stroke="currentColor" strokeWidth="1" />)}
|
|
</svg>
|
|
),
|
|
split: (
|
|
<svg className="absolute inset-0 w-full h-full opacity-[0.04]" viewBox="0 0 400 280" fill="none" aria-hidden="true">
|
|
<circle cx="200" cy="90" r="30" stroke="currentColor" strokeWidth="2" />
|
|
<circle cx="100" cy="200" r="22" stroke="currentColor" strokeWidth="2" />
|
|
<circle cx="200" cy="200" r="22" stroke="currentColor" strokeWidth="2" />
|
|
<circle cx="300" cy="200" r="22" stroke="currentColor" strokeWidth="2" />
|
|
<line x1="200" y1="120" x2="100" y2="178" stroke="currentColor" strokeWidth="1.5" />
|
|
<line x1="200" y1="120" x2="200" y2="178" stroke="currentColor" strokeWidth="1.5" />
|
|
<line x1="200" y1="120" x2="300" y2="178" stroke="currentColor" strokeWidth="1.5" />
|
|
</svg>
|
|
),
|
|
monitor: (
|
|
<svg className="absolute inset-0 w-full h-full opacity-[0.04]" viewBox="0 0 400 280" fill="none" aria-hidden="true">
|
|
{[0,1,2,3,4].map(i => <line key={i} x1="40" y1={60 + i * 40} x2="360" y2={60 + i * 40} stroke="currentColor" strokeWidth="1" />)}
|
|
{[0,1,2,3,4,5,6,7].map(i => <line key={i} x1={40 + i * 46} y1="60" x2={40 + i * 46} y2="220" stroke="currentColor" strokeWidth="1" />)}
|
|
<polyline points="40,200 120,140 200,160 280,100 360,120" stroke="currentColor" strokeWidth="2" fill="none" />
|
|
<circle cx="280" cy="100" r="5" fill="currentColor" />
|
|
</svg>
|
|
),
|
|
}
|
|
|
|
const colorMap: Record<string, { ring: string; badge: string; icon: string }> = {
|
|
blue: { ring: "hover:border-primary/50", badge: "bg-primary/10 text-primary", icon: "bg-primary text-white" },
|
|
indigo: { ring: "hover:border-indigo-400/50", badge: "bg-indigo-50 text-indigo-600", icon: "bg-indigo-600 text-white" },
|
|
violet: { ring: "hover:border-violet-400/50", badge: "bg-violet-50 text-violet-600", icon: "bg-violet-600 text-white" },
|
|
}
|
|
|
|
export function ServicesSection() {
|
|
const { products } = homeData
|
|
const [active, setActive] = useState(0)
|
|
|
|
return (
|
|
<section id="products" className="py-20 lg:py-28 bg-background">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
{/* Header */}
|
|
<div className="flex flex-col items-center text-center gap-4 mb-14">
|
|
<Badge
|
|
variant="outline"
|
|
className="border-primary/30 text-primary bg-primary-light px-4 py-1 text-sm font-medium"
|
|
>
|
|
{products.sectionBadge}
|
|
</Badge>
|
|
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-foreground text-balance">
|
|
{products.title}
|
|
</h2>
|
|
<p className="text-muted-foreground text-base lg:text-lg max-w-2xl text-pretty">
|
|
{products.subtitle}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Tab triggers */}
|
|
<div className="flex flex-col sm:flex-row items-center justify-center gap-3 mb-10">
|
|
{products.items.map((item, i) => {
|
|
const colors = colorMap[item.color]
|
|
return (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => setActive(i)}
|
|
className={cn(
|
|
"flex items-center gap-2.5 px-5 py-3 rounded-xl border text-sm font-semibold transition-all duration-200",
|
|
active === i
|
|
? "bg-primary border-primary text-white shadow-lg shadow-primary/25"
|
|
: "bg-background border-border text-muted-foreground hover:border-primary/40 hover:text-foreground"
|
|
)}
|
|
>
|
|
<span className={cn("w-6 h-6 rounded-lg flex items-center justify-center", active === i ? "bg-white/20" : colors.icon)}>
|
|
{item.iconType === "pos" && <Monitor className="w-3.5 h-3.5" />}
|
|
{item.iconType === "split" && <Network className="w-3.5 h-3.5" />}
|
|
{item.iconType === "monitor" && <Eye className="w-3.5 h-3.5" />}
|
|
</span>
|
|
{item.tag}
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
|
|
{/* Active product detail */}
|
|
{products.items.map((item, i) => {
|
|
const colors = colorMap[item.color]
|
|
if (i !== active) return null
|
|
return (
|
|
<Card
|
|
key={item.id}
|
|
className={cn(
|
|
"border-2 overflow-hidden transition-all duration-300",
|
|
active === i ? "border-primary/30 shadow-xl shadow-primary/10" : "border-border"
|
|
)}
|
|
>
|
|
<CardContent className="p-0">
|
|
<div className="grid grid-cols-1 lg:grid-cols-2">
|
|
{/* Left: text */}
|
|
<div className="p-8 lg:p-10 flex flex-col gap-6">
|
|
<div className="flex items-center gap-3">
|
|
<div className={cn("w-12 h-12 rounded-2xl flex items-center justify-center shadow-sm", colors.icon)}>
|
|
{productIcons[item.iconType]}
|
|
</div>
|
|
<div>
|
|
<span className={cn("text-xs font-semibold px-2.5 py-1 rounded-full", colors.badge)}>
|
|
{item.tag}
|
|
</span>
|
|
<h3 className="text-xl font-bold text-foreground mt-1">{item.title}</h3>
|
|
</div>
|
|
</div>
|
|
<p className="text-muted-foreground leading-relaxed text-pretty">{item.description}</p>
|
|
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-2.5">
|
|
{item.features.map((f) => (
|
|
<li key={f} className="flex items-center gap-2 text-sm text-foreground">
|
|
<div className="w-5 h-5 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
|
|
<Check className="w-3 h-3 text-primary" />
|
|
</div>
|
|
{f}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<a
|
|
href="#contact"
|
|
className="self-start flex items-center gap-1.5 text-sm font-semibold text-primary hover:gap-2.5 transition-all"
|
|
>
|
|
了解更多 <ArrowRight className="w-4 h-4" />
|
|
</a>
|
|
</div>
|
|
|
|
{/* Right: visual mockup */}
|
|
<div className={cn(
|
|
"relative flex items-center justify-center min-h-[280px] lg:min-h-0 overflow-hidden",
|
|
item.color === "blue" && "bg-primary/5",
|
|
item.color === "indigo" && "bg-indigo-50",
|
|
item.color === "violet" && "bg-violet-50",
|
|
)}>
|
|
{productBgSvg[item.iconType]}
|
|
<div className="relative z-10 flex flex-col items-center gap-5 p-8">
|
|
{/* Central icon */}
|
|
<div className={cn("w-20 h-20 rounded-3xl flex items-center justify-center shadow-xl", colors.icon)}>
|
|
{item.iconType === "pos" && <Monitor className="w-10 h-10" />}
|
|
{item.iconType === "split" && <SplitSquareVertical className="w-10 h-10" />}
|
|
{item.iconType === "monitor" && <BarChart3 className="w-10 h-10" />}
|
|
</div>
|
|
{/* Floating stat cards */}
|
|
<div className="flex flex-wrap justify-center gap-3 max-w-xs">
|
|
{item.features.slice(0, 3).map((f) => (
|
|
<span key={f} className="bg-white border border-border text-xs text-foreground font-medium px-3 py-1.5 rounded-full shadow-sm">
|
|
{f}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<div className="flex items-center gap-2 bg-white border border-border rounded-xl px-4 py-2.5 shadow-sm">
|
|
<Cpu className="w-4 h-4 text-primary" />
|
|
<span className="text-xs font-semibold text-foreground">智店软件 · 智能引擎驱动</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
})}
|
|
</div>
|
|
</section>
|
|
)
|
|
}
|