fix(contact): 切换表单服务到 FormSubmit 并改进错误处理
- 将表单提交服务从 Formspree 切换到 FormSubmit(无需 API Key) - 添加成功状态 URL 参数处理(?success=true) - 改进错误提示,区分开发环境和生产环境 - 添加 _next 参数支持成功后重定向 - 重构状态初始化避免 useEffect 中同步调用 setState
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -28,12 +29,18 @@ interface FormErrors {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function ContactPage() {
|
export default function ContactPage() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const isSuccessFromRedirect = searchParams.get('success') === 'true';
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [showToast, setShowToast] = useState(false);
|
const [showToast, setShowToast] = useState(isSuccessFromRedirect);
|
||||||
const [toastMessage, setToastMessage] = useState('');
|
const [toastMessage, setToastMessage] = useState(
|
||||||
const [toastType, setToastType] = useState<'success' | 'error'>('success');
|
isSuccessFromRedirect ? '表单提交成功!我们会尽快与您联系。' : ''
|
||||||
|
);
|
||||||
|
const [toastType, setToastType] = useState<'success' | 'error'>(
|
||||||
|
isSuccessFromRedirect ? 'success' : 'success'
|
||||||
|
);
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const [isSubmitted, setIsSubmitted] = useState(false);
|
const [isSubmitted, setIsSubmitted] = useState(isSuccessFromRedirect);
|
||||||
const [formData, setFormData] = useState<ContactFormData>({
|
const [formData, setFormData] = useState<ContactFormData>({
|
||||||
name: '',
|
name: '',
|
||||||
phone: '',
|
phone: '',
|
||||||
@@ -43,6 +50,7 @@ export default function ContactPage() {
|
|||||||
});
|
});
|
||||||
const [errors, setErrors] = useState<FormErrors>({});
|
const [errors, setErrors] = useState<FormErrors>({});
|
||||||
const sectionRef = useRef<HTMLElement>(null);
|
const sectionRef = useRef<HTMLElement>(null);
|
||||||
|
const hasProcessedSuccess = useRef(isSuccessFromRedirect);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
@@ -50,6 +58,13 @@ export default function ContactPage() {
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccessFromRedirect && !hasProcessedSuccess.current) {
|
||||||
|
hasProcessedSuccess.current = true;
|
||||||
|
window.history.replaceState({}, '', '/contact');
|
||||||
|
}
|
||||||
|
}, [isSuccessFromRedirect]);
|
||||||
|
|
||||||
const validateField = (field: keyof ContactFormData, value: string) => {
|
const validateField = (field: keyof ContactFormData, value: string) => {
|
||||||
try {
|
try {
|
||||||
contactFormSchema.shape[field].parse(value);
|
contactFormSchema.shape[field].parse(value);
|
||||||
@@ -92,21 +107,41 @@ export default function ContactPage() {
|
|||||||
|
|
||||||
setIsSubmitting(true);
|
setIsSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const response = await fetch('https://formspree.io/f/' + process.env.NEXT_PUBLIC_FORMSPREE_ID, {
|
const formEndpoint = `https://formsubmit.co/${COMPANY_INFO.email}`;
|
||||||
|
const formBody = new URLSearchParams();
|
||||||
|
formBody.append('name', formData.name);
|
||||||
|
formBody.append('phone', formData.phone);
|
||||||
|
formBody.append('email', formData.email);
|
||||||
|
formBody.append('subject', formData.subject);
|
||||||
|
formBody.append('message', formData.message);
|
||||||
|
formBody.append('_subject', `网站留言: ${formData.subject}`);
|
||||||
|
formBody.append('_captcha', 'false');
|
||||||
|
formBody.append('_template', 'table');
|
||||||
|
formBody.append('_next', `${window.location.origin}/contact?success=true`);
|
||||||
|
|
||||||
|
const response = await fetch(formEndpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
headers: { 'Accept': 'application/json' },
|
||||||
body: JSON.stringify(formData),
|
body: formBody,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (response.ok && data.success === 'true') {
|
||||||
setIsSubmitted(true);
|
setIsSubmitted(true);
|
||||||
setToastMessage('表单提交成功!我们会尽快与您联系。');
|
setToastMessage('表单提交成功!我们会尽快与您联系。');
|
||||||
setToastType('success');
|
setToastType('success');
|
||||||
setShowToast(true);
|
setShowToast(true);
|
||||||
setFormData({ name: '', phone: '', email: '', subject: '', message: '' });
|
setFormData({ name: '', phone: '', email: '', subject: '', message: '' });
|
||||||
} else {
|
} else {
|
||||||
setToastMessage('提交失败,请稍后重试或直接发送邮件联系我们。');
|
const errorMsg = data.message || '提交失败,请稍后重试或直接发送邮件联系我们。';
|
||||||
|
if (errorMsg.includes('HTML files') || errorMsg.includes('web server')) {
|
||||||
|
setToastMessage('表单服务需要在生产环境激活。部署后首次提交会发送确认邮件到 ' + COMPANY_INFO.email);
|
||||||
setToastType('error');
|
setToastType('error');
|
||||||
|
} else {
|
||||||
|
setToastMessage(errorMsg);
|
||||||
|
setToastType('error');
|
||||||
|
}
|
||||||
setShowToast(true);
|
setShowToast(true);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
Reference in New Issue
Block a user