复评(impeccable critique)全部 Priority Issues 与 Minor Observations 落地: - P0 数字口径:MetricsBasisNote 单一真源 + metrics-basis.test fs 防线,接入 products/solutions/services/home/about;纯渲染端加角注,不动 DB/seed 值 - P1 转化断头路:敬请期待态 + 404 语境出口 + services 空态双出口 - P1 首页并卡:TrustSection 早鸟横幅并入 CasesSection 单张深色共创卡 - P2 CTA 治理:CONSULT_CTA_ALLOWLIST 白名单 + 逐字符扫描器,/contact CTA 全归一 - P3 polish:footer 补 服务/公司列、contact 死三元/空槽/toast、GlobalErrorTracker 幂等日志 - 文档同源:PRODUCT/CONTEXT 动效带统一 180–280ms;DESIGN.md 立 The 10px Floor; font-floor.test 守卫禁止再引入 <10px(保留 10/11px 有意 Bain 微标签) Gates: type-check 0 · jest 1581/132 · lint 0e/126w · contrast 7/7 · headings 10/10
86 lines
3.2 KiB
TypeScript
86 lines
3.2 KiB
TypeScript
'use client';
|
|
|
|
import { StaticLink } from '@/components/ui/static-link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { Home, Lightbulb, Package, FolderOpen, User } from 'lucide-react';
|
|
import { motion } from 'framer-motion';
|
|
import { cn } from '@/lib/utils';
|
|
|
|
// 关于页让位给案例页:转化路径上「看实证」优先于「看介绍」,
|
|
// 关于/新闻等仍可通过汉堡抽屉(header)到达。
|
|
const tabs = [
|
|
{ id: 'home', label: '首页', href: '/', icon: Home },
|
|
{ id: 'products', label: '产品', href: '/products', icon: Package },
|
|
{ id: 'solutions', label: '方案', href: '/solutions', icon: Lightbulb },
|
|
{ id: 'cases', label: '案例', href: '/cases', icon: FolderOpen },
|
|
{ id: 'contact', label: '联系', href: '/contact', icon: User },
|
|
];
|
|
|
|
export function MobileTabBar() {
|
|
const pathname = usePathname();
|
|
|
|
const isActive = (_href: string, id: string) => {
|
|
if (id === 'home') {
|
|
return pathname === '/';
|
|
}
|
|
if (id === 'contact') {
|
|
return pathname === '/contact';
|
|
}
|
|
if (id === 'products') {
|
|
return pathname === '/products' || pathname.startsWith('/products/');
|
|
}
|
|
if (id === 'solutions') {
|
|
return pathname === '/solutions' || pathname.startsWith('/solutions/');
|
|
}
|
|
if (id === 'cases') {
|
|
return pathname === '/cases' || pathname.startsWith('/cases/');
|
|
}
|
|
return false;
|
|
};
|
|
|
|
return (
|
|
<nav className="fixed bottom-0 left-0 right-0 z-50 md:hidden bg-bg-primary/95 backdrop-blur-xl border-t border-border-primary" style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}>
|
|
<div className="flex items-center justify-around h-16">
|
|
{tabs.map((tab) => {
|
|
const Icon = tab.icon;
|
|
const active = isActive(tab.href, tab.id);
|
|
|
|
return (
|
|
<StaticLink
|
|
key={tab.id}
|
|
href={tab.href}
|
|
className="flex flex-col items-center justify-center flex-1 h-full relative group min-h-12"
|
|
>
|
|
<div className="relative flex flex-col items-center justify-center py-2">
|
|
<Icon
|
|
className={cn(
|
|
'w-6 h-6 transition-colors duration-300',
|
|
active ? 'text-brand-ink' : 'text-text-muted group-hover:text-text-secondary'
|
|
)}
|
|
/>
|
|
<span
|
|
className={cn(
|
|
'text-xs mt-1 transition-colors duration-300',
|
|
active ? 'text-brand-ink font-medium' : 'text-text-muted'
|
|
)}
|
|
>
|
|
{tab.label}
|
|
</span>
|
|
{active && (
|
|
/* critique P2(2026-09-19):双下划线为同状态冗余编码,只保留 tab 惯例的
|
|
底部指示条;spring 违反「内容入场禁回弹」,改 ease-ink 200ms。 */
|
|
<motion.div
|
|
layoutId="activeTab"
|
|
className="absolute -bottom-1 w-8 h-0.5 bg-brand"
|
|
transition={{ duration: 0.2, ease: [0.22, 1, 0.36, 1] }}
|
|
/>
|
|
)}
|
|
</div>
|
|
</StaticLink>
|
|
);
|
|
})}
|
|
</div>
|
|
</nav>
|
|
);
|
|
}
|