Files
pangolin/web/website/src/components/Header.jsx
T
wangjia cb8e4bddd8 feat(web): 官网恢复登录入口 → usercenter(app.yanmeiai.com)
usercenter(web/usercenter, Next 静态导出)已部署到 CF Pages(pangolin-usercenter,
自定义域 app.yanmeiai.com)。site.ts 加 usercenter 字段;Header 恢复 .linklogin(t.login)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
2026-07-06 21:48:59 +08:00

85 lines
3.0 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Header.jsx — 交互式顶栏(React island@astrojs/react 渲染 + 水合)。
* 迁自 design/ui_kits/website/index.html 的 <header> + site.js 的菜单/滚动毛玻璃逻辑。
* 语言切换由原型的 JS 文本替换改为「路由跳转」(zh=/, en=/en/),单显不并排(铁律 6)。
*/
import { useEffect, useState } from 'react';
import { Download, Menu } from 'lucide-react';
import { SITE } from '../config/site';
function Mark() {
return (
<svg width="30" height="30" viewBox="0 0 96 96" fill="none" aria-hidden="true">
<g transform="translate(96,0) scale(-1,1)">
<path
d="M16 59 Q17 51 26 50 Q34 31 45 30 Q55 30 62 37 Q69 41 75 45 Q85 49 89 44 Q92 50 84 52 Q75 53 67 53 Q66 62 60 62 L56 62 Q54 55 49 55 Q47 62 37 62 L33 62 Q31 56 26 56 Q20 59 16 59 Z"
fill="#B96A3D"
/>
<g stroke="#FAF3ED" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" fill="none" opacity="0.85">
<path d="M26 50 Q34 43 42 50" />
<path d="M36 48 Q44 41 52 48" />
<path d="M46 48 Q54 41 62 49" />
</g>
<circle cx="23" cy="55" r="2.2" fill="#FAF3ED" />
</g>
</svg>
);
}
export default function Header({ lang = 'zh', t = {} }) {
const [open, setOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 8);
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
const nav = [
['#product', t.product],
['#pricing', t.pricing],
['#download', t.download],
['#docs', t.docs],
];
return (
<header class={`hdr${scrolled ? ' scrolled' : ''}`}>
<div class="wrap row">
<a class="brand" href="#top">
<Mark />
<span class="nm">穿山甲</span>
</a>
<nav class="nav">
{nav.map(([href, label]) => (
<a key={href} href={href}>{label}</a>
))}
</nav>
<div class="right">
<div class="langseg">
<a data-lang="zh" class={lang === 'zh' ? 'on' : undefined} href="/">中文</a>
<a data-lang="en" class={lang === 'en' ? 'on' : undefined} href="/en/">EN</a>
</div>
<a class="linklogin" href={SITE.usercenter}>{t.login}</a>
<a class="btn btn-primary" href="#download">
<Download />
<span>{t.get}</span>
</a>
<button class="menu-btn" onClick={() => setOpen((v) => !v)} aria-label="menu" aria-expanded={open}>
<Menu />
</button>
</div>
</div>
<div class={`mnav${open ? ' open' : ''}`} id="mnav">
{nav.map(([href, label]) => (
<a key={href} href={href} onClick={() => setOpen(false)}>{label}</a>
))}
<a class="btn btn-primary" href="#signup" onClick={() => setOpen(false)}>
<span>{t.suBtn}</span>
</a>
</div>
</header>
);
}