基本完成静态页面
This commit is contained in:
217
src/components/ui/ContactForm.vue
Normal file
217
src/components/ui/ContactForm.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
// 表单数据
|
||||
const formData = ref({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
message: ''
|
||||
})
|
||||
|
||||
// 错误信息
|
||||
const errors = ref({
|
||||
name: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
message: ''
|
||||
})
|
||||
|
||||
// 表单验证
|
||||
const validateForm = () => {
|
||||
let isValid = true
|
||||
errors.value = { name: '', email: '', phone: '', message: '' }
|
||||
|
||||
// 姓名验证
|
||||
if (!formData.value.name.trim()) {
|
||||
errors.value.name = '请输入姓名'
|
||||
isValid = false
|
||||
} else if (formData.value.name.length < 2) {
|
||||
errors.value.name = '姓名至少需要2个字符'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// 邮箱验证
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
if (!formData.value.email) {
|
||||
errors.value.email = '请输入邮箱'
|
||||
isValid = false
|
||||
} else if (!emailRegex.test(formData.value.email)) {
|
||||
errors.value.email = '请输入有效的邮箱地址'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// 电话验证
|
||||
const phoneRegex = /^1[3-9]\d{9}$/
|
||||
if (!formData.value.phone) {
|
||||
errors.value.phone = '请输入电话'
|
||||
isValid = false
|
||||
} else if (!phoneRegex.test(formData.value.phone)) {
|
||||
errors.value.phone = '请输入有效的手机号码'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// 留言验证
|
||||
if (!formData.value.message.trim()) {
|
||||
errors.value.message = '请输入留言内容'
|
||||
isValid = false
|
||||
} else if (formData.value.message.length < 10) {
|
||||
errors.value.message = '留言内容至少需要10个字符'
|
||||
isValid = false
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const submitForm = (event: Event) => {
|
||||
event.preventDefault()
|
||||
|
||||
if (validateForm()) {
|
||||
// 在控制台打印表单值
|
||||
console.log('表单提交数据:', JSON.parse(JSON.stringify(formData.value)))
|
||||
|
||||
// 这里可以添加实际的提交逻辑,如API请求
|
||||
// axios.post('/api/submit', formData.value)
|
||||
|
||||
// 提交成功后重置表单
|
||||
formData.value = { name: '', email: '', phone: '', message: '' }
|
||||
alert('表单提交成功!')
|
||||
} else {
|
||||
console.log('表单验证失败:', errors.value)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<form class="space-y-6" @submit="submitForm">
|
||||
<!-- 姓名 -->
|
||||
<div data-aos="fade-up" data-aos-delay="200">
|
||||
<label for="name" class="block text-sm font-medium text-blue-200 mb-1">姓名</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
v-model="formData.name"
|
||||
class="w-full px-4 py-2 rounded-lg border border-blue-200 focus:outline-none focus:ring-2 focus:ring-blue-400 bg-blue-900/50 text-white"
|
||||
placeholder="请输入您的姓名"
|
||||
:class="{ 'border-red-500': errors.name }"
|
||||
>
|
||||
<div v-if="errors.name" class="text-red-400 text-sm mt-1">{{ errors.name }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 邮箱 -->
|
||||
<div data-aos="fade-up" data-aos-delay="250">
|
||||
<label for="email" class="block text-sm font-medium text-blue-200 mb-1">邮箱</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
v-model="formData.email"
|
||||
class="w-full px-4 py-2 rounded-lg border border-blue-200 focus:outline-none focus:ring-2 focus:ring-blue-400 bg-blue-900/50 text-white"
|
||||
placeholder="请输入您的邮箱"
|
||||
:class="{ 'border-red-500': errors.email }"
|
||||
>
|
||||
<div v-if="errors.email" class="text-red-400 text-sm mt-1">{{ errors.email }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 电话 -->
|
||||
<div data-aos="fade-up" data-aos-delay="300">
|
||||
<label for="phone" class="block text-sm font-medium text-blue-200 mb-1">电话</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="phone"
|
||||
v-model="formData.phone"
|
||||
class="w-full px-4 py-2 rounded-lg border border-blue-200 focus:outline-none focus:ring-2 focus:ring-blue-400 bg-blue-900/50 text-white"
|
||||
placeholder="请输入您的电话"
|
||||
:class="{ 'border-red-500': errors.phone }"
|
||||
>
|
||||
<div v-if="errors.phone" class="text-red-400 text-sm mt-1">{{ errors.phone }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 留言 -->
|
||||
<div data-aos="fade-up" data-aos-delay="350">
|
||||
<label for="message" class="block text-sm font-medium text-blue-200 mb-1">留言内容</label>
|
||||
<textarea
|
||||
id="message"
|
||||
v-model="formData.message"
|
||||
rows="4"
|
||||
class="w-full px-4 py-2 rounded-lg border border-blue-200 focus:outline-none focus:ring-2 focus:ring-blue-400 bg-blue-900/50 text-white"
|
||||
placeholder="请输入您的留言内容"
|
||||
:class="{ 'border-red-500': errors.message }"
|
||||
></textarea>
|
||||
<div v-if="errors.message" class="text-red-400 text-sm mt-1">{{ errors.message }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 提交按钮 - 优化版 -->
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full bg-gradient-to-r from-blue-500 to-indigo-600 hover:from-blue-600 hover:to-indigo-700 text-white font-medium py-3 px-6 rounded-lg flex items-center justify-center transition-all duration-300 transform hover:-translate-y-1 shadow-lg hover:shadow-xl active:translate-y-0 active:shadow-md"
|
||||
data-aos="fade-up"
|
||||
data-aos-delay="400"
|
||||
>
|
||||
<i class="fas fa-paper-plane mr-2 transform group-hover:translate-x-1 transition-transform"></i>
|
||||
<span class="group-hover:font-semibold transition-all">提交申请</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 添加输入框聚焦时的动画效果 */
|
||||
input:focus, textarea:focus {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 错误提示动画 */
|
||||
.text-red-400 {
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(-5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* 按钮悬停效果 */
|
||||
button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
opacity: 0;
|
||||
border-radius: 100%;
|
||||
transform: scale(1, 1) translate(-50%);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
button:focus:not(:active)::after {
|
||||
animation: ripple 1s ease-out;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
0% {
|
||||
transform: scale(0, 0);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: scale(50, 50);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮图标动画 */
|
||||
button:hover i {
|
||||
transform: translateX(5px);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user