ActivitiesPage.vue 8.21 KB
<template>
  <AppLayout title="最新活动" :showBackButton="true" :rightContent="RightContent">
    <div class="bg-gradient-to-b from-green-50/70 to-white/90 min-h-screen pb-20">
      <!-- Top filter section -->
      <div class="sticky top-0 z-10 bg-white/80 backdrop-blur-sm shadow-sm">
        <!-- Category filters -->
        <div class="px-4 pt-3 pb-2 overflow-x-auto">
          <div class="flex space-x-6 whitespace-nowrap">
            <button
              v-for="filter in filterCategories"
              :key="filter"
              :class="[
                'pb-1',
                activeFilter === filter
                  ? 'text-green-600 border-b-2 border-green-600 font-medium'
                  : 'text-gray-500'
              ]"
              @click="activeFilter = filter"
            >
              {{ filter }}
            </button>
          </div>
        </div>

        <!-- Additional filters -->
        <div class="px-4 py-2 flex justify-between items-center border-b border-gray-100">
          <div class="flex space-x-2">
            <div
              class="flex items-center px-3 py-1 bg-white/70 backdrop-blur-sm rounded-full text-sm border border-gray-200 shadow-sm"
              @click="showFilters = !showFilters"
            >
              <span class="text-gray-700">{{ locationFilter || '活动地点' }}</span>
              <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
              </svg>
            </div>

            <div
              class="flex items-center px-3 py-1 bg-white/70 backdrop-blur-sm rounded-full text-sm border border-gray-200 shadow-sm"
              @click="showFilters = !showFilters"
            >
              <span class="text-gray-700">{{ statusFilter || '活动状态' }}</span>
              <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1 text-gray-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
              </svg>
            </div>
          </div>

          <button
            :class="[
              'p-1 rounded-lg',
              showFilters ? 'bg-green-100 text-green-600' : 'bg-white/70 text-gray-500'
            ]"
            @click="toggleFilters"
          >
            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
            </svg>
          </button>
        </div>
      </div>

      <!-- Filter panel -->
      <div v-if="showFilters" class="bg-white/90 backdrop-blur-md p-4 shadow-md">
        <div class="mb-4">
          <h3 class="font-medium text-gray-700 mb-2">活动地点</h3>
          <div class="flex flex-wrap gap-2">
            <button
              v-for="location in locationOptions"
              :key="location"
              :class="[
                'px-3 py-1 rounded-full text-sm',
                (location === '全部' && !locationFilter) || locationFilter === location
                  ? 'bg-green-100 text-green-600 border border-green-200'
                  : 'bg-gray-100 text-gray-600 border border-gray-200'
              ]"
              @click="applyLocationFilter(location)"
            >
              {{ location }}
            </button>
          </div>
        </div>

        <div class="mb-4">
          <h3 class="font-medium text-gray-700 mb-2">活动状态</h3>
          <div class="flex flex-wrap gap-2">
            <button
              v-for="status in statusOptions"
              :key="status"
              :class="[
                'px-3 py-1 rounded-full text-sm',
                (status === '全部' && !statusFilter) || statusFilter === status
                  ? 'bg-green-100 text-green-600 border border-green-200'
                  : 'bg-gray-100 text-gray-600 border border-gray-200'
              ]"
              @click="applyStatusFilter(status)"
            >
              {{ status }}
            </button>
          </div>
        </div>

        <div class="flex justify-between">
          <button
            class="px-4 py-2 text-gray-600 bg-gray-100 rounded-lg"
            @click="resetFilters"
          >
            重置
          </button>
          <button
            class="px-4 py-2 text-white bg-green-600 rounded-lg"
            @click="showFilters = false"
          >
            确认
          </button>
        </div>
      </div>

      <!-- Activity count -->
      <div class="px-4 py-3 text-sm text-gray-500">
        共 {{ filteredActivities.length }} 个活动
      </div>

      <!-- Activity list -->
      <div class="px-4 pb-4 space-y-4">
        <template v-if="filteredActivities.length > 0">
          <ActivityCard
            v-for="activity in filteredActivities"
            :key="activity.id"
            :activity="activity"
          />
        </template>
        <div v-else class="flex flex-col items-center justify-center py-10">
          <svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 text-gray-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
          </svg>
          <p class="mt-4 text-gray-500">没有找到符合条件的活动</p>
          <button
            class="mt-2 px-4 py-2 text-green-600 border border-green-600 rounded-full text-sm"
            @click="resetFilters"
          >
            重置筛选条件
          </button>
        </div>
      </div>
    </div>
  </AppLayout>
</template>

<script setup>
import { ref, computed, defineComponent, h } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import AppLayout from '@/components/layout/AppLayout.vue'
import ActivityCard from '@/components/ui/ActivityCard.vue'
import { activities } from '@/utils/mockData'
import { useTitle } from '@vueuse/core';
const $route = useRoute();
const $router = useRouter();
useTitle($route.meta.title);
// 响应式状态
const activeFilter = ref('全部')
const locationFilter = ref('')
const statusFilter = ref('')
const showFilters = ref(false)

// 过滤选项
const filterCategories = ['全部', '游学', '共读', '营地', '线上讲座']
const locationOptions = ['全部', '线上', '北京', '上海', '广州', '成都']
const statusOptions = ['全部', '活动中', '即将开始', '已结束']

// 右侧内容组件
const RightContent = defineComponent({
  setup() {
    return () => 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'
        })
      ])
    ])
  }
})

// 切换筛选面板
const toggleFilters = () => {
  showFilters.value = !showFilters.value
}

// 应用地点筛选
const applyLocationFilter = (location) => {
  locationFilter.value = location === '全部' ? '' : location
}

// 应用状态筛选
const applyStatusFilter = (status) => {
  statusFilter.value = status === '全部' ? '' : status
}

// 重置所有筛选
const resetFilters = () => {
  locationFilter.value = ''
  statusFilter.value = ''
  activeFilter.value = '全部'
  showFilters.value = false
}

// 计算筛选后的活动列表
const filteredActivities = computed(() => {
  return activities.filter(activity => {
    // 地点筛选
    if (locationFilter.value && activity.location !== locationFilter.value) {
      return false
    }

    // 状态筛选
    if (statusFilter.value && activity.status !== statusFilter.value) {
      return false
    }

    return true
  })
})
</script>