feat: 添加面包屑导航组件并优化页面布局

refactor: 重构页面结构和导航逻辑

fix: 修复移动端菜单导航和滚动行为

perf: 优化图片加载性能和资源请求

test: 添加端到端测试和性能测试用例

docs: 更新.gitignore文件

chore: 更新依赖和配置

style: 优化代码格式和类型安全

ci: 调整Playwright测试超时时间

build: 更新Next.js配置和构建选项
This commit is contained in:
张翔
2026-02-28 09:09:04 +08:00
parent 9d01e0982f
commit 9451814ca4
60 changed files with 4078 additions and 148 deletions
+88 -5
View File
@@ -1,6 +1,6 @@
'use client';
import { useState, useRef } from 'react';
import { useState, useRef, useEffect } from 'react';
import { useInView } from 'framer-motion';
import { motion } from 'framer-motion';
import { COMPANY_INFO } from '@/lib/constants';
@@ -9,25 +9,73 @@ import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PageHeader } from '@/components/ui/page-header';
import { Breadcrumb } from '@/components/layout/breadcrumb';
import { Mail, Phone, MapPin, Send, Loader2 } from 'lucide-react';
export default function ContactPage() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitResult, setSubmitResult] = useState<{ success: boolean; message?: string; error?: string } | null>(null);
const [mathAnswer, setMathAnswer] = useState('');
const [mathProblem, setMathProblem] = useState({ num1: 0, num2: 0, hash: '', timestamp: 0 });
const contentRef = useRef(null);
const isContentInView = useInView(contentRef, { once: true, margin: '-100px' });
const isSubmitted = submitResult?.success || false;
useEffect(() => {
const num1 = Math.floor(Math.random() * 10) + 1;
const num2 = Math.floor(Math.random() * 10) + 1;
const answer = num1 + num2;
const timestamp = Date.now();
const hash = btoa(`${answer}-${timestamp}`);
setMathProblem({ num1, num2, hash, timestamp });
}, []);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
console.log('Form submission started');
setIsSubmitting(true);
setSubmitResult(null);
const formData = new FormData(e.currentTarget);
const data = Object.fromEntries(formData);
console.log('FormData:', data);
const honeypot = formData.get('website') as string;
if (honeypot) {
console.log('Honeypot triggered');
return;
}
const userAnswer = parseInt(formData.get('mathAnswer') as string);
if (isNaN(userAnswer)) {
setSubmitResult({ success: false, error: '请输入验证码' });
return;
}
const expectedHash = btoa(`${userAnswer}-${mathProblem.timestamp}`);
if (expectedHash !== mathProblem.hash) {
setSubmitResult({ success: false, error: '验证码错误,请重新计算' });
return;
}
const submitTime = formData.get('submitTime') as string;
const timeDiff = Date.now() - parseInt(submitTime);
if (timeDiff < 2000) {
console.log('Too fast submission');
setSubmitResult({ success: false, error: '提交过快,请稍后再试' });
return;
}
setIsSubmitting(true);
setSubmitResult(null);
const submitData = {
...data,
mathHash: mathProblem.hash,
mathTimestamp: mathProblem.timestamp,
mathAnswer: userAnswer,
submitTime: submitTime
};
console.log('FormData:', submitData);
try {
const response = await fetch('/api/contact', {
@@ -35,12 +83,13 @@ export default function ContactPage() {
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
body: JSON.stringify(submitData),
});
console.log('Response status:', response.status);
const result = await response.json();
console.log('Response result:', result);
console.log('Setting submitResult:', result);
setSubmitResult(result);
} catch (error) {
console.error('Form submission error:', error);
@@ -52,6 +101,7 @@ export default function ContactPage() {
return (
<div className="min-h-screen bg-white">
<Breadcrumb items={[{ label: '联系我们', href: '/contact' }]} />
<PageHeader
badge="联系我们"
title="与我们取得联系"
@@ -210,6 +260,39 @@ export default function ContactPage() {
required
/>
</div>
<div className="space-y-3">
<label htmlFor="mathAnswer" className="block text-sm font-medium text-[#1C1C1C] mb-2">
*
</label>
<div className="flex items-center gap-4">
<div className="bg-[#f9f9f9] px-4 py-2 rounded-lg text-[#1C1C1C] font-medium min-w-[120px] text-center">
{mathProblem.num1} + {mathProblem.num2} = ?
</div>
<Input
id="mathAnswer"
name="mathAnswer"
type="number"
placeholder="请输入答案"
value={mathAnswer}
onChange={(e) => setMathAnswer(e.target.value)}
required
className="flex-1"
/>
</div>
</div>
<input
type="hidden"
name="website"
value=""
/>
<input
type="hidden"
name="submitTime"
value={Date.now()}
/>
<Button
type="submit"
className="w-full bg-[#C41E3A] hover:bg-[#A01830] text-white"