diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b45fcb9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# vscode +.vscode \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..36f0850 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,11 @@ +{ + "singleQuote": true, + "printWidth": 100, + "tabWidth": 4, + "useTabs": false, + "semi": true, + "jsxSingleQuote": false, + "bracketSpacing": true, + "arrowParens": "always", + "endOfLine": "lf" +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a3329a8 --- /dev/null +++ b/README.md @@ -0,0 +1,273 @@ +# HaoAWS官方网站 + +

+ HaoAWS +

+ +一个现代化的多语言企业云服务官网,基于 Next.js 14 构建,支持完全静态导出和多语言国际化。 + +## ✨ 项目特性 + +- 🌍 **多语言支持** - 简体中文、繁体中文、英文 +- 📱 **响应式设计** - 完美适配桌面、平板、手机 +- ⚡ **静态导出** - 支持完全静态化部署,加载速度极快 +- 🔍 **SEO 优化** - 完整的 Meta 标签、结构化数据、多语言 sitemap +- 📰 **新闻系统** - 基于 Markdown 的内容管理系统 +- 🎨 **现代 UI** - 使用 Tailwind CSS 和 ShadCN 组件 +- 🚀 **性能优化** - 代码分割、图片优化、预加载 +- 📦 **部署友好** - 支持 Netlify、Vercel、GitHub Pages 等平台 + +## 🛠️ 技术栈 + +- **前端框架**: [Next.js 14](https://nextjs.org/) (App Router) +- **样式**: [Tailwind CSS](https://tailwindcss.com/) +- **组件**: [ShadCN/UI](https://ui.shadcn.com/) +- **国际化**: 自定义 i18n 解决方案 +- **内容管理**: Markdown + Gray Matter +- **类型安全**: TypeScript +- **代码规范**: ESLint + Prettier + +## 📁 项目结构 + +``` +kejiyun/ +├── app/ # Next.js App Router +│ ├── [locale]/ # 多语言路由 +│ │ ├── about/ # 关于我们页面 +│ │ ├── news/ # 新闻列表和详情 +│ │ ├── products/ # 产品页面 +│ │ ├── support/ # 支持页面 +│ │ └── layout.tsx # 多语言布局 +│ ├── news/[id]/ # 默认语言新闻详情 +│ ├── globals.css # 全局样式 +│ └── layout.tsx # 根布局 +├── components/ # 可复用组件 +│ ├── about/ # 关于页面组件 +│ ├── LanguageSwitcher.tsx # 语言切换器 +│ ├── Layout.tsx # 布局组件 +│ └── SEOPreview.tsx # SEO 预览 +├── data/ # 数据文件 +│ └── locales/ # 多语言翻译文件 +│ ├── en/ # 英文翻译 +│ ├── zh-CN/ # 简体中文翻译 +│ └── zh-TW/ # 繁体中文翻译 +├── docs/ # 文档和新闻内容 +│ └── news/ # 新闻 Markdown 文件 +├── lib/ # 工具函数 +│ ├── i18n.ts # 国际化配置 +│ ├── markdown.ts # Markdown 解析 +│ └── seo.ts # SEO 工具 +├── public/ # 静态资源 +└── middleware.ts # 路由中间件 +``` + +## 🚀 快速开始 + +### 安装依赖 + +```bash +npm install +# 或 +yarn install +``` + +### 开发模式 + +```bash +npm run dev +``` + +打开 [http://localhost:3000](http://localhost:3000) 查看网站。 + +### 构建生产版本 + +```bash +# 普通构建 +npm run build + +# 静态导出构建 +npm run build:static +``` + +### 本地预览静态版本 + +```bash +npm run serve +# 或 +npm run preview +``` + +## 🌍 多语言支持 + +### 支持的语言 + +- **简体中文** (`zh-CN`) - 默认语言,路径: `/` +- **繁体中文** (`zh-TW`) - 路径: `/zh-TW/` +- **英文** (`en`) - 路径: `/en/` + +### 路由示例 + +``` +/ # 简体中文首页 +/about/ # 简体中文关于页面 +/news/article-id/ # 简体中文新闻详情 + +/zh-TW/ # 繁体中文首页 +/zh-TW/about/ # 繁体中文关于页面 +/zh-TW/news/article-id/ # 繁体中文新闻详情 + +/en/ # 英文首页 +/en/about/ # 英文关于页面 +/en/news/article-id/ # 英文新闻详情 +``` + +### 添加新语言 + +1. 在 `lib/i18n.ts` 中添加语言配置 +2. 在 `data/locales/` 中创建对应的翻译文件 +3. 在 `docs/news/` 中添加对应语言的新闻文件 +4. 更新各页面的 `generateStaticParams` 函数 + +## 📰 新闻系统 + +### 新闻文件结构 + +```markdown +--- +title: "文章标题" +description: "文章描述" +date: "2024-01-01" +author: "作者" +tags: ["标签1", "标签2"] +--- + +# 文章内容 + +这里是 Markdown 格式的文章内容... +``` + +### 添加新新闻 + +1. 在 `docs/news/[语言]/` 目录下创建新的 `.md` 文件 +2. 更新 `app/[locale]/news/[id]/page.tsx` 中的 `generateStaticParams` 函数 +3. 重新构建网站 + +## 🔍 SEO 优化 + +### 已实现的 SEO 功能 + +- ✅ 多语言 Meta 标签 +- ✅ Open Graph 标签 +- ✅ Twitter Card 支持 +- ✅ 结构化数据 (JSON-LD) +- ✅ 自动生成 sitemap.xml +- ✅ robots.txt 配置 +- ✅ 语言链接标签 (hreflang) + +### SEO 配置 + +在 `lib/seo.ts` 中配置默认的 SEO 设置,每个页面可以覆盖这些设置。 + +## 📦 部署 + +### 静态部署(推荐) + +#### Netlify +```bash +# 构建命令 +npm run build:static + +# 发布目录 +out +``` + +#### Vercel +```bash +npm install -g vercel +vercel --prod +``` + +#### GitHub Pages +```bash +npm run build:static +# 将 out/ 目录内容推送到 gh-pages 分支 +``` + +### 服务器部署 + +```bash +npm run build +npm start +``` + +## 🎨 自定义和扩展 + +### 添加新页面 + +1. 在 `app/[locale]/` 目录下创建新页面 +2. 添加对应的翻译文件 +3. 更新导航菜单 +4. 添加 `generateStaticParams` 函数(如果需要静态导出) + +### 修改样式 + +项目使用 Tailwind CSS,可以在以下文件中修改样式: +- `app/globals.css` - 全局样式 +- `tailwind.config.ts` - Tailwind 配置 +- 各组件文件中的 className + +### 添加新组件 + +在 `components/` 目录下创建新组件,遵循现有的命名和结构规范。 + +## 🔧 开发指南 + +### 代码规范 + +```bash +# 代码格式化 +npm run format + +# 代码检查 +npm run lint +``` + +### Windows 开发 + +如果在 Windows 上遇到权限问题,请参考 [WINDOWS_BUILD_GUIDE.md](./WINDOWS_BUILD_GUIDE.md)。 + +## 📋 可用脚本 + +```bash +npm run dev # 开发模式 +npm run build # 生产构建 +npm run build:static # 静态导出构建 +npm run build:simple # 简化构建(解决权限问题) +npm run start # 启动生产服务器 +npm run lint # 代码检查 +npm run format # 代码格式化 +npm run serve # 本地预览静态文件 +npm run preview # 构建并预览 +``` + +## 🐛 故障排除 + +### 常见问题 + +1. **构建权限错误** - 参考 [WINDOWS_BUILD_GUIDE.md](./WINDOWS_BUILD_GUIDE.md) +2. **语言切换不工作** - 检查 Cookie 设置和客户端 JavaScript +3. **静态导出失败** - 确保所有动态路由都有 `generateStaticParams` + +### 获取帮助 + +- 查看 [静态部署指南](./STATIC_DEPLOYMENT.md) +- 查看 [Windows 构建指南](./WINDOWS_BUILD_GUIDE.md) +- 检查 Next.js 官方文档 + +## 📄 许可证 + +本项目采用 MIT 许可证。 + +--- + +**提示**: 这是一个完全静态化的网站,构建后可以部署到任何静态文件托管服务,无需 Node.js 服务器环境。 diff --git a/STATIC_DEPLOYMENT.md b/STATIC_DEPLOYMENT.md new file mode 100644 index 0000000..f9280ea --- /dev/null +++ b/STATIC_DEPLOYMENT.md @@ -0,0 +1,166 @@ +# 静态部署指南 + +## 🚀 静态打包命令 + +### 构建静态文件 +```bash +npm run build:static +``` + +### 本地预览静态网站 +```bash +npm run preview +``` + +### 仅构建(不启动服务器) +```bash +npm run build +``` + +### 仅启动静态文件服务器 +```bash +npm run serve +``` + +## 📁 输出目录 + +静态文件将生成在 `out/` 目录中,包含: + +``` +out/ +├── index.html # 首页 (简体中文) +├── zh-TW/ +│ ├── index.html # 繁体中文首页 +│ ├── about/index.html # 关于我们页面 +│ ├── products/index.html # 产品页面 +│ ├── news/ +│ │ ├── index.html # 新闻列表 +│ │ └── [id]/index.html # 新闻详情 +│ └── support/index.html # 支持页面 +├── en/ +│ ├── index.html # 英文首页 +│ └── ... # 其他英文页面 +├── news/ +│ ├── index.html # 默认语言新闻列表 +│ └── [id]/index.html # 默认语言新闻详情 +├── about/index.html # 默认语言关于页面 +├── products/index.html # 默认语言产品页面 +├── support/index.html # 默认语言支持页面 +└── _next/ # Next.js 静态资源 +``` + +## 🌐 部署到不同平台 + +### 1. Netlify +1. 将 `out/` 目录拖拽到 Netlify 控制台 +2. 或连接 Git 仓库,设置构建命令: + ``` + Build command: npm run build:static + Publish directory: out + ``` + +### 2. Vercel +```bash +npx vercel --prod +``` + +### 3. GitHub Pages +1. 将 `out/` 目录内容推送到 `gh-pages` 分支 +2. 在仓库设置中启用 GitHub Pages + +### 4. 服务器部署 +将 `out/` 目录上传到服务器的网站根目录 + +## ⚙️ 配置说明 + +### 基础路径配置 +如果需要部署到子目录,修改 `next.config.mjs`: + +```js +const nextConfig = { + output: 'export', + basePath: '/your-subdirectory', // 取消注释并设置路径 + // ... +}; +``` + +### 图片优化 +静态导出中图片优化已设置为: +```js +images: { + unoptimized: true, // 静态导出必需 +} +``` + +## 🔄 多语言路由说明 + +静态导出后的路由结构: + +- **简体中文 (默认)**:`/`, `/about`, `/news/xxx` +- **繁体中文**:`/zh-TW/`, `/zh-TW/about`, `/zh-TW/news/xxx` +- **英文**:`/en/`, `/en/about`, `/en/news/xxx` + +## ⚠️ 注意事项 + +### 静态导出限制 +1. **无服务器端渲染**:所有页面在构建时生成 +2. **无中间件**:语言切换依赖客户端逻辑 +3. **无动态API路由**:不能使用 `/api` 路由 +4. **无服务器端重定向**:依赖客户端跳转 + +### 语言切换机制 +在静态模式下: +- 中间件不会执行 +- 语言切换完全依赖客户端 JavaScript +- Cookie 设置在客户端进行 + +### 新增文章 +如需添加新文章,需要: +1. 在 `docs/news/` 中添加 Markdown 文件 +2. 更新页面中的 `generateStaticParams` 函数 +3. 重新构建 + +## 🧪 测试 + +### 本地测试 +```bash +# 构建并预览 +npm run preview + +# 访问 http://localhost:3000 +``` + +### 构建验证 +```bash +# 检查构建输出 +npm run build:static +ls -la out/ + +# 验证多语言页面 +ls -la out/zh-TW/ +ls -la out/en/ +``` + +## 🔧 故障排除 + +### 常见问题 + +1. **页面404**:检查 `generateStaticParams` 是否包含所有路由 +2. **样式丢失**:确认 CSS 文件正确引入 +3. **图片不显示**:检查图片路径和 `unoptimized: true` 设置 +4. **语言切换无效**:确认 JavaScript 已启用 + +### 调试步骤 +1. 检查 `out/` 目录结构 +2. 查看浏览器开发者工具控制台 +3. 验证静态文件访问路径 + +## 📈 性能优化 + +- ✅ 自动代码分割 +- ✅ 静态资源优化 +- ✅ CSS 压缩 +- ✅ JavaScript 压缩 +- ✅ 预加载关键资源 + +静态导出的网站加载速度极快,适合 CDN 分发! \ No newline at end of file diff --git a/WINDOWS_BUILD_GUIDE.md b/WINDOWS_BUILD_GUIDE.md new file mode 100644 index 0000000..b6ecdce --- /dev/null +++ b/WINDOWS_BUILD_GUIDE.md @@ -0,0 +1,185 @@ +# Windows 静态构建指南 + +## 🚨 常见权限问题解决方案 + +### 方法一:使用管理员权限 +1. 右键点击 PowerShell 或 CMD +2. 选择"以管理员身份运行" +3. 导航到项目目录:`cd "E:\成品的网站\kejiyun"` +4. 运行构建:`npm run build:static` + +### 方法二:修改杀毒软件设置 +1. 临时关闭实时保护(Windows Defender 或其他杀毒软件) +2. 将项目目录添加到杀毒软件白名单 +3. 重新尝试构建 + +### 方法三:使用简化构建命令 +```bash +# 方法 1 +npm run build:simple + +# 方法 2 +npx next build + +# 方法 3(如果上面的失败) +set NEXT_DISABLE_TELEMETRY=1 +set NODE_ENV=production +npx next build +``` + +## 🔧 手动构建步骤 + +### 1. 清理环境 +```powershell +# 停止所有Node进程 +Get-Process node -ErrorAction SilentlyContinue | Stop-Process -Force + +# 清理缓存目录 +Remove-Item -Path ".next", "out" -Recurse -Force -ErrorAction SilentlyContinue + +# 清理npm缓存 +npm cache clean --force +``` + +### 2. 检查文件权限 +```powershell +# 检查当前目录权限 +Get-Acl . | Format-List + +# 如果需要,修改目录权限 +icacls . /grant $env:USERNAME:F /t +``` + +### 3. 重新安装依赖 +```bash +# 删除node_modules +Remove-Item -Path "node_modules" -Recurse -Force -ErrorAction SilentlyContinue + +# 重新安装 +npm install +``` + +### 4. 构建 +```bash +npm run build:static +``` + +## 🚀 成功构建后的验证 + +构建成功后,您应该看到: + +``` +out/ +├── index.html # ✅ 简体中文首页 +├── about/ +│ └── index.html # ✅ 关于页面 +├── news/ +│ ├── index.html # ✅ 新闻列表 +│ ├── ai-transformation-2024/ +│ ├── cloud-security-best-practices/ +│ └── company-expansion-2024/ +├── products/ +├── support/ +├── zh-TW/ # ✅ 繁体中文版本 +├── en/ # ✅ 英文版本 +└── _next/ # ✅ 静态资源 +``` + +## 🌐 本地预览 + +```bash +# 启动静态服务器 +npm run serve + +# 或者使用Python(如果安装了) +python -m http.server 3000 --directory out + +# 或者使用其他静态服务器 +npx http-server out -p 3000 +``` + +## 🔍 故障排除 + +### 错误:EPERM: operation not permitted +**原因**:文件权限或进程锁定问题 + +**解决方案**: +1. 以管理员身份运行终端 +2. 确保没有其他进程正在使用项目文件 +3. 临时关闭杀毒软件 +4. 重启电脑后重试 + +### 错误:构建过程中卡住 +**解决方案**: +1. 按 `Ctrl+C` 停止进程 +2. 运行:`npm run build:simple` +3. 或使用 `npx next build --debug` + +### 错误:out目录为空或缺少文件 +**检查**: +1. 查看构建日志中的错误信息 +2. 确认 `generateStaticParams` 函数正确 +3. 检查是否有动态导入或服务器端功能 + +### 错误:语言切换不工作 +这是正常的,静态导出模式下: +- 中间件不会执行 +- 语言切换依赖客户端JavaScript +- 需要在浏览器中测试 + +## 📦 部署选项 + +### 1. Netlify(推荐) +```bash +# 方法1:拖拽部署 +# 将整个 out/ 目录拖拽到 https://app.netlify.com/drop + +# 方法2:命令行部署 +npm install -g netlify-cli +netlify deploy --prod --dir=out +``` + +### 2. Vercel +```bash +npm install -g vercel +vercel --prod +``` + +### 3. GitHub Pages +```bash +# 推送 out/ 内容到 gh-pages 分支 +git subtree push --prefix out origin gh-pages +``` + +### 4. 传统服务器 +- 上传 `out/` 目录到服务器的网站根目录 +- 确保服务器支持SPA路由(对于多语言URL) + +## ⚡ 性能优化 + +构建后的静态网站已经包含: +- ✅ 代码分割 +- ✅ 资源压缩 +- ✅ 图片优化 +- ✅ CSS/JS 最小化 +- ✅ 预加载关键资源 + +## 🔄 更新流程 + +当您修改内容后: +1. 更新相应的Markdown或JSON文件 +2. 如果添加新文章,更新 `generateStaticParams` +3. 重新运行 `npm run build:static` +4. 重新部署 `out/` 目录 + +## 📞 需要帮助? + +如果仍然遇到问题: +1. 确保使用管理员权限 +2. 检查Windows版本兼容性 +3. 尝试在不同的文件夹位置重新克隆项目 +4. 考虑使用WSL(Windows Subsystem for Linux) + +--- + +**记住**:静态导出的网站速度极快,完全不需要Node.js服务器,可以部署到任何静态网站托管服务! \ No newline at end of file diff --git a/app/[locale]/about/page.tsx b/app/[locale]/about/page.tsx new file mode 100644 index 0000000..4258b51 --- /dev/null +++ b/app/[locale]/about/page.tsx @@ -0,0 +1,90 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; +import CompanyOverview from '@/components/about/CompanyOverview'; +import CompanyMilestones from '@/components/about/CompanyMilestones'; +import TeamSection from '@/components/about/TeamSection'; +import CertificationsSection from '@/components/about/CertificationsSection'; + +interface AboutPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: AboutPageProps) { + return generateSEOMetadata(params.locale, 'about'); +} + +export default async function AboutPage({ params }: AboutPageProps) { + const { locale } = params; + const [common, about] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'about'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Hero Section */} +
+
+

