layout.tsx 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import type { Metadata, Viewport } from "next";
  2. import { Geist, Geist_Mono } from "next/font/google";
  3. import "./globals.css";
  4. import MatomoTracker from "@/components/MatomoTracker";
  5. import { Toaster } from "@/components/ui/toaster";
  6. import { getSiteUrl } from "@/lib/site";
  7. import { I18nProvider } from "@/lib/i18n/provider";
  8. const geistSans = Geist({
  9. variable: "--font-geist-sans",
  10. subsets: ["latin"],
  11. });
  12. const geistMono = Geist_Mono({
  13. variable: "--font-geist-mono",
  14. subsets: ["latin"],
  15. });
  16. export const metadata: Metadata = {
  17. title: "AI-CS 智能客服系统",
  18. description: "融合 AI 技术与人工客服,为企业提供高效、智能的客户服务解决方案",
  19. };
  20. /** 移动端:正确缩放、刘海屏 safe-area、禁止误触极小字号(仍允许用户双指放大) */
  21. export const viewport: Viewport = {
  22. width: "device-width",
  23. initialScale: 1,
  24. viewportFit: "cover",
  25. themeColor: [
  26. { media: "(prefers-color-scheme: light)", color: "#f8fafc" },
  27. { media: "(prefers-color-scheme: dark)", color: "#0f172a" },
  28. ],
  29. };
  30. // Matomo 容器 URL(格式:container_*.js)
  31. const MATOMO_CONTAINER_URL = process.env.NEXT_PUBLIC_MATOMO_CONTAINER_URL || '';
  32. // 后端端口配置(用于 widget.js)
  33. const BACKEND_PORT = process.env.NEXT_PUBLIC_BACKEND_PORT || '18080';
  34. export default function RootLayout({
  35. children,
  36. }: Readonly<{
  37. children: React.ReactNode;
  38. }>) {
  39. return (
  40. <html lang="zh-CN">
  41. <head>
  42. <script
  43. dangerouslySetInnerHTML={{
  44. __html: `window.AICS_BACKEND_PORT = '${BACKEND_PORT}';`,
  45. }}
  46. />
  47. </head>
  48. <body
  49. className={`${geistSans.variable} ${geistMono.variable} antialiased`}
  50. >
  51. <I18nProvider>{children}</I18nProvider>
  52. <Toaster />
  53. {MATOMO_CONTAINER_URL && <MatomoTracker containerUrl={MATOMO_CONTAINER_URL} />}
  54. </body>
  55. </html>
  56. );
  57. }