diff --git a/.gitignore b/.gitignore index 808d909..2585c90 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ node_modules/ .next/ out/ .next/cache/ +.next/static/ # Production build/ @@ -50,19 +51,56 @@ htmlcov/ .pytest_cache/ .coverage.* *.cover +.pytest_cache/ +.pytest_cache/ +*.pytest_cache/ +htmlcov/ +.coverage +coverage.xml +*.cover +.hypothesis/ +.mypy_cache/ +.dmypy.json +dmypy.json +.pyre/ +.pytype/ +cython_debug/ + +# Python Virtual Environments +venv/ +env/ +ENV/ +env.bak/ +venv.bak/ +.venv/ +virtualenv/ +virtualenvs/ # Playwright test-results/ playwright-report/ playwright/.cache/ +playwright-report/ +test-results/ +*.traces/ # Testing coverage/ .nyc_output/ +test-results/ +playwright-report/ +test-results/ +*.lcov +coverage/ +.nyc_output/ +test-results/ +playwright-report/ # TypeScript *.tsbuildinfo next-env.d.ts +*.tsbuildinfo +*.d.ts.map # Environment variables .env @@ -71,6 +109,8 @@ next-env.d.ts .env.test.local .env.production.local .env.*.local +*.env +*.env.* # Vercel .vercel/ @@ -82,6 +122,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* +lerna-debug.log* +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* # IDE .idea/ @@ -91,6 +137,17 @@ pnpm-debug.log* *~ *.sublime-workspace *.sublime-project +*.iml +.idea/ +.vscode/ +*.swp +*.swo +*~ +.project +.classpath +.settings/ +.vscode/ +*.code-workspace # OS .DS_Store @@ -100,9 +157,20 @@ pnpm-debug.log* .Trashes Thumbs.db ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ +*.cab +*.msi +*.msix +*.msm +*.msp +*.lnk # Debug *.pem +*.key +*.cert +*.crt # Runtime data pids/ @@ -127,3 +195,82 @@ dist-ssr/ # Trae .trae/ + +# Next.js specific +.next/ +out/ +.swc/ +.vercel/ +.turbo/ + +# Tailwind CSS +*.css.map + +# Framer Motion +*.framer-motion.json + +# Three.js +*.three.json + +# Image optimization +*.avif +*.webp + +# Temporary files +*.tmp +*.temp +*.bak +*.backup +*.swp +*~ +.npm +.eslintcache + +# Test files +test-email.js +test-screenshot.png +hero-check.png +playwright-test-not-portal.js +test-results/ +playwright-report/ + +# CI/CD +.gitlab-ci-local/ +.github/workflows/*.local.yml + +# Documentation +docs/plans/*.md.bak + +# Database +*.db +*.sqlite +*.sqlite3 + +# Archives +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Package manager locks (keep package-lock.json) +yarn.lock +pnpm-lock.yaml + +# Misc +*.pid +*.seed +*.pid.lock +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* +.pnpm-debug.log* + +# Sentry +.sentryclirc +*.sentryclirc diff --git a/docs/plans/2026-02-27-website-comprehensive-optimization.md b/docs/plans/2026-02-27-website-comprehensive-optimization.md new file mode 100644 index 0000000..64fba9d --- /dev/null +++ b/docs/plans/2026-02-27-website-comprehensive-optimization.md @@ -0,0 +1,1420 @@ +# 网站全面优化实施计划 + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** 全面优化睿新致远网站,提升导航一致性、测试覆盖率、错误处理、性能、移动端体验和SEO。 + +**Architecture:** 采用渐进式优化策略,按优先级分阶段实施,每个阶段独立可验证和回滚。 + +**Tech Stack:** Next.js 16.1.6, TypeScript, Framer Motion, Playwright, Tailwind CSS + +--- + +## 阶段一:高优先级修复(1-2天) + +### Task 1.1: 修复导航一致性问题 + +**Files:** +- Create: `src/components/layout/breadcrumb.tsx` +- Modify: `src/components/layout/header.tsx:1-150` +- Modify: `src/app/(marketing)/about/page.tsx:1-15` +- Modify: `src/app/(marketing)/cases/page.tsx:1-20` +- Modify: `src/app/(marketing)/services/page.tsx:1-20` +- Modify: `src/app/(marketing)/products/page.tsx:1-20` +- Modify: `src/app/(marketing)/solutions/page.tsx:1-20` +- Modify: `src/app/(marketing)/contact/page.tsx:1-20` +- Modify: `src/app/news/page.tsx:1-20` + +**Step 1: 创建面包屑导航组件** + +```typescript +// src/components/layout/breadcrumb.tsx +'use client'; + +import Link from 'next/link'; +import { ChevronRight, Home } from 'lucide-react'; + +interface BreadcrumbItem { + label: string; + href: string; +} + +interface BreadcrumbProps { + items: BreadcrumbItem[]; +} + +export function Breadcrumb({ items }: BreadcrumbProps) { + return ( + + ); +} +``` + +**Step 2: 运行测试验证组件创建** + +Run: `npm run build` + +Expected: BUILD SUCCESS + +**Step 3: 在各个页面集成面包屑导航** + +```typescript +// 示例:src/app/(marketing)/about/page.tsx +import { Breadcrumb } from '@/components/layout/breadcrumb'; + +export default function AboutPage() { + return ( +
+ + {/* 其他内容 */} +
+ ); +} +``` + +**Step 4: 更新 Header 组件以支持统一导航** + +```typescript +// src/components/layout/header.tsx +// 修改导航逻辑,使其在首页使用锚点,在详情页使用路由 +const getNavigationHref = (item: typeof NAVIGATION[number], isHomePage: boolean) => { + return isHomePage ? item.href : `/${item.id}`; +}; +``` + +**Step 5: 运行测试验证导航一致性** + +Run: `npm run lint` + +Expected: No linting errors + +**Step 6: 提交代码** + +```bash +git add src/components/layout/breadcrumb.tsx src/components/layout/header.tsx src/app/ +git commit -m "feat: implement consistent navigation with breadcrumbs" +``` + +--- + +### Task 1.2: 补充关键页面的E2E测试 + +**Files:** +- Create: `e2e/src/pages/AboutPage.ts` +- Create: `e2e/src/pages/CasesPage.ts` +- Create: `e2e/src/pages/ServicesPage.ts` +- Create: `e2e/src/pages/ProductsPage.ts` +- Create: `e2e/src/pages/SolutionsPage.ts` +- Create: `e2e/src/pages/NewsPage.ts` +- Create: `e2e/src/tests/smoke/about-page.smoke.spec.ts` +- Create: `e2e/src/tests/smoke/cases-page.smoke.spec.ts` +- Create: `e2e/src/tests/smoke/services-page.smoke.spec.ts` +- Create: `e2e/src/tests/smoke/products-page.smoke.spec.ts` +- Create: `e2e/src/tests/smoke/solutions-page.smoke.spec.ts` +- Create: `e2e/src/tests/smoke/news-page.smoke.spec.ts` + +**Step 1: 创建 AboutPage Page Object** + +```typescript +// e2e/src/pages/AboutPage.ts +import { Page, Locator, expect } from '@playwright/test'; + +export class AboutPage { + readonly page: Page; + readonly breadcrumb: Locator; + readonly pageHeader: Locator; + + constructor(page: Page) { + this.page = page; + this.breadcrumb = page.locator('nav'); + this.pageHeader = page.locator('h1'); + } + + async goto() { + await this.page.goto('/about'); + await this.page.waitForLoadState('networkidle'); + } + + async waitForPageLoad() { + await this.page.waitForLoadState('networkidle'); + await expect(this.pageHeader).toBeVisible(); + } + + async getBreadcrumbItems() { + return await this.breadcrumb.allTextContents(); + } +} +``` + +**Step 2: 创建关于页面冒烟测试** + +```typescript +// e2e/src/tests/smoke/about-page.smoke.spec.ts +import { test, expect } from '../../fixtures/base.fixture'; + +test.describe('关于页面冒烟测试 @smoke', () => { + test.beforeEach(async ({ aboutPage }) => { + await aboutPage.goto(); + await aboutPage.waitForPageLoad(); + }); + + test('应该成功加载关于页面', async ({ aboutPage }) => { + await expect(aboutPage.page).toHaveURL(/\/about/); + await expect(aboutPage.pageHeader).toBeVisible(); + }); + + test('应该显示面包屑导航', async ({ aboutPage }) => { + const items = await aboutPage.getBreadcrumbItems(); + expect(items.length).toBeGreaterThan(0); + }); +}); +``` + +**Step 3: 运行测试验证失败** + +Run: `cd e2e && npm test --grep "关于页面冒烟测试"` + +Expected: FAIL with "aboutPage is not defined" + +**Step 4: 更新 fixtures 以包含新页面** + +```typescript +// e2e/src/fixtures/base.fixture.ts +import { test as base } from '@playwright/test'; +import { AboutPage } from '../pages/AboutPage'; +import { CasesPage } from '../pages/CasesPage'; +import { ServicesPage } from '../pages/ServicesPage'; +import { ProductsPage } from '../pages/ProductsPage'; +import { SolutionsPage } from '../pages/SolutionsPage'; +import { NewsPage } from '../pages/NewsPage'; + +export const test = base.extend<{ + aboutPage: AboutPage; + casesPage: CasesPage; + servicesPage: ServicesPage; + productsPage: ProductsPage; + solutionsPage: SolutionsPage; + newsPage: NewsPage; +}>({ + aboutPage: async ({ page }, use) => { + const aboutPage = new AboutPage(page); + await use(aboutPage); + }, + casesPage: async ({ page }, use) => { + const casesPage = new CasesPage(page); + await use(casesPage); + }, + servicesPage: async ({ page }, use) => { + const servicesPage = new ServicesPage(page); + await use(servicesPage); + }, + productsPage: async ({ page }, use) => { + const productsPage = new ProductsPage(page); + await use(productsPage); + }, + solutionsPage: async ({ page }, use) => { + const solutionsPage = new SolutionsPage(page); + await use(solutionsPage); + }, + newsPage: async ({ page }, use) => { + const newsPage = new NewsPage(page); + await use(newsPage); + }, +}); +``` + +**Step 5: 运行测试验证通过** + +Run: `cd e2e && npm test --grep "关于页面冒烟测试"` + +Expected: PASS + +**Step 6: 为其他页面创建类似的测试** + +重复步骤 1-5,为 CasesPage、ServicesPage、ProductsPage、SolutionsPage、NewsPage 创建 Page Objects 和测试。 + +**Step 7: 提交代码** + +```bash +git add e2e/src/pages/ e2e/src/tests/smoke/ e2e/src/fixtures/base.fixture.ts +git commit -m "test: add smoke tests for all major pages" +``` + +--- + +### Task 1.3: 完善错误处理 + +**Files:** +- Create: `src/app/not-found.tsx` +- Create: `src/app/error.tsx` +- Create: `src/components/ui/error-boundary.tsx` +- Modify: `src/app/layout.tsx:1-50` + +**Step 1: 创建 404 页面** + +```typescript +// src/app/not-found.tsx +import Link from 'next/link'; +import { Button } from '@/components/ui/button'; +import { Home, ArrowRight } from 'lucide-react'; + +export default function NotFound() { + return ( +
+
+

