feat(public-frontend): 公开前端 UI 视觉升级
CI / Go (api) (pull_request) Failing after 32s
CI / Python (ingestion) (pull_request) Failing after 31s
CI / Migrations (postgres) (pull_request) Failing after 32s

引入统一品牌主题(色阶/字体/阴影/动效),重做首页 hero、卡片、导航与页脚,统一各页面的卡片/输入框/按钮样式。

- tailwind: 新增 brand 色阶、Inter 字体、card/glow 阴影与 fade-up 动效
- index.html: 引入 Inter 字体与 theme-color/description meta
- index.css: 双径向渐变背景 + @layer 组件类(.card/.input/.btn-primary 等)
- App/Home/ProductView/Contribute/ApiDocs/Account: 套用新设计系统

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
rosemariejebbjtxbfp
2026-06-24 03:16:41 +00:00
parent 8f9a03a929
commit d58f46bc80
9 changed files with 297 additions and 151 deletions
+54 -49
View File
@@ -14,6 +14,13 @@ type View =
| { name: "api" }
| { name: "account" };
const NAV: { key: View["name"]; label: string; icon: typeof Search }[] = [
{ key: "home", label: "检索", icon: Search },
{ key: "contribute", label: "贡献档案", icon: PlusCircle },
{ key: "api", label: "API", icon: Code2 },
{ key: "account", label: "API 密钥", icon: KeyRound },
];
export default function App() {
const [view, setView] = useState<View>({ name: "home" });
const [qualified, setQualified] = useState<number | null>(null);
@@ -27,53 +34,46 @@ export default function App() {
return (
<div className="min-h-full flex flex-col">
<header className="bg-white border-b">
<div className="max-w-5xl mx-auto px-4 h-14 flex items-center justify-between">
<header className="sticky top-0 z-30 border-b border-gray-200/70 bg-white/80 backdrop-blur-md">
<div className="max-w-5xl mx-auto px-4 h-16 flex items-center justify-between gap-4">
<button
className="flex items-center gap-2 font-semibold text-gray-800"
className="flex items-center gap-2.5 font-semibold text-gray-800 group"
onClick={() => setView({ name: "home" })}
>
<Boxes className="w-6 h-6 text-emerald-600" />
<span className="text-gray-400 font-normal text-sm"></span>
<span className="grid h-9 w-9 place-items-center rounded-xl bg-gradient-to-br from-brand-500 to-brand-700 text-white shadow-glow transition group-hover:scale-105">
<Boxes className="w-5 h-5" />
</span>
<span className="flex items-baseline gap-1.5">
<span className="text-lg tracking-tight"></span>
<span className="hidden sm:inline text-gray-400 font-normal text-xs">
</span>
</span>
</button>
<nav className="flex items-center gap-1 text-sm">
<button
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 ${
view.name === "home" ? "bg-emerald-50 text-emerald-700" : "text-gray-600 hover:bg-gray-100"
}`}
onClick={() => setView({ name: "home" })}
>
<Search className="w-4 h-4" />
</button>
<button
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 ${
view.name === "contribute" ? "bg-emerald-50 text-emerald-700" : "text-gray-600 hover:bg-gray-100"
}`}
onClick={() => setView({ name: "contribute" })}
>
<PlusCircle className="w-4 h-4" />
</button>
<button
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 ${
view.name === "api" ? "bg-emerald-50 text-emerald-700" : "text-gray-600 hover:bg-gray-100"
}`}
onClick={() => setView({ name: "api" })}
>
<Code2 className="w-4 h-4" /> API
</button>
<button
className={`px-3 py-1.5 rounded-md flex items-center gap-1.5 ${
view.name === "account" ? "bg-emerald-50 text-emerald-700" : "text-gray-600 hover:bg-gray-100"
}`}
onClick={() => setView({ name: "account" })}
>
<KeyRound className="w-4 h-4" /> API
</button>
<nav className="flex items-center gap-0.5 text-sm">
{NAV.map(({ key, label, icon: Icon }) => {
const active =
view.name === key || (key === "home" && view.name === "product");
return (
<button
key={key}
className={`px-3 py-1.5 rounded-lg flex items-center gap-1.5 font-medium transition ${
active
? "bg-brand-50 text-brand-700"
: "text-gray-600 hover:bg-gray-100"
}`}
onClick={() => setView({ name: key } as View)}
>
<Icon className="w-4 h-4" />
<span className="hidden sm:inline">{label}</span>
</button>
);
})}
</nav>
</div>
</header>
<main className="flex-1 max-w-5xl w-full mx-auto px-4 py-6">
<main className="flex-1 max-w-5xl w-full mx-auto px-4 py-8">
{view.name === "home" && (
<Home
onOpen={(id) => setView({ name: "product", id })}
@@ -91,22 +91,27 @@ export default function App() {
{view.name === "account" && <Account />}
</main>
<footer className="border-t bg-white">
<div className="max-w-5xl mx-auto px-4 py-4 text-xs text-gray-400 leading-relaxed text-center">
<footer className="border-t border-gray-200/70 bg-white/60">
<div className="max-w-5xl mx-auto px-4 py-6 text-xs text-gray-400 leading-relaxed text-center">
{qualified != null && (
<div className="mb-2 text-gray-500">
<span className="mx-1 font-semibold text-emerald-600">
<div className="mb-3 inline-flex items-center gap-1.5 rounded-full border border-brand-100 bg-brand-50 px-3 py-1 text-gray-500">
<span className="font-semibold text-brand-600">
{qualified.toLocaleString()}
</span>
</div>
)}
<button onClick={() => setView({ name: "api" })} className="ml-1 text-emerald-600 hover:underline">
API
</button>
<div className="mt-2">
<p className="max-w-2xl mx-auto">
<button
onClick={() => setView({ name: "api" })}
className="ml-1 text-brand-600 font-medium hover:underline"
>
API
</button>
</p>
<div className="mt-3">
<a
href="https://beian.miit.gov.cn/"
target="_blank"