281 lines
10 KiB
TypeScript
281 lines
10 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState } from 'react';
|
||
import { MapPin, Phone, Mail } from 'lucide-react';
|
||
import { useTranslations } from 'next-intl';
|
||
|
||
interface FormData {
|
||
name: string;
|
||
phone: string;
|
||
email: string;
|
||
description: string;
|
||
}
|
||
|
||
interface FormErrors {
|
||
name?: string;
|
||
phone?: string;
|
||
email?: string;
|
||
description?: string;
|
||
}
|
||
|
||
const Contact: React.FC = () => {
|
||
const t = useTranslations('Contact');
|
||
const [formData, setFormData] = useState<FormData>({
|
||
name: '',
|
||
phone: '',
|
||
email: '',
|
||
description: '',
|
||
});
|
||
|
||
const [errors, setErrors] = useState<FormErrors>({});
|
||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||
const [submitStatus, setSubmitStatus] = useState<{type: 'success' | 'error', message: string} | null>(null);
|
||
|
||
const validateForm = (): boolean => {
|
||
const newErrors: FormErrors = {};
|
||
|
||
if (!formData.name.trim()) {
|
||
newErrors.name = t('form.errors.nameRequired');
|
||
}
|
||
|
||
const phoneRegex = /^1[3-9]\d{9}$/;
|
||
if (!formData.phone.trim()) {
|
||
newErrors.phone = t('form.errors.phoneRequired');
|
||
} else if (!phoneRegex.test(formData.phone)) {
|
||
newErrors.phone = t('form.errors.phoneInvalid');
|
||
}
|
||
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
if (!formData.email.trim()) {
|
||
newErrors.email = t('form.errors.emailRequired');
|
||
} else if (!emailRegex.test(formData.email)) {
|
||
newErrors.email = t('form.errors.emailInvalid');
|
||
}
|
||
|
||
if (!formData.description.trim()) {
|
||
newErrors.description = t('form.errors.descRequired');
|
||
}
|
||
|
||
setErrors(newErrors);
|
||
return Object.keys(newErrors).length === 0;
|
||
};
|
||
|
||
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||
const { name, value } = e.target;
|
||
setFormData(prev => ({ ...prev, [name]: value }));
|
||
// 清除该字段的错误
|
||
if (errors[name as keyof FormErrors]) {
|
||
setErrors(prev => ({ ...prev, [name]: undefined }));
|
||
}
|
||
};
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
|
||
if (!validateForm()) {
|
||
return;
|
||
}
|
||
|
||
setIsSubmitting(true);
|
||
setSubmitStatus(null);
|
||
|
||
try {
|
||
const response = await fetch('/api/messages', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(formData),
|
||
});
|
||
|
||
const data = await response.json();
|
||
|
||
if (response.ok) {
|
||
setSubmitStatus({
|
||
type: 'success',
|
||
message: data.message || t('form.success')
|
||
});
|
||
// 清空表单
|
||
setFormData({
|
||
name: '',
|
||
phone: '',
|
||
email: '',
|
||
description: '',
|
||
});
|
||
} else {
|
||
setSubmitStatus({
|
||
type: 'error',
|
||
message: data.error || '提交失败,请稍后再试'
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('提交错误:', error);
|
||
setSubmitStatus({
|
||
type: 'error',
|
||
message: '网络错误,请检查网络连接后重试'
|
||
});
|
||
} finally {
|
||
setIsSubmitting(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<section id="contact" className="py-24 bg-feie-dark text-feie-white relative overflow-hidden">
|
||
{/* Abstract background element */}
|
||
<div className="absolute top-0 left-0 w-full h-full opacity-10 pointer-events-none">
|
||
<div className="absolute top-1/2 left-1/4 w-96 h-96 bg-feie-gold rounded-full blur-[120px]"></div>
|
||
</div>
|
||
|
||
<div className="container mx-auto px-6 relative z-10">
|
||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16">
|
||
|
||
<div className="space-y-8">
|
||
<div>
|
||
<h2 className="text-sm font-bold tracking-widest text-feie-gold uppercase mb-3">{t('title')} • {t('subtitle')}</h2>
|
||
<h3 className="text-4xl md:text-5xl font-serif leading-tight">
|
||
{t('heading')} <br/>
|
||
<span className="text-feie-white/50">共创价值</span>
|
||
</h3>
|
||
</div>
|
||
|
||
<p className="text-gray-400 font-light text-lg">
|
||
无论您有项目需求还是技术咨询,我们都随时准备为您提供专业的解答和服务。
|
||
</p>
|
||
|
||
<div className="space-y-6 pt-6">
|
||
<div className="flex items-start gap-4 group">
|
||
<div className="p-3 bg-white/5 rounded-sm group-hover:bg-feie-gold/20 transition-colors border border-white/10">
|
||
<MapPin className="w-6 h-6 text-feie-gold" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">{t('info.address')}</p>
|
||
<p className="text-xl font-serif">{t('info.addressValue')}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-start gap-4 group">
|
||
<div className="p-3 bg-white/5 rounded-sm group-hover:bg-feie-gold/20 transition-colors border border-white/10">
|
||
<Phone className="w-6 h-6 text-feie-gold" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">{t('info.phone')}</p>
|
||
<p className="text-xl font-serif">19844561014</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-start gap-4 group">
|
||
<div className="p-3 bg-white/5 rounded-sm group-hover:bg-feie-gold/20 transition-colors border border-white/10">
|
||
<Mail className="w-6 h-6 text-feie-gold" />
|
||
</div>
|
||
<div>
|
||
<p className="text-sm text-gray-400 uppercase tracking-wider mb-1">{t('info.email')}</p>
|
||
<a href="mailto:feie9454@gmail.com" className="text-xl font-serif hover:text-feie-gold transition-colors">
|
||
feie9454@gmail.com
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-feie-cream p-8 md:p-10 rounded-sm text-feie-dark shadow-2xl">
|
||
<h4 className="text-2xl font-serif mb-6">在线留言</h4>
|
||
|
||
{submitStatus && (
|
||
<div className={`mb-6 p-4 rounded ${
|
||
submitStatus.type === 'success'
|
||
? 'bg-green-100 text-green-800 border border-green-300'
|
||
: 'bg-red-100 text-red-800 border border-red-300'
|
||
}`}>
|
||
{submitStatus.message}
|
||
</div>
|
||
)}
|
||
|
||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div>
|
||
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
|
||
{t('form.name')} <span className="text-red-500">*</span>
|
||
</label>
|
||
<input
|
||
type="text"
|
||
name="name"
|
||
value={formData.name}
|
||
onChange={handleChange}
|
||
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors ${
|
||
errors.name ? 'border-red-500' : 'border-gray-200'
|
||
}`}
|
||
placeholder={t('form.name')}
|
||
/>
|
||
{errors.name && <p className="text-red-500 text-sm mt-1">{errors.name}</p>}
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
|
||
{t('form.phone')} <span className="text-red-500">*</span>
|
||
</label>
|
||
<input
|
||
type="tel"
|
||
name="phone"
|
||
value={formData.phone}
|
||
onChange={handleChange}
|
||
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors ${
|
||
errors.phone ? 'border-red-500' : 'border-gray-200'
|
||
}`}
|
||
placeholder={t('form.phone')}
|
||
/>
|
||
{errors.phone && <p className="text-red-500 text-sm mt-1">{errors.phone}</p>}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
|
||
{t('form.email')} <span className="text-red-500">*</span>
|
||
</label>
|
||
<input
|
||
type="email"
|
||
name="email"
|
||
value={formData.email}
|
||
onChange={handleChange}
|
||
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors ${
|
||
errors.email ? 'border-red-500' : 'border-gray-200'
|
||
}`}
|
||
placeholder="example@email.com"
|
||
/>
|
||
{errors.email && <p className="text-red-500 text-sm mt-1">{errors.email}</p>}
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-bold uppercase tracking-wider text-gray-500 mb-2">
|
||
{t('form.description')} <span className="text-red-500">*</span>
|
||
</label>
|
||
<textarea
|
||
rows={4}
|
||
name="description"
|
||
value={formData.description}
|
||
onChange={handleChange}
|
||
className={`w-full bg-white border-b-2 p-3 focus:border-feie-gold outline-none transition-colors resize-none ${
|
||
errors.description ? 'border-red-500' : 'border-gray-200'
|
||
}`}
|
||
placeholder={t('form.description')}
|
||
></textarea>
|
||
{errors.description && <p className="text-red-500 text-sm mt-1">{errors.description}</p>}
|
||
</div>
|
||
<button
|
||
type="submit"
|
||
disabled={isSubmitting}
|
||
className={`w-full py-4 font-serif font-bold uppercase tracking-widest transition-colors duration-300 ${
|
||
isSubmitting
|
||
? 'bg-gray-400 text-gray-200 cursor-not-allowed'
|
||
: 'bg-feie-dark text-feie-white hover:bg-feie-gold'
|
||
}`}
|
||
>
|
||
{isSubmitting ? t('form.submitting') : t('form.submit')}
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
};
|
||
|
||
export default Contact;
|