404

+

+ 页面未找到 +

+

+ 抱歉,您访问的页面不存在。 +

+
+ + +
+
+
+ ); +} +``` + +**Step 2: 创建全局错误页面** + +```typescript +// src/app/error.tsx +'use client'; + +import { useEffect } from 'react'; +import { Button } from '@/components/ui/button'; +import Link from 'next/link'; +import { AlertCircle, RefreshCw } from 'lucide-react'; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+
+
+ +
+

+ 出错了 +

+

+ 抱歉,页面加载时发生了错误。请刷新页面或联系我们的技术支持。 +

+
+ + +
+
+
+ ); +} +``` + +**Step 3: 创建错误边界组件** + +```typescript +// src/components/ui/error-boundary.tsx +'use client'; + +import { Component, ReactNode } from 'react'; + +interface ErrorBoundaryProps { + children: ReactNode; + fallback?: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; +} + +export class ErrorBoundary extends Component< + ErrorBoundaryProps, + ErrorBoundaryState +> { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(_: Error): ErrorBoundaryState { + return { hasError: true }; + } + + componentDidCatch(error: Error, errorInfo: any) { + console.error('ErrorBoundary caught an error:', error, errorInfo); + } + + render() { + if (this.state.hasError) { + return this.props.fallback || ( +
+
+

