ActivitySignupPage.vue 6.38 KB
<template>
  <div class="min-h-screen flex flex-col bg-gradient-to-br from-green-50 via-teal-50 to-blue-50 py-12 px-4 sm:px-6 lg:px-8">
    <div class="sm:mx-auto sm:w-full sm:max-w-md">
      <h2 class="text-center text-2xl font-bold text-gray-800 mb-2">活动报名</h2>
      <p class="text-center text-gray-600">{{ activity?.title }}</p>
    </div>

    <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
      <FrostedGlass class="py-8 px-6 rounded-lg">
        <!-- 活动基本信息 -->
        <div class="mb-6 space-y-4">
          <div class="flex items-center justify-between text-sm">
            <span class="text-gray-500">活动时间</span>
            <span class="text-gray-700">{{ activity?.period }}</span>
          </div>
          <div class="flex items-center justify-between text-sm">
            <span class="text-gray-500">活动地点</span>
            <span class="text-gray-700">{{ activity?.location }}</span>
          </div>
          <div class="flex items-center justify-between text-sm">
            <span class="text-gray-500">活动费用</span>
            <span class="text-red-500 font-medium">¥{{ activity?.price }}/人</span>
          </div>
        </div>

        <div v-if="error" class="mb-4 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-md">
          {{ error }}
        </div>

        <form class="space-y-6" @submit.prevent="handleSubmit">
          <div>
            <label for="name" class="block text-sm font-medium text-gray-700">
              联系人姓名 <span class="text-red-500">*</span>
            </label>
            <input
              id="name"
              v-model="formData.name"
              type="text"
              required
              class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
            />
          </div>

          <div>
            <label for="phone" class="block text-sm font-medium text-gray-700">
              联系电话 <span class="text-red-500">*</span>
            </label>
            <input
              id="phone"
              v-model="formData.phone"
              type="tel"
              required
              class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
            />
          </div>

          <div>
            <label for="participants" class="block text-sm font-medium text-gray-700">
              参与人数 <span class="text-red-500">*</span>
            </label>
            <input
              id="participants"
              v-model="formData.participants"
              type="number"
              min="1"
              :max="activity?.maxParticipants - activity?.participantsCount"
              required
              class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
            />
          </div>

          <div>
            <label for="remark" class="block text-sm font-medium text-gray-700">
              备注信息
            </label>
            <textarea
              id="remark"
              v-model="formData.remark"
              rows="3"
              class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-green-500 focus:border-green-500"
            ></textarea>
          </div>

          <div class="flex items-center">
            <input
              id="agreeTerms"
              v-model="formData.agreeTerms"
              type="checkbox"
              class="h-4 w-4 text-green-600 focus:ring-green-500 border-gray-300 rounded"
            />
            <label for="agreeTerms" class="ml-2 block text-sm text-gray-700">
              我已阅读并同意 <a href="#" class="text-green-600 hover:text-green-500">活动协议</a>
            </label>
          </div>

          <div class="space-y-2">
            <div class="flex items-center justify-between text-sm">
              <span class="text-gray-500">费用合计</span>
              <span class="text-red-500 font-bold">¥{{ totalAmount }}</span>
            </div>
            <button
              type="submit"
              :disabled="loading"
              class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
              :class="{ 'opacity-70 cursor-not-allowed': loading }"
            >
              {{ loading ? '提交中...' : '提交报名' }}
            </button>
          </div>
        </form>
      </FrostedGlass>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import FrostedGlass from '@/components/ui/FrostedGlass.vue'
import { activities } from '@/utils/mockData'
import { useTitle } from '@vueuse/core';
const $route = useRoute();
const $router = useRouter();
useTitle($route.meta.title);

const route = useRoute()
const router = useRouter()
const activity = ref(null)

const formData = reactive({
  name: '',
  phone: '',
  participants: 1,
  remark: '',
  agreeTerms: false
})

const error = ref('')
const loading = ref(false)

// 获取活动数据
onMounted(() => {
  const id = route.params.id
  const foundActivity = activities.find((a) => a.id === id)
  if (foundActivity) {
    activity.value = foundActivity
  } else {
    router.push('/activities')
  }
})

// 计算总金额
const totalAmount = computed(() => {
  return activity.value ? activity.value.price * formData.participants : 0
})

const handleSubmit = async () => {
  if (!formData.name || !formData.phone || !formData.participants) {
    error.value = '请填写所有必填字段'
    return
  }

  if (!formData.agreeTerms) {
    error.value = '请阅读并同意活动协议'
    return
  }

  try {
    error.value = ''
    loading.value = true

    // TODO: 实现报名和支付逻辑
    await new Promise(resolve => setTimeout(resolve, 1000))

    // 报名成功后跳转回活动详情页,使用replace避免返回到报名页
    router.replace(`/activities/${route.params.id}`)
  } catch (e) {
    error.value = '报名失败,请稍后重试'
  } finally {
    loading.value = false
  }
}
</script>