feat(app): 全局样式重构与核心应用层更新

- 重构 globals.css 设计令牌系统,对齐咨询风品牌色与排版
- 更新根布局,集成 SEO schema 与黑暗模式防闪烁脚本
- 添加 robots.ts 与 sitemap.ts 动态生成
- 添加 middleware.ts 中间件层
- 更新隐私政策与服务条款页面
This commit is contained in:
张翔
2026-07-07 06:52:19 +08:00
parent cf99e7556c
commit 9053f69123
12 changed files with 1444 additions and 975 deletions
+41 -54
View File
@@ -1,9 +1,6 @@
import type { Metadata, Viewport } from "next";
import localFont from "next/font/local";
import { Ma_Shan_Zheng, Noto_Sans_SC } from "next/font/google";
import "./globals.css";
import { Suspense } from "react";
import { ThemeProvider } from "@/contexts/theme-context";
import { GoogleAnalyticsWrapper } from "@/components/analytics/GoogleAnalyticsWrapper";
import { GlobalErrorTracker } from "@/components/analytics/GlobalErrorTracker";
import { CookieConsent } from "@/components/analytics/CookieConsent";
@@ -16,42 +13,8 @@ import { ErrorBoundary } from "@/components/ui/error-boundary";
import { ScrollProgress } from "@/components/ui/scroll-progress";
import { BackToTop } from "@/components/ui/back-to-top";
import { ClientLayout } from "@/components/layout/client-layout";
const geistSans = localFont({
src: "./fonts/geist-sans.woff2",
variable: "--font-geist-sans",
display: "swap",
preload: false,
});
const geistMono = localFont({
src: "./fonts/geist-mono.woff2",
variable: "--font-geist-mono",
display: "swap",
preload: false,
});
const aoyagiReisho = localFont({
src: "./fonts/AoyagiReisho-subset.ttf",
variable: "--font-aoyagi-reisho",
display: "swap",
preload: true,
});
const maShanZheng = Ma_Shan_Zheng({
weight: "400",
variable: "--font-ma-shan-zheng",
display: "swap",
preload: false,
});
const notoSansSC = Noto_Sans_SC({
weight: ["400", "500", "700"],
variable: "--font-noto-sans-sc",
display: "swap",
subsets: ["latin"],
preload: false,
});
import { getPublishedItems } from "@/lib/cms/data-server";
import { SiteConfigProvider, type SiteConfig, type NavigationItem, type MegaDropdownGroup } from "@/lib/site-config";
export const metadata: Metadata = {
metadataBase: new URL("https://www.novalon.cn"),
@@ -109,25 +72,42 @@ export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
maximumScale: 5,
themeColor: [
{ media: "(prefers-color-scheme: light)", color: "#FFFFFF" },
{ media: "(prefers-color-scheme: dark)", color: "#0A0A0A" },
],
themeColor: "#FFFFFF",
};
export default function RootLayout({
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
// Fetch site configuration and navigation from CMS
const [navItems, configItems] = await Promise.all([
getPublishedItems('navigation').catch(() => []),
getPublishedItems('site-config').catch(() => []),
]);
const navData = navItems[0]?.data as Record<string, unknown> | undefined;
const configData = configItems[0]?.data as Record<string, unknown> | undefined;
const siteConfig: SiteConfig = {
name: (configData?.name as string) || '',
shortName: (configData?.shortName as string) || '',
displayName: (configData?.displayName as string) || '',
slogan: (configData?.slogan as string) || '',
description: (configData?.description as string) || '',
founded: (configData?.founded as string) || '',
location: (configData?.location as string) || '',
email: (configData?.email as string) || '',
address: (configData?.address as string) || '',
icp: (configData?.icp as string) || '',
police: (configData?.police as string) || '',
mainNav: (navData?.mainNav as NavigationItem[]) || [],
megaDropdown: (navData?.megaDropdown as Record<string, MegaDropdownGroup[]>) || {},
};
return (
<html lang="zh-CN" className="scroll-smooth" suppressHydrationWarning>
<head>
<script
dangerouslySetInnerHTML={{
__html: `(function(){try{var t=localStorage.getItem('novalon-theme');if(t==='dark'||(t==='system'||!t)&&window.matchMedia('(prefers-color-scheme:dark)').matches){document.documentElement.setAttribute('data-theme','dark')}}catch(e){}})()`,
}}
/>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
@@ -142,21 +122,28 @@ export default function RootLayout({
<LocalBusinessSchema />
</head>
<body
className={`${geistSans.variable} ${geistMono.variable} ${aoyagiReisho.variable} ${maShanZheng.variable} ${notoSansSC.variable} font-sans antialiased`}
className="font-sans antialiased"
>
<a
href="#main-content"
data-skip-to-content="true"
className="absolute left-[-9999px] top-0 z-50 px-4 py-2 bg-brand text-white rounded-br-lg shadow-lg focus:left-0 focus:outline-none"
>
跳转到主要内容
</a>
<ScrollProgress />
<GoogleAnalyticsWrapper />
<GlobalErrorTracker />
<PerformanceTracker />
<OutboundLinkTracker />
<ScrollDepthTracker />
<ThemeProvider>
<ErrorBoundary>
<ErrorBoundary>
<SiteConfigProvider config={siteConfig}>
<ClientLayout>
{children}
</ClientLayout>
</ErrorBoundary>
</ThemeProvider>
</SiteConfigProvider>
</ErrorBoundary>
<Suspense fallback={null}>
<MobileTabBar />
</Suspense>