CloudBridge/pages/contact.vue
2025-09-04 19:06:49 +08:00

267 lines
9.8 KiB
Vue

<template>
<div>
<!-- Hero Section -->
<section class="aws-gradient text-white section-padding">
<div class="container-custom text-center">
<h1 class="text-5xl font-bold mb-6">
{{ $t('contact.title') }}
</h1>
<p class="text-xl text-white/90 max-w-3xl mx-auto">
{{ $t('contact.subtitle') }}
</p>
</div>
</section>
<!-- Contact Content -->
<section class="section-padding bg-white">
<div class="container-custom">
<div class="grid lg:grid-cols-2 gap-12">
<!-- Contact Form -->
<div>
<h2 class="text-3xl font-bold mb-6">Send us a message</h2>
<form @submit.prevent="submitForm" class="space-y-6">
<div>
<label :for="'name'" class="block text-sm font-medium text-gray-700 mb-2">
{{ $t('contact.form.name') }}
</label>
<input
id="name"
v-model="form.name"
type="text"
required
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-aws-orange focus:border-transparent"
/>
</div>
<div>
<label :for="'email'" class="block text-sm font-medium text-gray-700 mb-2">
{{ $t('contact.form.email') }}
</label>
<input
id="email"
v-model="form.email"
type="email"
required
:class="['w-full px-4 py-3 rounded-lg focus:ring-2 focus:ring-aws-orange focus:border-transparent', emailError ? 'border-red-500' : 'border-gray-300']"
/>
<p v-if="emailError" class="mt-1 text-sm text-red-600">{{ emailError }}</p>
</div>
<div>
<label :for="'subject'" class="block text-sm font-medium text-gray-700 mb-2">
{{ $t('contact.form.subject') }}
</label>
<input
id="subject"
v-model="form.subject"
type="text"
required
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-aws-orange focus:border-transparent"
/>
</div>
<div>
<label :for="'message'" class="block text-sm font-medium text-gray-700 mb-2">
{{ $t('contact.form.message') }}
</label>
<textarea
id="message"
v-model="form.message"
rows="6"
required
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-aws-orange focus:border-transparent"
></textarea>
</div>
<button
type="submit"
:disabled="isSubmitting || !isEmailValid"
class="btn-primary w-full text-lg py-4 disabled:opacity-50 disabled:cursor-not-allowed"
>
<span v-if="isSubmitting">Sending...</span>
<span v-else>{{ $t('contact.form.submit') }}</span>
</button>
</form>
</div>
<!-- Contact Info -->
<div>
<h2 class="text-3xl font-bold mb-6">Get in touch</h2>
<!-- Social Links -->
<div class="space-y-6 mb-8">
<div class="flex items-center space-x-4 p-4 bg-gray-50 rounded-lg">
<div class="w-12 h-12 bg-aws-orange rounded-lg flex items-center justify-center">
<MessageCircle class="w-6 h-6 text-white" />
</div>
<div>
<h3 class="font-semibold text-lg">{{ $t('contact.telegram') }}</h3>
<a
href="https://t.me/pinnovatecloud"
target="_blank"
rel="noopener noreferrer"
class="text-aws-orange hover:underline"
>
@pinnovatecloud
</a>
</div>
</div>
<div class="flex items-center space-x-4 p-4 bg-gray-50 rounded-lg">
<div class="w-12 h-12 bg-aws-orange rounded-lg flex items-center justify-center">
<Phone class="w-6 h-6 text-white" />
</div>
<div>
<h3 class="font-semibold text-lg">{{ $t('contact.whatsapp') }}</h3>
<a
href="https://wa.me/19174029875"
target="_blank"
rel="noopener noreferrer"
class="text-aws-orange hover:underline"
>
+1 9174029875
</a>
</div>
</div>
</div>
<!-- Additional Info -->
<div class="bg-aws-blue text-white p-6 rounded-lg">
<h3 class="text-xl font-semibold mb-4">Why choose us?</h3>
<ul class="space-y-2">
<li class="flex items-center">
<Check class="w-5 h-5 text-aws-orange mr-3 flex-shrink-0" />
<span>24/7 Expert Support</span>
</li>
<li class="flex items-center">
<Check class="w-5 h-5 text-aws-orange mr-3 flex-shrink-0" />
<span>Fast Response Time</span>
</li>
<li class="flex items-center">
<Check class="w-5 h-5 text-aws-orange mr-3 flex-shrink-0" />
<span>Custom Solutions</span>
</li>
<li class="flex items-center">
<Check class="w-5 h-5 text-aws-orange mr-3 flex-shrink-0" />
<span>Competitive Pricing</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</section>
<!-- Toast Notification -->
<transition name="fade">
<div v-if="toast.open" :class="['fixed bottom-6 right-6 z-50 rounded-lg shadow-lg px-5 py-4', toast.type === 'success' ? 'bg-green-600 text-white' : 'bg-red-600 text-white']">
<div class="flex items-start space-x-3">
<svg v-if="toast.type === 'success'" class="w-5 h-5 mt-0.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"/></svg>
<svg v-else class="w-5 h-5 mt-0.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
<div>
<p class="font-semibold">{{ toast.title }}</p>
<p class="text-sm opacity-90">{{ toast.message }}</p>
</div>
<button class="ml-3 text-white/90 hover:text-white" @click="toast.open = false"></button>
</div>
</div>
</transition>
</div>
</template>
<script setup>
import { MessageCircle, Phone, Check } from 'lucide-vue-next'
// i18n
const { t, locale } = useI18n()
// SEO
useSeoMeta({
title: () => t('seo.contact.title'),
description: () => t('seo.contact.description')
})
const config = useRuntimeConfig()
const isSubmitting = ref(false)
const emailError = ref('')
const isEmailValid = computed(() => {
const value = String(form.value.email || '').trim()
// RFC 5322 简化邮箱校验
const ok = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)
emailError.value = ok ? '' : (value ? 'Invalid email format' : '')
return ok
})
const toast = reactive({ open: false, type: 'success', title: '', message: '' })
const form = ref({
name: '',
email: '',
subject: '',
message: ''
})
const submitForm = async () => {
isSubmitting.value = true
try {
if (!isEmailValid.value) {
emailError.value = 'Invalid email format'
throw new Error('invalid_email')
}
const fd = new FormData()
// 更友好的字段标签(供邮件展示)
fd.append('🧑 姓名', form.value.name)
fd.append('✉️ 邮箱', form.value.email)
fd.append('🧩 主题', form.value.subject)
fd.append('💬 消息', form.value.message)
// 兼容性保留原始字段(便于自动解析和统计)
fd.append('name', form.value.name)
fd.append('email', form.value.email)
fd.append('subject', form.value.subject)
fd.append('message', form.value.message)
// 自定义邮件主题/语言/回复地址
fd.append('_subject', `📨 ${t('contact.title')} | ${form.value.subject}`)
fd.append('_language', locale.value)
fd.append('_replyto', form.value.email)
const response = await fetch(config.public.formspreeEndpoint, {
method: 'POST',
headers: { Accept: 'application/json' },
body: fd
})
if (!response.ok) {
throw new Error(`Formspree error: ${response.status}`)
}
// Reset form
form.value = {
name: '',
email: '',
subject: '',
message: ''
}
// Custom toast success
toast.type = 'success'
toast.title = t('contact.title')
toast.message = 'Your message has been sent successfully.'
toast.open = true
} catch (error) {
console.error('Error submitting form:', error)
// Custom toast error
toast.type = 'error'
toast.title = 'Send failed'
toast.message = 'There was an error sending your message. Please try again later.'
toast.open = true
} finally {
isSubmitting.value = false
}
}
</script>
<style scoped>
.fade-enter-active, .fade-leave-active { transition: opacity .2s ease; }
.fade-enter-from, .fade-leave-to { opacity: 0; }
</style>