+ {about.title} +

+

+ {about.subtitle} +

+
+
+ + {/* Company Overview */} + + + {/* Company Milestones */} + + + {/* Team Section */} + + + {/* Certifications */} + +
+ ); +} diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 0000000..4debfc7 --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,14 @@ +import { Locale } from '@/lib/i18n'; + +interface LocaleLayoutProps { + children: React.ReactNode; + params: { + locale: Locale; + }; +} + +export default function LocaleLayout({ children, params }: LocaleLayoutProps) { + // 在动态路由布局中,我们只需要返回children + // 语言属性通过generateMetadata在页面级别设置 + return <>{children}; +} diff --git a/app/[locale]/news/[id]/not-found.tsx b/app/[locale]/news/[id]/not-found.tsx new file mode 100644 index 0000000..1166bb0 --- /dev/null +++ b/app/[locale]/news/[id]/not-found.tsx @@ -0,0 +1,147 @@ +import Link from 'next/link'; +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import Layout from '@/components/Layout'; + +export default async function NewsNotFound() { + // Since we can't access params in not-found.tsx, we'll use the default locale + const locale: Locale = 'zh-CN'; + const common = await getTranslations(locale, 'common'); + const navigationPaths = getNavigationPaths(locale); + + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + +
+
+
+
+ + + +
+ +

+ 404 +

+ +

+ 文章未找到 +

+ +

+ 抱歉,您请求的新闻文章不存在,或者该文章的当前语言版本尚未提供。 +

+
+ +
+

+ 可能的原因: +

+ +
    +
  • + + • + + 文章链接已过期或不正确 +
  • +
  • + + • + + 该文章尚未翻译成当前语言 +
  • +
  • + + • + + 文章已被移除或归档 +
  • +
+ +
+ p.key === 'news')?.path || '/news' + } + className="bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors font-light" + data-oid="c45-.pd" + > + 返回新闻列表 + + + p.key === 'home')?.path || '/'} + className="bg-gray-100 text-gray-700 px-6 py-3 rounded-lg hover:bg-gray-200 transition-colors font-light" + data-oid="vzzstbf" + > + 返回首页 + +
+
+ +
+

