Files
novalon-website/src/components/layout/product-header.tsx
T
张翔 e83ecddfe5 refactor(project): 全面清理项目代码并重命名项目
- 移除无用文件和空文件夹,清理 effects 和 scripts 目录
- 将项目从 ruixin-website-react 重命名为 novalon-website-react
- 修复所有测试用例,确保 731 个测试全部通过
- 优化组件导入路径和测试 mock 设置
- 更新项目配置文件和依赖管理

关联任务:项目清理与重构
2026-04-27 12:56:22 +08:00

97 lines
3.1 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import { StaticLink } from '@/components/ui/static-link';
import Image from 'next/image';
import { ArrowLeft, Phone } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { Button } from '@/components/ui/button';
import { RippleButton } from '@/components/ui/ripple-button';
/**
* 产品站专属 Header
*
* 与主站 Header 的区别:
* - 无导航菜单(产品列表、关于我们等)
* - 只保留 Logo + 返回主站按钮
* - 始终浅色毛玻璃风格(适配产品页浅色主题)
* - 更简洁的视觉层次,强化"独立产品站"感知
*/
function ProductHeaderContent() {
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 20);
};
window.addEventListener('scroll', handleScroll, { passive: true });
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
<motion.header
className="fixed top-0 left-0 right-0 z-50 transition-all duration-300"
initial={{ y: -100 }}
animate={{ y: 0 }}
transition={{ duration: 0.5, ease: [0.25, 0.46, 0.45, 0.94] }}
>
<div
className={`transition-all duration-300 ${
isScrolled
? 'bg-white/90 backdrop-blur-xl border-b border-[#E5E5E5] shadow-sm'
: 'bg-transparent'
}`}
>
<div className="container-wide">
<div className="flex items-center justify-between h-16">
<div className="flex items-center gap-3">
{/* Logo */}
<StaticLink href="/" className="flex items-center gap-3 group">
<Image
src="/logo.svg"
alt="Novalon"
width={120}
height={32}
className="h-8 w-auto"
priority
/>
</StaticLink>
{/* 立即咨询 CTA */}
<RippleButton
href="/contact"
className="bg-[#C41E3A] hover:bg-[#A01830] text-white px-5 py-2 rounded-lg text-sm font-semibold inline-flex items-center gap-2"
rippleColor="rgba(255, 255, 255, 0.3)"
>
<Phone className="w-4 h-4" />
<span className="hidden md:inline"></span>
</RippleButton>
</div>
{/* 返回主站按钮 */}
<StaticLink href="/">
<Button
variant="outline"
size="sm"
className="border-[#E5E5E5] text-[#5C5C5C] hover:text-[#1C1C1C] hover:bg-[#F5F5F5] hover:border-[#C41E3A]/30 transition-all duration-200 text-sm"
>
<ArrowLeft className="w-4 h-4 mr-2" />
</Button>
</StaticLink>
</div>
</div>
</div>
</motion.header>
);
}
export function ProductHeader() {
return (
<AnimatePresence mode="wait">
<ProductHeaderContent />
</AnimatePresence>
);
}