import type { Metadata, Viewport } from "next"; import localFont from "next/font/local"; import "./globals.css"; import { Suspense } from "react"; import { GoogleAnalyticsWrapper } from "@/components/analytics/GoogleAnalyticsWrapper"; import { GlobalErrorTracker } from "@/components/analytics/GlobalErrorTracker"; import { CookieConsent } from "@/components/analytics/CookieConsent"; import { PerformanceTracker } from "@/components/analytics/PerformanceTracker"; import { OutboundLinkTracker } from "@/components/analytics/OutboundLinkTracker"; import { ScrollDepthTracker } from "@/components/analytics/ScrollDepthTracker"; import { OrganizationSchema, WebsiteSchema, LocalBusinessSchema } from "@/components/seo/structured-data"; import { MobileTabBar } from "@/components/layout/mobile-tab-bar"; 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"; import { getPublishedItems } from "@/lib/cms/data-server"; import { SiteConfigProvider, type SiteConfig, type NavigationItem, type MegaDropdownGroup, type MegaDropdownItem } from "@/lib/site-config"; import { COMPANY_INFO, BRAND_NARRATIVE, type BrandPillar } from "@/lib/constants/company"; import { NAVIGATION_V2, MEGA_DROPDOWN_DATA } from "@/lib/constants/navigation"; /** * 拉丁字形使用 Geist(本地 woff2,无网络请求); * 中文由 globals.css 的 --font-sans 回退到 PingFang SC / 微软雅黑。 * 这样英文数字有设计字体,中文又不需下载数 MB 的 CJK 字重。 * 两者总重 140KB,display: swap 不阻塞首屏。 */ const geistSans = localFont({ src: "./fonts/geist-sans.woff2", variable: "--font-geist-sans", display: "swap", preload: true, }); const geistMono = localFont({ src: "./fonts/geist-mono.woff2", variable: "--font-geist-mono", display: "swap", preload: false, }); export const metadata: Metadata = { metadataBase: new URL("https://www.novalon.cn"), title: { default: "四川睿新致远科技有限公司 - 企业数字化转型服务商", template: "%s | 四川睿新致远科技有限公司", }, description: "四川睿新致远科技有限公司成立于2026年1月,专注于企业数字化转型服务,提供软件开发、云计算、数据分析、信息安全等一站式解决方案。", keywords: ["数字化转型", "企业软件", "ERP系统", "CRM系统", "云计算", "数据分析", "软件开发", "成都科技公司", "金融科技", "睿新致远"], authors: [{ name: "四川睿新致远科技有限公司" }], creator: "四川睿新致远科技有限公司", publisher: "四川睿新致远科技有限公司", robots: { index: true, follow: true, googleBot: { index: true, follow: true, "max-video-preview": -1, "max-image-preview": "large", "max-snippet": -1, }, }, openGraph: { type: "website", locale: "zh_CN", url: "https://www.novalon.cn", siteName: "四川睿新致远科技有限公司", title: "四川睿新致远科技有限公司 - 企业数字化转型服务商", description: "专注于企业数字化转型服务,提供软件开发、云计算、数据分析、信息安全等一站式解决方案", images: [ { url: "/og-image.jpg", width: 1200, height: 630, alt: "四川睿新致远科技有限公司", }, ], }, twitter: { card: "summary_large_image", title: "四川睿新致远科技有限公司 - 企业数字化转型服务商", description: "专注于企业数字化转型服务,提供软件开发、云计算、数据分析、信息安全等一站式解决方案", images: ["/og-image.jpg"], }, alternates: { canonical: "https://www.novalon.cn", }, verification: { google: process.env.NEXT_PUBLIC_GOOGLE_SITE_VERIFICATION || "", }, }; export const viewport: Viewport = { width: "device-width", initialScale: 1, maximumScale: 5, themeColor: "#FFFFFF", }; export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { // Fetch site configuration, navigation, and standalone products from CMS const [navItems, configItems, standaloneItems] = await Promise.all([ getPublishedItems('navigation').catch(() => []), getPublishedItems('site-config').catch(() => []), getPublishedItems('standalone-product').catch(() => []), ]); const navData = navItems[0]?.data as Record | undefined; const configData = configItems[0]?.data as Record | undefined; // Generate standalone product navigation items from CMS data const standaloneNavItems: MegaDropdownItem[] = standaloneItems.map((item) => { const data = item.data as Record; const externalUrl = data.externalUrl as string | undefined; return { id: (data.id as string) || item.slug || '', title: (data.title as string) || item.title, description: (data.description as string) || '', href: externalUrl || `/products/standalone/${item.slug || data.id}`, badge: (data.status as string) === '内测中' ? '内测中' : undefined, }; }); // Build megaDropdown with standalone products dynamically injected const baseMegaDropdown = (navData?.megaDropdown as Record) || MEGA_DROPDOWN_DATA; const megaDropdown: Record = {}; for (const [key, groups] of Object.entries(baseMegaDropdown)) { if (key === 'products' && standaloneNavItems.length > 0) { megaDropdown[key] = groups.map((group) => { if (group.id === 'standalone-products') { return { ...group, description: undefined, items: standaloneNavItems }; } return group; }); } else { megaDropdown[key] = groups; } } const siteConfig: SiteConfig = { name: (configData?.name as string) || COMPANY_INFO.name, shortName: (configData?.shortName as string) || COMPANY_INFO.shortName, displayName: (configData?.displayName as string) || COMPANY_INFO.displayName, slogan: (configData?.slogan as string) || COMPANY_INFO.slogan, description: (configData?.description as string) || COMPANY_INFO.description, valueProposition: (configData?.valueProposition as string) || BRAND_NARRATIVE.valueProposition, positioningStatement: (configData?.positioningStatement as string) || BRAND_NARRATIVE.positioningStatement, brandPromise: (configData?.brandPromise as string) || BRAND_NARRATIVE.brandPromise, toneOfVoice: (configData?.toneOfVoice as string) || BRAND_NARRATIVE.toneOfVoice, pillars: (Array.isArray(configData?.pillars) ? configData.pillars : BRAND_NARRATIVE.pillars) as BrandPillar[], founded: (configData?.founded as string) || COMPANY_INFO.founded, location: (configData?.location as string) || COMPANY_INFO.location, email: (configData?.email as string) || COMPANY_INFO.email, address: (configData?.address as string) || COMPANY_INFO.address, icp: (configData?.icp as string) || COMPANY_INFO.icp, police: (configData?.police as string) || COMPANY_INFO.police, mainNav: (navData?.mainNav as NavigationItem[]) || NAVIGATION_V2, megaDropdown, }; return ( {/* 主题防 FOUC:首帧前同步解析偏好并写入 data-theme,避免刷新闪白/闪黑。 三档语义(与 src/lib/theme.ts 严格一致,改动时两处必须同步): 'light' | 'dark' → 用户显式选择,直接生效 'system' | 无值 → 跟随操作系统 prefers-color-scheme 显式选择优先于系统偏好;localStorage 不可用(隐私模式)时兜底浅色。 */}