+ 组件加载失败 +

+

+ 抱歉,页面部分内容加载失败。请刷新页面重试。 +

+
+
+ ); + } + + return this.props.children; + } +} +``` + +**Step 4: 在根布局中集成错误边界** + +```typescript +// src/app/layout.tsx +import { ErrorBoundary } from '@/components/ui/error-boundary'; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + + {children} + + + + ); +} +``` + +**Step 5: 运行测试验证错误处理** + +Run: `npm run build` + +Expected: BUILD SUCCESS + +**Step 6: 添加错误处理的E2E测试** + +```typescript +// e2e/src/tests/smoke/error-handling.smoke.spec.ts +import { test, expect } from '@playwright/test'; + +test.describe('错误处理冒烟测试 @smoke', () => { + test('应该显示404页面', async ({ page }) => { + await page.goto('/non-existent-page'); + await expect(page.locator('h1')).toContainText('404'); + await expect(page.locator('h2')).toContainText('页面未找到'); + }); + + test('404页面应该有返回首页按钮', async ({ page }) => { + await page.goto('/non-existent-page'); + const homeButton = page.getByRole('link', { name: /返回首页/ }); + await expect(homeButton).toBeVisible(); + await homeButton.click(); + await expect(page).toHaveURL('/'); + }); +}); +``` + +**Step 7: 提交代码** + +```bash +git add src/app/not-found.tsx src/app/error.tsx src/components/ui/error-boundary.tsx src/app/layout.tsx e2e/src/tests/smoke/error-handling.smoke.spec.ts +git commit -m "feat: implement comprehensive error handling with 404 and error pages" +``` + +--- + +## 阶段二:中优先级优化(1周) + +### Task 2.1: 优化加载性能 + +**Files:** +- Create: `src/components/ui/optimized-image.tsx` +- Modify: `src/components/sections/hero-section.tsx:1-100` +- Modify: `src/components/sections/services-section.tsx:1-100` +- Modify: `src/components/sections/products-section.tsx:1-100` +- Modify: `src/components/sections/cases-section.tsx:1-100` +- Modify: `src/components/sections/news-section.tsx:1-100` +- Modify: `next.config.ts:1-50` + +**Step 1: 创建优化的图片组件** + +```typescript +// src/components/ui/optimized-image.tsx +'use client'; + +import Image from 'next/image'; +import { useState } from 'react'; + +interface OptimizedImageProps { + src: string; + alt: string; + width?: number; + height?: number; + className?: string; + priority?: boolean; +} + +export function OptimizedImage({ + src, + alt, + width = 800, + height = 600, + className, + priority = false, +}: OptimizedImageProps) { + const [isLoading, setIsLoading] = useState(true); + + return ( +
+ {isLoading && ( +
+ )} + {alt} setIsLoading(false)} + sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" + /> +
+ ); +} +``` + +**Step 2: 更新 Next.js 配置以支持图片优化** + +```typescript +// next.config.ts +const nextConfig = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: '**', + }, + ], + formats: ['image/avif', 'image/webp'], + deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], + imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], + }, + // 其他配置... +}; +``` + +**Step 3: 在各个 section 中使用优化的图片组件** + +```typescript +// 示例:src/components/sections/hero-section.tsx +import { OptimizedImage } from '@/components/ui/optimized-image'; + +// 替换所有 标签为 + +``` + +**Step 4: 实现代码分割和懒加载** + +```typescript +// src/app/(marketing)/page.tsx +import dynamic from 'next/dynamic'; + +// 使用动态导入和懒加载 +const HeroSection = dynamic( + () => import('@/components/sections/hero-section').then(mod => ({ default: mod.HeroSection })), + { loading: () => , ssr: true } +); + +const ServicesSection = dynamic( + () => import('@/components/sections/services-section').then(mod => ({ default: mod.ServicesSection })), + { loading: () => , ssr: false } +); +``` + +**Step 5: 添加性能测试** + +```typescript +// e2e/src/tests/performance/image-loading.spec.ts +import { test, expect } from '@playwright/test'; + +test.describe('图片加载性能测试 @performance', () => { + test('应该使用优化的图片格式', async ({ page }) => { + await page.goto('/'); + + const images = await page.locator('img').all(); + for (const image of images) { + const src = await image.getAttribute('src'); + if (src) { + expect(src).toMatch(/\.(avif|webp|jpg|png)$/); + } + } + }); + + test('应该有合理的图片尺寸', async ({ page }) => { + await page.goto('/'); + + const images = await page.locator('img').all(); + for (const image of images) { + const width = await image.getAttribute('width'); + const height = await image.getAttribute('height'); + expect(width).toBeTruthy(); + expect(height).toBeTruthy(); + } + }); +}); +``` + +**Step 6: 运行性能测试** + +Run: `cd e2e && npm test --grep "图片加载性能测试"` + +Expected: PASS + +**Step 7: 提交代码** + +```bash +git add src/components/ui/optimized-image.tsx next.config.ts src/components/sections/ e2e/src/tests/performance/ +git commit -m "perf: implement image optimization and code splitting" +``` + +--- + +### Task 2.2: 改进移动端体验 + +**Files:** +- Modify: `src/components/layout/mobile-menu.tsx:1-200` +- Modify: `src/components/layout/mobile-tab-bar.tsx:1-100` +- Create: `e2e/src/tests/responsive/mobile-ux.spec.ts` + +**Step 1: 优化移动端菜单交互** + +```typescript +// src/components/layout/mobile-menu.tsx +'use client'; + +import { useState, useEffect, useRef } from 'react'; +import { X } from 'lucide-react'; + +export function MobileMenu({ isOpen, onClose, children }: any) { + const menuRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (menuRef.current && !menuRef.current.contains(event.target as Node)) { + onClose(); + } + }; + + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'unset'; + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + document.body.style.overflow = 'unset'; + }; + }, [isOpen, onClose]); + + if (!isOpen) return null; + + return ( +
+
+ +
+ {children} +
+
+
+ ); +} +``` + +**Step 2: 添加移动端触摸优化** + +```typescript +// src/components/ui/touch-button.tsx +'use client'; + +import { Button } from './button'; +import { forwardRef } from 'react'; + +export const TouchButton = forwardRef< + HTMLButtonElement, + React.ComponentProps +>((props, ref) => { + return ( +