+ 如果您认为这是一个错误,请联系我们的技术支持团队。 +

+
+
+
+
+ ); +} diff --git a/app/[locale]/news/[id]/page.tsx b/app/[locale]/news/[id]/page.tsx new file mode 100644 index 0000000..48ba963 --- /dev/null +++ b/app/[locale]/news/[id]/page.tsx @@ -0,0 +1,467 @@ +import { notFound } from 'next/navigation'; +import ReactMarkdown from 'react-markdown'; +import remarkGfm from 'remark-gfm'; +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import { getNewsArticle, getAvailableLocalesForArticle, checkArticleExists } from '@/lib/markdown'; +import Layout from '@/components/Layout'; +import LanguageSwitcher from '@/components/LanguageSwitcher'; +import Link from 'next/link'; + +interface NewsDetailPageProps { + params: { + locale: Locale; + id: string; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + const articles = ['ai-transformation-2024', 'cloud-security-best-practices', 'company-expansion-2024']; + + const params = []; + for (const locale of locales) { + for (const id of articles) { + params.push({ locale, id }); + } + } + return params; +} + +export async function generateMetadata({ params }: NewsDetailPageProps) { + const { locale, id } = params; + + // Check if article exists + if (!checkArticleExists(id, locale)) { + return { + title: 'Article Not Found', + description: 'The requested article could not be found.', + }; + } + + const article = await getNewsArticle(id, locale); + if (!article) { + return { + title: 'Article Not Found', + description: 'The requested article could not be found.', + }; + } + + return { + title: article.title, + description: article.excerpt, + openGraph: { + title: article.title, + description: article.excerpt, + type: 'article', + publishedTime: article.date, + authors: [article.author], + tags: article.tags, + }, + }; +} + +export default async function NewsDetailPage({ params }: NewsDetailPageProps) { + const { locale, id } = params; + + // Check if article exists in the requested locale + if (!checkArticleExists(id, locale)) { + notFound(); + } + + const article = await getNewsArticle(id, locale); + if (!article) { + notFound(); + } + + const [common, availableLocales] = await Promise.all([ + getTranslations(locale, 'common'), + getAvailableLocalesForArticle(id), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Breadcrumb */} +
+
+ +
+
+ + {/* Language Switcher */} +
+
+
+
+ + {locale === 'en' + ? 'Available in:' + : locale === 'zh-TW' + ? '可用語言:' + : '可用语言:'} + + +
+ + {/* Back to News */} + p.key === 'news')?.path || '/news'} + className="text-sm text-blue-600 hover:text-blue-800 flex items-center space-x-1" + data-oid="eatue3o" + > + + + + + {locale === 'en' + ? 'Back to News' + : locale === 'zh-TW' + ? '返回新聞' + : '返回新闻'} + + +
+
+
+ + {/* Article Header */} +
+
+
+
+ + {article.category} + + {article.featured && ( + + {locale === 'en' + ? 'Featured' + : locale === 'zh-TW' + ? '精選' + : '精选'} + + )} +
+ +

