Files
sui_blog/src/app/layout.tsx
T
胡旭 fae61f924e feat: 浏览量统计、浏览记录追踪、UI色彩优化
- 新增 Post.viewCount 字段,文章详情/列表/精选卡片显示浏览量
- 新增 PageView 模型记录访客IP(脱敏)、设备类型、来源
- 后台新增浏览记录独立页面及侧边栏菜单
- 仪表盘新增访问概览:页面访问/今日/独立IP/设备分布
- 整体色彩体系优化:降低黑白对比度、统一暖色调
- UI细节修复:focus-visible、Footer空列、mobile hover、transition-all等10+项
2026-06-30 09:09:46 +08:00

92 lines
2.8 KiB
TypeScript

import type { Metadata } from "next";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import TrackingProvider from "@/components/TrackingProvider";
import { Noto_Serif_SC, Noto_Sans_SC, Cormorant_Garamond, Geist } from "next/font/google";
import "./globals.css";
import { cn } from "@/lib/utils";
const geist = Geist({subsets:['latin'],variable:'--font-sans'});
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://asui.xyz";
// ── 字体:用 next/font 自托管,避免 @import 阻塞渲染 ──
// Cormorant 仅含拉丁字符,display: swap 防止 FOIT
const cormorant = Cormorant_Garamond({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
style: ["normal", "italic"],
display: "swap",
variable: "--font-cormorant",
});
// Noto Serif SC / Sans SC 体积大,预连接 + swap
const notoSerif = Noto_Serif_SC({
subsets: ["latin"],
weight: ["300", "400", "500", "600", "700"],
display: "swap",
variable: "--font-noto-serif",
preload: false,
});
const notoSans = Noto_Sans_SC({
subsets: ["latin"],
weight: ["300", "400", "500"],
display: "swap",
variable: "--font-noto-sans",
preload: false,
});
export const metadata: Metadata = {
metadataBase: new URL(SITE_URL),
title: {
default: "随 · asui.xyz",
template: "%s | 随",
},
description: "胡旭的个人博客 — 记录技术探索、生活感悟与创业路上的点滴",
keywords: ["博客", "技术", "生活", "创业", "Web开发", "AI", "前端"],
authors: [{ name: "胡旭", url: SITE_URL }],
creator: "胡旭",
openGraph: {
type: "website",
locale: "zh_CN",
url: SITE_URL,
siteName: "随 · asui.xyz",
title: "随 · asui.xyz",
description: "胡旭的个人博客 — 记录技术探索、生活感悟与创业路上的点滴",
},
twitter: {
card: "summary_large_image",
title: "随 · asui.xyz",
description: "胡旭的个人博客 — 记录技术探索、生活感悟与创业路上的点滴",
},
robots: {
index: true,
follow: true,
},
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="zh-CN" className={cn("h-full", notoSerif.variable, notoSans.variable, cormorant.variable, "font-sans", geist.variable)}>
<body className="min-h-full flex flex-col">
<a
href="#main-content"
className="sr-only focus:not-sr-only focus:absolute focus:z-[100] focus:top-3 focus:left-3 focus:px-4 focus:py-2 focus:rounded-lg focus:bg-ink focus:text-cream focus:font-sans focus:text-sm"
>
跳转到主内容
</a>
<Header />
<main id="main-content" className="flex-1">
<TrackingProvider>{children}</TrackingProvider>
</main>
<Footer />
</body>
</html>
);
}