ActivityDetailPage.vue 13.1 KB
<script setup>
import { ref, onMounted, defineComponent, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AppLayout from '@/components/layout/AppLayout.vue'
import FrostedGlass from '@/components/ui/FrostedGlass.vue'
import { activities } from '@/utils/mockData'

const route = useRoute()
const router = useRouter()
const activity = ref(null)
const activeTab = ref('活动信息')

// 获取活动数据
onMounted(() => {
  const id = route.params.id
  const foundActivity = activities.find(a => a.id === id)
  if (foundActivity) {
    activity.value = foundActivity
  } else {
    // 活动未找到,重定向到活动列表页
    router.push('/activities')
  }
})

// 获取状态颜色类名
const getStatusColorClass = (status) => {
  switch (status) {
    case '活动中':
      return 'bg-blue-100 text-blue-700'
    case '进行中':
      return 'bg-green-100 text-green-700'
    case '即将开始':
      return 'bg-orange-100 text-orange-700'
    case '已结束':
      return 'bg-gray-100 text-gray-700'
    default:
      return 'bg-gray-100 text-gray-700'
  }
}

// 相关活动
const relatedActivities = ref([])
onMounted(() => {
  if (activity.value) {
    relatedActivities.value = activities
      .filter(a => a.id !== activity.value.id)
      .slice(0, 3)
  }
})

// 页面导航
const navigateTo = (path) => {
  router.push(path)
}

// 右侧内容组件
const RightContent = defineComponent({
  setup() {
    return () => h('div', { class: 'flex' }, [
      h('button', { class: 'p-2' }, [
        h('svg', {
          xmlns: 'http://www.w3.org/2000/svg',
          class: 'h-6 w-6 text-gray-700',
          fill: 'none',
          viewBox: '0 0 24 24',
          stroke: 'currentColor'
        }, [
          h('path', {
            'stroke-linecap': 'round',
            'stroke-linejoin': 'round',
            'stroke-width': '2',
            d: 'M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z'
          })
        ])
      ]),
      h('button', { class: 'p-2' }, [
        h('svg', {
          xmlns: 'http://www.w3.org/2000/svg',
          class: 'h-6 w-6 text-gray-700',
          fill: 'none',
          viewBox: '0 0 24 24',
          stroke: 'currentColor'
        }, [
          h('path', {
            'stroke-linecap': 'round',
            'stroke-linejoin': 'round',
            'stroke-width': '2',
            d: 'M12 5v.01M12 12v.01M12 19v.01M12 6a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2zm0 7a1 1 0 110-2 1 1 0 010 2z'
          })
        ])
      ])
    ])
  }
})
</script>

<template>
  <AppLayout title="活动详情" :showBackButton="true" :rightContent="RightContent">
    <div class="pb-24">
      <!-- Activity Cover Image -->
      <div class="w-full h-56 relative">
        <img
          :src="activity?.imageUrl"
          :alt="activity?.title"
          class="w-full h-full object-cover"
        />
        <div class="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4">
          <div class="inline-block px-2 py-1 rounded bg-white/20 backdrop-blur-sm text-white text-xs mb-2">
            {{ activity?.subtitle }}
          </div>
          <h1 class="text-xl text-white font-bold">{{ activity?.title }}</h1>
        </div>
      </div>

      <!-- Activity Tags and Status -->
      <div class="px-4 py-3 flex justify-between items-center">
        <div class="flex space-x-2">
          <span class="px-2 py-1 rounded-full bg-green-100 text-green-700 text-xs">
            亲子活动
          </span>
          <span class="px-2 py-1 rounded-full bg-blue-100 text-blue-700 text-xs">
            教育
          </span>
          <span :class="['px-2 py-1 rounded-full text-xs', getStatusColorClass(activity?.status)]">
            {{ activity?.status }}
          </span>
        </div>
        <div>
          <button class="p-1">
            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-pink-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
            </svg>
          </button>
        </div>
      </div>

      <!-- Tab Navigation -->
      <div class="border-b border-gray-200 px-4">
        <div class="flex space-x-8">
          <button
            v-for="tab in ['活动信息', '参与人员', '活动说明']"
            :key="tab"
            @click="activeTab = tab"
            :class="[
              'pb-3 font-medium',
              activeTab === tab
                ? 'text-green-600 border-b-2 border-green-600'
                : 'text-gray-500'
            ]"
          >
            {{ tab }}
          </button>
        </div>
      </div>

      <!-- Tab Content -->
      <div class="px-4 py-4">
        <div v-if="activeTab === '活动信息'">
          <!-- Activity Details -->
          <FrostedGlass class="p-4 mb-4 rounded-xl">
            <div class="space-y-4">
              <div class="flex items-start">
                <div class="p-2 rounded-full bg-green-100 mr-3">
                  <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
                  </svg>
                </div>
                <div class="flex-1">
                  <p class="font-medium">活动地点</p>
                  <p class="text-gray-600 text-sm mt-1">{{ activity?.location }}</p>
                </div>
              </div>

              <div class="flex items-start">
                <div class="p-2 rounded-full bg-blue-100 mr-3">
                  <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
                  </svg>
                </div>
                <div class="flex-1">
                  <p class="font-medium">活动时间</p>
                  <p class="text-gray-600 text-sm mt-1">{{ activity?.period }}</p>
                  <p class="text-xs text-gray-500 mt-1">每周六、日 10:00-12:00</p>
                </div>
              </div>

              <div class="flex items-start">
                <div class="p-2 rounded-full bg-purple-100 mr-3">
                  <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-purple-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
                  </svg>
                </div>
                <div class="flex-1">
                  <p class="font-medium">参与人数</p>
                  <p class="text-gray-600 text-sm mt-1">{{ activity?.participantsCount }}人 / 上限{{ activity?.maxParticipants }}人</p>
                  <div class="w-full bg-gray-200 rounded-full h-1.5 mt-2">
                    <div class="bg-green-600 h-1.5 rounded-full" :style="{ width: (activity?.participantsCount / activity?.maxParticipants * 100) + '%' }"></div>
                  </div>
                </div>
              </div>

              <div class="flex items-start">
                <div class="p-2 rounded-full bg-orange-100 mr-3">
                  <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-orange-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
                  </svg>
                </div>
                <div class="flex-1">
                  <p class="font-medium">活动费用</p>
                  <p class="text-gray-600 text-sm mt-1">¥{{ activity?.price }}/人(包含材料费、场地费)</p>
                </div>
              </div>
            </div>
          </FrostedGlass>

          <!-- Activity Description -->
          <FrostedGlass class="p-4 mb-4 rounded-xl">
            <h2 class="text-lg font-semibold mb-3">活动介绍</h2>
            <p class="text-gray-700 whitespace-pre-line">
              {{ activity?.title }} 是一个精心设计的亲子互动活动,旨在增强家长与孩子之间的沟通与理解。

              通过一系列有趣的游戏和学习环节,让孩子在轻松愉快的氛围中学习知识,培养良好的学习习惯和价值观。同时,也给予家长更多指导和支持,帮助他们更好地理解孩子的需求和成长过程。

              活动特色:
              - 专业导师全程引导
              - 互动性强,参与感高
              - 寓教于乐,收获满满
              - 结交志同道合的家庭
            </p>
          </FrostedGlass>
        </div>

        <div v-if="activeTab === '参与人员'">
          <FrostedGlass class="p-4 rounded-xl">
            <h2 class="text-lg font-semibold mb-3">活动参与者</h2>
            <div class="grid grid-cols-4 gap-4">
              <div v-for="index in 12" :key="index" class="flex flex-col items-center">
                <div class="w-14 h-14 rounded-full bg-gray-200 overflow-hidden mb-1">
                  <svg xmlns="http://www.w3.org/2000/svg" class="h-full w-full text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
                  </svg>
                </div>
                <span class="text-xs text-gray-600">用户{{ index }}</span>
              </div>
            </div>
            <div class="mt-4 flex justify-center">
              <button class="text-green-600 text-sm">
                查看全部参与者
              </button>
            </div>
          </FrostedGlass>
        </div>

        <div v-if="activeTab === '活动说明'">
          <FrostedGlass class="p-4 rounded-xl">
            <h2 class="text-lg font-semibold mb-3">活动说明</h2>
            <div class="space-y-4 text-gray-700">
              <div>
                <h3 class="font-medium mb-1">适合人群</h3>
                <p class="text-sm">5-12岁儿童及其家长</p>
              </div>
              <div>
                <h3 class="font-medium mb-1">注意事项</h3>
                <ul class="text-sm list-disc pl-5 space-y-1">
                  <li>请提前15分钟到达活动地点</li>
                  <li>携带必要的学习用具(铅笔、彩笔等)</li>
                  <li>穿着舒适的衣物</li>
                  <li>如需取消报名,请提前48小时通知我们</li>
                </ul>
              </div>
              <div>
                <h3 class="font-medium mb-1">活动流程</h3>
                <ol class="text-sm list-decimal pl-5 space-y-1">
                  <li>签到入场(15分钟)</li>
                  <li>活动介绍与热身(20分钟)</li>
                  <li>主题活动(60分钟)</li>
                  <li>小组分享(30分钟)</li>
                  <li>总结与合影(15分钟)</li>
                </ol>
              </div>
            </div>
          </FrostedGlass>
        </div>
      </div>

      <!-- Related Activities -->
      <div class="px-4 py-2">
        <h2 class="text-lg font-semibold mb-3">相关活动</h2>
        <div class="space-y-3">
          <div
            v-for="relatedActivity in relatedActivities"
            :key="relatedActivity.id"
            class="flex items-center bg-white/80 p-2 rounded-lg shadow-sm"
            @click="navigateTo(`/activities/${relatedActivity.id}`)"
          >
            <div class="w-16 h-16 rounded-lg overflow-hidden mr-3">
              <img
                :src="relatedActivity.imageUrl"
                :alt="relatedActivity.title"
                class="w-full h-full object-cover"
              />
            </div>
            <div class="flex-1">
              <h3 class="font-medium line-clamp-1">{{ relatedActivity.title }}</h3>
              <p class="text-xs text-gray-500 mt-1">
                {{ relatedActivity.period }}
              </p>
            </div>
          </div>
        </div>
      </div>
    </div>
  </AppLayout>
</template>