+ {article.title} +

+ +
+
+ + + + {article.date} +
+ +
+ + + + {article.author} +
+ +
+ + + + {article.readTime} +
+
+ +

+ {article.excerpt} +

+
+ + {/* Tags */} + {article.tags.length > 0 && ( +
+ {article.tags.map((tag) => ( + + #{tag} + + ))} +
+ )} +
+
+ + {/* Article Content */} +
+
+
+ ( +

+ {children} +

+ ), + + h2: ({ children }) => ( +

+ {children} +

+ ), + + h3: ({ children }) => ( +

+ {children} +

+ ), + + p: ({ children }) => ( +

+ {children} +

+ ), + + ul: ({ children }) => ( +
    + {children} +
+ ), + + ol: ({ children }) => ( +
    + {children} +
+ ), + + li: ({ children }) => ( +
  • + {children} +
  • + ), + + blockquote: ({ children }) => ( +
    + {children} +
    + ), + + code: ({ children }) => ( + + {children} + + ), + + pre: ({ children }) => ( +
    +                                        {children}
    +                                    
    + ), + }} + data-oid="gwxptyo" + > + {article.content} +
    +
    +
    +
    + + {/* Article Footer */} +
    +
    +
    +
    +

    + {locale === 'en' + ? 'Published on' + : locale === 'zh-TW' + ? '發布於' + : '发布于'}{' '} + {article.date} +

    +

    + {locale === 'en' ? 'By' : locale === 'zh-TW' ? '作者:' : '作者:'}{' '} + {article.author} +

    +
    + +
    + +
    +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/news/page.tsx b/app/[locale]/news/page.tsx new file mode 100644 index 0000000..0701942 --- /dev/null +++ b/app/[locale]/news/page.tsx @@ -0,0 +1,380 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; +import Link from 'next/link'; + +interface NewsPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: NewsPageProps) { + return generateSEOMetadata(params.locale, 'news'); +} + +export default async function NewsPage({ params }: NewsPageProps) { + const { locale } = params; + const [common, news] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'news'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + // Separate featured and regular articles + const featuredArticles = news.articles.filter((article: any) => article.featured); + const regularArticles = news.articles.filter((article: any) => !article.featured); + + return ( + + {/* Hero Section */} +
    +
    +

    + {news.title} +

    +

    + {news.subtitle} +

    +
    +
    + + {/* Categories Filter */} +
    +
    +
    + + {news.categories.map((category: any) => ( + + ))} +
    +
    +
    + + {/* Featured Articles */} + {featuredArticles.length > 0 && ( +
    +
    +

    + {locale === 'en' + ? 'Featured Stories' + : locale === 'zh-TW' + ? '精選報導' + : '精选报道'} +

    +
    + {featuredArticles.map((article: any, index: number) => ( +
    + {/* Article Image */} +
    +
    +
    + + {article.category} + +
    +
    + + {locale === 'en' + ? 'Featured' + : locale === 'zh-TW' + ? '精選' + : '精选'} + +
    +
    + +
    +
    + {article.date} + + {article.readTime} +
    + +

    + {article.title} +

    + +

    + {article.excerpt} +

    + + {news.readMore} + + + + +
    +
    + ))} +
    +
    +
    + )} + + {/* Regular Articles */} +
    +
    +

    + {locale === 'en' + ? 'Latest News' + : locale === 'zh-TW' + ? '最新消息' + : '最新消息'} +

    +
    + {regularArticles.map((article: any, index: number) => ( +
    +
    +
    +
    +
    +
    +
    + + {article.category} + + + {article.date} + + + {article.readTime} + +
    + +

    + {article.title} +

    + +

    + {article.excerpt} +

    +
    + + {news.readMore} + + + + + + {locale === 'en' + ? 'By' + : locale === 'zh-TW' + ? '作者:' + : '作者:'}{' '} + {article.author} + +
    +
    +
    +
    + ))} +
    +
    +
    + + {/* Newsletter Subscription */} +
    +
    +

    + {locale === 'en' + ? 'Stay Updated' + : locale === 'zh-TW' + ? '保持更新' + : '保持更新'} +

    +

    + {locale === 'en' + ? 'Subscribe to our newsletter for the latest industry insights and company updates.' + : locale === 'zh-TW' + ? '訂閱我們的電子報,獲取最新的行業洞察和公司動態。' + : '订阅我们的电子报,获取最新的行业洞察和公司动态。'} +

    +
    + + + +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx new file mode 100644 index 0000000..15ad012 --- /dev/null +++ b/app/[locale]/page.tsx @@ -0,0 +1,65 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import { getLatestNewsArticles } from '@/lib/markdown'; +import Layout from '@/components/Layout'; +import HomePageClient from '@/components/HomePageClient'; +import HomePageSEO from '@/components/HomePageSEO'; + +interface HomePageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: HomePageProps) { + return generateSEOMetadata(params.locale, 'home'); +} + +export default async function HomePage({ params }: HomePageProps) { + const { locale } = params; + const [common, home, latestNews] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'home'), + getLatestNewsArticles(locale, 3), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + <> + + + + + + ); +} diff --git a/app/[locale]/products/page.tsx b/app/[locale]/products/page.tsx new file mode 100644 index 0000000..91c44af --- /dev/null +++ b/app/[locale]/products/page.tsx @@ -0,0 +1,266 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; + +interface ProductsPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: ProductsPageProps) { + return generateSEOMetadata(params.locale, 'products'); +} + +export default async function ProductsPage({ params }: ProductsPageProps) { + const { locale } = params; + const [common, products] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'products'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Hero Section */} +
    +
    +

    + {products.title} +

    +

    + {products.subtitle} +

    +
    +
    + + {/* Categories Section */} +
    +
    +
    +

    + {locale === 'en' + ? 'Service Categories' + : locale === 'zh-TW' + ? '服務類別' + : '服务类别'} +

    +
    +
    + {products.categories.map((category: any, index: number) => ( +
    +
    + {category.icon} +
    +

    + {category.name} +

    +

    + {category.description} +

    +
    + ))} +
    +
    +
    + + {/* Products Grid */} +
    +
    +
    +

    + {locale === 'en' + ? 'Our Solutions' + : locale === 'zh-TW' + ? '我們的解決方案' + : '我们的解决方案'} +

    +
    +
    + {products.items.map((product: any, index: number) => ( +
    + {/* Product Icon/Image */} +
    + + {product.icon || '💼'} + +
    + +
    +

    + {product.title} +

    +

    + {product.description} +

    + + {/* Features */} +
    +

    + {locale === 'en' + ? 'Key Features:' + : locale === 'zh-TW' + ? '主要功能:' + : '主要功能:'} +

    +
    + {product.features + .slice(0, 4) + .map((feature: string, featureIndex: number) => ( + + {feature} + + ))} + {product.features.length > 4 && ( + + +{product.features.length - 4}{' '} + {locale === 'en' + ? 'more' + : locale === 'zh-TW' + ? '更多' + : '更多'} + + )} +
    +
    + + {/* CTA Button */} + +
    +
    + ))} +
    +
    +
    + + {/* CTA Section */} +
    +
    +

    + {locale === 'en' + ? 'Ready to Transform Your Business?' + : locale === 'zh-TW' + ? '準備好轉型您的業務了嗎?' + : '准备好转型您的业务了吗?'} +

    +

    + {locale === 'en' + ? 'Contact our experts to discuss your specific needs and get a customized solution.' + : locale === 'zh-TW' + ? '聯絡我們的專家討論您的具體需求,獲得客製化解決方案。' + : '联系我们的专家讨论您的具体需求,获得定制化解决方案。'} +

    +
    + + +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/support/page.tsx b/app/[locale]/support/page.tsx new file mode 100644 index 0000000..d28b658 --- /dev/null +++ b/app/[locale]/support/page.tsx @@ -0,0 +1,259 @@ +import { getTranslations, Locale, getNavigationPaths } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; +import Layout from '@/components/Layout'; + +interface SupportPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: SupportPageProps) { + return generateSEOMetadata(params.locale, 'support'); +} + +export default async function SupportPage({ params }: SupportPageProps) { + const { locale } = params; + const [common, support] = await Promise.all([ + getTranslations(locale, 'common'), + getTranslations(locale, 'support'), + ]); + + const navigationPaths = getNavigationPaths(locale); + const navigation = [ + { + name: common.navigation.home, + href: navigationPaths.find((p) => p.key === 'home')?.path || '/', + }, + { + name: common.navigation.products, + href: navigationPaths.find((p) => p.key === 'products')?.path || '/products', + }, + { + name: common.navigation.news, + href: navigationPaths.find((p) => p.key === 'news')?.path || '/news', + }, + { + name: common.navigation.support, + href: navigationPaths.find((p) => p.key === 'support')?.path || '/support', + }, + { + name: common.navigation.about, + href: navigationPaths.find((p) => p.key === 'about')?.path || '/about', + }, + ]; + + return ( + + {/* Hero Section */} +
    +
    +

    + {support.title} +

    +

    + {support.subtitle} +

    +
    +
    + + {/* Contact Methods */} +
    +
    +
    +

    + {support.sections.contact.title} +

    +

    + {support.sections.contact.subtitle} +

    +
    +
    + {support.sections.contact.methods.map((method: any, index: number) => ( +
    +
    + {method.icon} +
    +

    + {method.title} +

    +

    + {method.value} +

    +

    + {method.description} +

    +
    + ))} +
    +
    +
    + + {/* Help Resources */} +
    +
    +
    +

    + {support.sections.resources.title} +

    +

    + {support.sections.resources.subtitle} +

    +
    +
    + {support.sections.resources.items.map((resource: any, index: number) => ( +
    +
    + {resource.icon} +
    +

    + {resource.title} +

    +

    + {resource.description} +

    +
    + ))} +
    +
    +
    + + {/* FAQ Section */} +
    +
    +
    +

    + {support.sections.faq.title} +

    +

    + {support.sections.faq.subtitle} +

    +
    +
    + {support.sections.faq.items.map((item: any, index: number) => ( +
    +

    + + Q + + {item.question} +

    +
    +

    + {item.answer} +

    +
    +
    + ))} +
    +
    +
    + + {/* Contact CTA */} +
    +
    +

    + {locale === 'en' + ? 'Still Need Help?' + : locale === 'zh-TW' + ? '仍需要幫助?' + : '仍需要帮助?'} +

    +

    + {locale === 'en' + ? 'Our support team is ready to assist you with any questions or issues.' + : locale === 'zh-TW' + ? '我們的支援團隊隨時準備協助您解決任何問題。' + : '我们的支持团队随时准备协助您解决任何问题。'} +

    +
    + + +
    +
    +
    +
    + ); +} diff --git a/app/[locale]/test-seo/page.tsx b/app/[locale]/test-seo/page.tsx new file mode 100644 index 0000000..1741e5b --- /dev/null +++ b/app/[locale]/test-seo/page.tsx @@ -0,0 +1,34 @@ +import { Locale } from '@/lib/i18n'; +import { generateMetadata as generateSEOMetadata } from '@/lib/seo'; + +interface TestSEOPageProps { + params: { + locale: Locale; + }; +} + +export async function generateStaticParams() { + const locales = ['zh-CN', 'zh-TW', 'en']; + return locales.map((locale) => ({ + locale, + })); +} + +export async function generateMetadata({ params }: TestSEOPageProps) { + return generateSEOMetadata(params.locale, 'home'); +} + +export default function TestSEOPage({ params }: TestSEOPageProps) { + const { locale } = params; + + return ( +
    +

    SEO测试页面 - {locale}

    +

    检查页面的title、description和keywords是否正确显示

    +
    +

    当前语言: {locale}

    +

    请查看浏览器标题栏和页面源代码中的meta标签

    +
    +
    + ); +} diff --git a/app/about/page.tsx b/app/about/page.tsx new file mode 100644 index 0000000..0efb8f6 --- /dev/null +++ b/app/about/page.tsx @@ -0,0 +1,7 @@ +import { redirect } from 'next/navigation'; +import { defaultLocale } from '@/lib/i18n'; + +export default function AboutPage() { + // Redirect to the localized version + redirect(`/${defaultLocale}/about`); +} diff --git a/app/favicon.ico b/app/favicon.ico new file mode 100644 index 0000000..08f2470 Binary files /dev/null and b/app/favicon.ico differ diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..48bb368 --- /dev/null +++ b/app/globals.css @@ -0,0 +1,168 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + } +} + +@layer utilities { + .text-balance { + text-wrap: balance; + } + + /* Line clamp utilities for text truncation */ + .line-clamp-2 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + } + + .line-clamp-3 { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + } +} + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 0 0% 3.9%; + --card: 0 0% 100%; + --card-foreground: 0 0% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 0 0% 3.9%; + --primary: 0 0% 9%; + --primary-foreground: 0 0% 98%; + --secondary: 0 0% 96.1%; + --secondary-foreground: 0 0% 9%; + --muted: 0 0% 96.1%; + --muted-foreground: 0 0% 45.1%; + --accent: 0 0% 96.1%; + --accent-foreground: 0 0% 9%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 89.8%; + --input: 0 0% 89.8%; + --ring: 0 0% 3.9%; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + --radius: 0.5rem; + } + .dark { + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; + --card: 0 0% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 0 0% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 0 0% 9%; + --secondary: 0 0% 14.9%; + --secondary-foreground: 0 0% 98%; + --muted: 0 0% 14.9%; + --muted-foreground: 0 0% 63.9%; + --accent: 0 0% 14.9%; + --accent-foreground: 0 0% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 0% 98%; + --border: 0 0% 14.9%; + --input: 0 0% 14.9%; + --ring: 0 0% 83.1%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +/* 客服按钮样式优化 - 适配网页风格 */ +/* 使用更高优先级的选择器来覆盖第三方样式 */ +body [id*="hecong"] iframe, +body [class*="hecong"] iframe, +body iframe[src*="ahc.ink"], +body div[style*="fixed"] iframe, +body div[style*="bottom"] iframe, +html [id*="hecong"] iframe, +html [class*="hecong"] iframe, +html iframe[src*="ahc.ink"] { + border-radius: 12px !important; + box-shadow: 0 2px 12px rgba(79, 156, 249, 0.15) !important; + border: 1px solid rgba(79, 156, 249, 0.2) !important; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; + background-color: #4F9CF9 !important; +} + +/* 悬停效果 - 增强选择器优先级 */ +body [id*="hecong"] iframe:hover, +body [class*="hecong"] iframe:hover, +body iframe[src*="ahc.ink"]:hover, +body div[style*="fixed"] iframe:hover, +body div[style*="bottom"] iframe:hover, +html [id*="hecong"] iframe:hover, +html [class*="hecong"] iframe:hover, +html iframe[src*="ahc.ink"]:hover { + transform: translateY(-2px) !important; + box-shadow: 0 4px 20px rgba(79, 156, 249, 0.3) !important; + border-color: rgba(79, 156, 249, 0.5) !important; + background-color: #60A5FA !important; +} + +/* 确保客服组件在合适的层级 */ +body [id*="hecong"], +body [class*="hecong"], +html [id*="hecong"], +html [class*="hecong"] { + z-index: 1000 !important; +} + +/* 客服聊天窗口样式优化 */ +body [id*="hecong-chat"], +body [class*="hecong-chat"], +body [id*="chat-window"], +body [class*="chat-window"], +html [id*="hecong-chat"], +html [class*="hecong-chat"], +html [id*="chat-window"], +html [class*="chat-window"] { + border-radius: 16px !important; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1) !important; + border: 1px solid rgba(229, 231, 235, 0.8) !important; +} + +/* 针对可能的其他客服按钮选择器 */ +div[style*="position: fixed"][style*="bottom"][style*="right"] iframe, +div[style*="position:fixed"][style*="bottom"][style*="right"] iframe, +[style*="position: fixed"][style*="z-index"] iframe { + border-radius: 12px !important; + box-shadow: 0 2px 12px rgba(79, 156, 249, 0.15) !important; + border: 1px solid rgba(79, 156, 249, 0.2) !important; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; + background-color: #4F9CF9 !important; +} diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..6aab683 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,198 @@ +import type { Metadata } from 'next'; +import Script from 'next/script'; +import './globals.css'; + +export const metadata: Metadata = { + title: { + template: '%s | HaoAWS', + default: 'HaoAWS - 领先的AWS云服务与数字化转型解决方案提供商', + }, + description: + 'HaoAWS专注AWS云服务与企业数字化转型,提供专业的云计算解决方案。服务800+成功案例,150+AWS专业团队,助力企业实现云端升级。', + keywords: [ + 'AWS云服务', + '企业数字化转型', + '云计算迁移服务', + '云架构设计', + '数据分析平台', + ], + + authors: [{ name: 'HaoAWS', url: 'https://haoaws.com' }], + creator: 'HaoAWS', + publisher: 'HaoAWS', + formatDetection: { + email: false, + address: false, + telephone: false, + }, + metadataBase: new URL('https://haoaws.com'), + alternates: { + canonical: '/', + languages: { + 'zh-CN': '/', + 'zh-TW': '/zh-TW', + en: '/en', + }, + }, + openGraph: { + type: 'website', + locale: 'zh_CN', + url: 'https://haoaws.com', + siteName: 'HaoAWS', + title: 'HaoAWS - 领先的AWS云服务与数字化转型解决方案提供商', + description: + 'HaoAWS专注AWS云服务与企业数字化转型,提供专业的云计算解决方案。服务800+成功案例,150+AWS专业团队,助力企业实现云端升级。', + images: [ + { + url: '/og-image.jpg', + width: 1200, + height: 630, + alt: 'HaoAWS - AWS云服务与数字化转型解决方案专家', + }, + ], + }, + twitter: { + card: 'summary_large_image', + title: 'HaoAWS - 领先的AWS云服务与数字化转型解决方案提供商', + description: 'HaoAWS专注AWS云服务与企业数字化转型,提供专业的云计算解决方案。', + images: ['/og-image.jpg'], + creator: '@haoaws', + }, + robots: { + index: true, + follow: true, + googleBot: { + index: true, + follow: true, + 'max-video-preview': -1, + 'max-image-preview': 'large', + 'max-snippet': -1, + }, + }, + verification: { + google: 'your-google-verification-code', + yandex: 'your-yandex-verification-code', + yahoo: 'your-yahoo-verification-code', + }, +}; + +export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) { + return ( + + + + + + + + + + + + + + + {children} + + {/* Google Analytics */} +