feat: 添加面包屑导航组件并优化页面布局
refactor: 重构页面结构和导航逻辑 fix: 修复移动端菜单导航和滚动行为 perf: 优化图片加载性能和资源请求 test: 添加端到端测试和性能测试用例 docs: 更新.gitignore文件 chore: 更新依赖和配置 style: 优化代码格式和类型安全 ci: 调整Playwright测试超时时间 build: 更新Next.js配置和构建选项
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user