Files
company-site/components/sections/products-section.tsx
JanYork 6a68a96287 feat: 添加连锁门店系统首页及核心UI组件
新增首页布局、导航栏、页脚及多个核心UI组件(按钮、卡片、表格等)
添加图片资源、工具函数和样式配置
实现响应式设计和主题支持
包含行业解决方案展示区块
2026-03-18 10:50:42 +08:00

233 lines
9.4 KiB
TypeScript

"use client"
import { useState } from "react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Check, CreditCard, GitBranch, Monitor, ArrowRight } from "lucide-react"
import { cn } from "@/lib/utils"
import { SectionReveal } from "@/components/ui/section-reveal"
import homeData from "@/data/home.json"
import Link from "next/link"
const iconMap: Record<string, React.ReactNode> = {
pos: <CreditCard className="w-7 h-7" />,
split: <GitBranch className="w-7 h-7" />,
monitor: <Monitor className="w-7 h-7" />,
}
const accentMap: Record<string, { bg: string; border: string; text: string; glow: string }> = {
blue: {
bg: "bg-primary",
border: "border-primary/20",
text: "text-primary",
glow: "shadow-primary/20",
},
indigo: {
bg: "bg-indigo-600",
border: "border-indigo-500/20",
text: "text-indigo-600",
glow: "shadow-indigo-500/20",
},
violet: {
bg: "bg-violet-600",
border: "border-violet-500/20",
text: "text-violet-600",
glow: "shadow-violet-500/20",
},
}
export function ProductsSection() {
const { products } = homeData
const productsAny = products as any
const sectionBadge = productsAny.sectionBadge ?? productsAny.sectionTag
const normalizedItems = (Array.isArray(productsAny.items) ? productsAny.items : []).map((item: any) => {
const id = item.id ?? ""
const iconType = item.iconType ?? (id === "cashier" ? "pos" : id === "split" ? "split" : "monitor")
const color = item.color ?? (id === "cashier" ? "blue" : id === "split" ? "indigo" : "violet")
return {
...item,
iconType,
color,
title: item.title ?? item.name ?? item.tag,
}
})
const [active, setActive] = useState(0)
const current = normalizedItems[active] ?? normalizedItems[0]
const accent = accentMap[current?.color] ?? accentMap.blue
if (!current) {
return null
}
return (
<section id="products" className="section-panel bg-background">
<SectionReveal className="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" delay={40}>
{/* Header */}
<div className="text-center max-w-2xl mx-auto mb-16">
<Badge variant="secondary" className="mb-4 text-primary border-primary/20 bg-primary/8 px-3 py-1">
{sectionBadge}
</Badge>
<h2 className="text-3xl sm:text-4xl lg:text-5xl font-extrabold text-foreground text-balance mb-4 tracking-tight">
{products.title}
</h2>
<p className="text-muted-foreground text-lg leading-relaxed text-pretty">
{products.subtitle}
</p>
</div>
{/* Tab switcher */}
<div className="flex justify-center mb-12">
<div className="inline-flex items-center gap-2 bg-muted p-1.5 rounded-2xl">
{normalizedItems.map((item, i) => {
const a = accentMap[item.color] ?? accentMap.blue
return (
<button
key={item.id}
onClick={() => setActive(i)}
className={cn(
"flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-semibold transition-all duration-200",
active === i
? `${a.bg} text-white shadow-md`
: "text-muted-foreground hover:text-foreground"
)}
aria-pressed={active === i}
>
{iconMap[item.iconType] ?? iconMap.monitor}
<span className="hidden sm:inline">{item.tag}</span>
</button>
)
})}
</div>
</div>
{/* Detail panel */}
<div
key={current.id}
className={cn(
"grid lg:grid-cols-2 gap-10 items-center border rounded-3xl p-8 lg:p-12 shadow-xl",
accent.border,
accent.glow
)}
style={{ boxShadow: `0 20px 60px -10px var(--tw-shadow-color)` }}
>
{/* Left: info */}
<div className="flex flex-col gap-6">
<div className={cn("inline-flex items-center gap-2 rounded-xl px-3 py-1.5 w-fit", `${accent.bg}/10`)}>
<span className={accent.text}>{iconMap[current.iconType] ?? iconMap.monitor}</span>
<span className={cn("text-sm font-semibold", accent.text)}>{current.tag}</span>
</div>
<div>
<h3 className="text-2xl lg:text-3xl font-extrabold text-foreground mb-3 tracking-tight">
{current.title}
</h3>
<p className={cn("text-sm font-semibold mb-3", accent.text)}>
{current.tagline}
</p>
<p className="text-muted-foreground leading-relaxed text-pretty">
{current.description}
</p>
</div>
<div className="grid grid-cols-2 gap-3 max-w-lg">
<div className={cn("rounded-2xl border px-4 py-3", accent.border, `${accent.bg}/5`)}>
<p className={cn("text-lg font-black tracking-tight", accent.text)}>{current.highlight}</p>
<p className="text-xs text-muted-foreground mt-1"></p>
</div>
<div className={cn("rounded-2xl border px-4 py-3", accent.border, `${accent.bg}/5`)}>
<p className="text-sm font-bold text-foreground">{current.highlightSub}</p>
<p className="text-xs text-muted-foreground mt-1"></p>
</div>
</div>
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{current.features.map((f) => (
<li key={f} className="flex items-center gap-2.5 text-sm text-foreground">
<span className={cn("w-5 h-5 rounded-full flex items-center justify-center shrink-0", `${accent.bg}/10`)}>
<Check className={cn("w-3 h-3", accent.text)} />
</span>
{f}
</li>
))}
</ul>
<Button
asChild
className={cn("w-fit px-6 group", accent.bg, "hover:opacity-90 text-white")}
>
<Link href="#contact" className="flex items-center gap-2">
<ArrowRight className="w-4 h-4 group-hover:translate-x-0.5 transition-transform" />
</Link>
</Button>
</div>
{/* Right: visual mock */}
<div className="relative flex items-center justify-center min-h-[320px] lg:min-h-[380px]">
{/* Dark card base */}
<div className="relative w-full max-w-[420px] aspect-[4/3] rounded-2xl bg-surface-dark border border-white/8 shadow-2xl overflow-hidden">
{/* Top bar */}
<div className="flex items-center gap-1.5 px-4 py-3 border-b border-white/8">
<span className="w-2.5 h-2.5 rounded-full bg-red-500/70" />
<span className="w-2.5 h-2.5 rounded-full bg-yellow-500/70" />
<span className="w-2.5 h-2.5 rounded-full bg-green-500/70" />
<div className="ml-3 h-4 bg-white/10 rounded flex-1" />
</div>
{/* Body: chart + cards */}
<div className="p-5 flex flex-col gap-4">
{/* Stat row */}
<div className="grid grid-cols-3 gap-3">
{["日营业额", "交易笔数", "分账金额"].map((label, i) => (
<div key={label} className="bg-white/6 rounded-xl p-3">
<div className={cn("text-lg font-bold text-white mb-0.5", i === 0 && "text-primary")}>
{i === 0 ? "¥48,260" : i === 1 ? "1,203" : "¥12,400"}
</div>
<div className="text-white/40 text-xs">{label}</div>
</div>
))}
</div>
{/* Bar chart */}
<div className="bg-white/6 rounded-xl p-3 flex-1">
<p className="text-white/40 text-xs mb-3"> 7 </p>
<div className="flex items-end gap-2 h-16">
{[55, 70, 45, 88, 65, 92, 78].map((h, i) => (
<div
key={i}
className="flex-1 rounded-sm transition-all"
style={{
height: `${h}%`,
background: i === 5
? `oklch(0.46 0.22 264)`
: `oklch(0.46 0.22 264 / 0.35)`,
}}
/>
))}
</div>
</div>
</div>
{/* Glow overlay */}
<div
className="absolute inset-0 pointer-events-none"
style={{
background: `radial-gradient(ellipse at 80% 20%, oklch(0.46 0.22 264 / 0.12) 0%, transparent 60%)`,
}}
/>
</div>
{/* Floating badge */}
<div className="absolute -bottom-4 -right-4 bg-white border border-border shadow-lg rounded-2xl px-4 py-2.5 flex items-center gap-2">
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
<span className="text-xs font-semibold text-foreground"></span>
</div>
</div>
</div>
</SectionReveal>
</section>
)
}