- 新增detail-v2组件库(HeroV2/V3, TrustSectionV2, CTASectionV2等) - Hero组件水墨雅致改造:浅色宣纸底/深色墨色文字/墨韵纹理 - 方案卡片添加推荐组合徽标(Package图标) - 独立产品页从V1组件迁移到V2/V3 - 修复why-us-section未使用导入和ESLint引号转义错误
189 lines
5.2 KiB
TypeScript
189 lines
5.2 KiB
TypeScript
'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 (
|
|
<motion.div
|
|
initial={{ opacity: 0, scale: 0.5, rotate: -15 }}
|
|
whileInView={{ opacity: 1, scale: 1, rotate: -12 }}
|
|
viewport={{ once: true }}
|
|
transition={{
|
|
duration: 0.8,
|
|
delay: 0.5,
|
|
ease: [0.34, 1.56, 0.64, 1] as const,
|
|
}}
|
|
className={`relative ${sizes[size]} flex items-center justify-center rounded-lg border-2 font-bold select-none`}
|
|
style={{
|
|
borderColor: color.border,
|
|
backgroundColor: color.bg,
|
|
color: color.text,
|
|
boxShadow: `0 4px 16px ${color.shadow}`,
|
|
fontFamily: "'Noto Serif SC', serif",
|
|
}}
|
|
>
|
|
<div className="absolute inset-[4px] border border-current opacity-20 rounded-md" />
|
|
<span
|
|
className="relative z-10 tracking-widest"
|
|
style={{ writingMode: 'vertical-rl' }}
|
|
>
|
|
{text}
|
|
</span>
|
|
<div
|
|
className="absolute inset-0 rounded-lg opacity-0 hover:opacity-100 transition-opacity duration-500"
|
|
style={{
|
|
background: `conic-gradient(from 0deg, transparent 0%, ${color.border}10 50%, transparent 100%)`,
|
|
animation: 'rotate 8s linear infinite',
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
);
|
|
}
|
|
|
|
interface CalligraphyTextProps {
|
|
children: ReactNode;
|
|
className?: string;
|
|
as?: 'h1' | 'h2' | 'h3' | 'span';
|
|
}
|
|
|
|
export function CalligraphyText({
|
|
children,
|
|
className = '',
|
|
as: Tag = 'span',
|
|
}: CalligraphyTextProps) {
|
|
return (
|
|
<Tag
|
|
className={`font-calligraphy ${className}`}
|
|
style={{
|
|
fontFamily: "'Noto Serif SC', 'STKaiti', 'KaiTi', serif",
|
|
fontWeight: 700,
|
|
}}
|
|
>
|
|
{children}
|
|
</Tag>
|
|
);
|
|
}
|
|
|
|
interface InkDividerProps {
|
|
variant?: 'subtle' | 'bold' | 'decorative';
|
|
}
|
|
|
|
export function InkDivider({ variant = 'subtle' }: InkDividerProps) {
|
|
if (variant === 'subtle') {
|
|
return (
|
|
<div className="flex items-center justify-center gap-4 py-6">
|
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-200 to-transparent" />
|
|
<div className="w-2 h-2 rounded-full bg-gray-300" />
|
|
<div className="flex-1 h-px bg-gradient-to-r from-transparent via-gray-200 to-transparent" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (variant === 'bold') {
|
|
return (
|
|
<div className="flex items-center gap-6 py-8">
|
|
<div className="flex-1 h-0.5 bg-gradient-to-r from-transparent via-gray-300 to-[#C41E3A]" />
|
|
<BrandSeal size="sm" variant="red" text="墨" />
|
|
<div className="flex-1 h-0.5 bg-gradient-to-l from-transparent via-gray-300 to-[#C41E3A]" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex items-center justify-center py-8">
|
|
<svg width="200" height="24" viewBox="0 0 200 24" fill="none" className="opacity-40">
|
|
<path
|
|
d="M0 12 Q 50 4, 100 12 T 200 12"
|
|
stroke="#C41E3A"
|
|
strokeWidth="1"
|
|
fill="none"
|
|
vectorEffect="non-scaling-stroke"
|
|
/>
|
|
<circle cx="100" cy="12" r="3" fill="#C41E3A" opacity="0.6" />
|
|
<circle cx="60" cy="9" r="1.5" fill="#C41E3A" opacity="0.3" />
|
|
<circle cx="140" cy="15" r="1.5" fill="#C41E3A" opacity="0.3" />
|
|
</svg>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
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 (
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 28 }}
|
|
whileInView={{ opacity: 1, y: 0 }}
|
|
viewport={{ once: true, margin: '-80px' }}
|
|
transition={{ duration: 0.75, ease: [0.22, 1, 0.36, 1] as const }}
|
|
className={`mb-16 ${align === 'center' ? 'text-center max-w-3xl mx-auto' : ''}`}
|
|
>
|
|
{(badge || Icon) && (
|
|
<div className={`inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-red-50 text-[#C41E3A] text-sm font-bold tracking-wider uppercase mb-5 ${
|
|
align === 'center' ? '' : ''
|
|
}`}>
|
|
{Icon && <Icon className="w-4 h-4" />}
|
|
{badge}
|
|
</div>
|
|
)}
|
|
|
|
<h2 className="text-3xl md:text-4xl lg:text-5xl font-bold tracking-tight text-gray-900 mb-4">
|
|
{title}
|
|
</h2>
|
|
|
|
{subtitle && (
|
|
<p className="text-lg text-gray-600 leading-relaxed">{subtitle}</p>
|
|
)}
|
|
</motion.div>
|
|
);
|
|
}
|