index.vue 4.87 KB
<!--
 * @Date: 2026-02-05 19:48:00
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2026-02-05 20:09:34
 * @FilePath: /lls_program/src/pages/CheckinMap/index.vue
 * @Description: 便民地图列表页
-->
<template>
  <view class="checkin-map-page">
    <!-- 列表内容 -->
    <view class="map-list">
      <view v-for="item in mapList" :key="item.id" class="map-card" @tap="handleCardClick(item)">
        <!-- 封面图 -->
        <view class="card-cover">
          <image :src="item.cover" mode="aspectFill" class="cover-image" />
        </view>

        <!-- 卡片内容 -->
        <view class="card-content">
          <text class="card-title">{{ item.title }}</text>
          <view class="card-time-wrapper">
            <text class="card-time-label">活动日期</text>
            <text class="card-time">{{ item.timeRange }}</text>
          </view>
          <view class="enter-btn" @tap.stop="handleEnter(item)">
            <text class="enter-btn-text">立即进入</text>
          </view>
        </view>
      </view>
    </view>

    <BottomNav />
  </view>
</template>

<script setup>
import { ref } from 'vue'
import Taro from '@tarojs/taro'
import BottomNav from '@/components/BottomNav.vue'
import { listAPI } from '@/api/map_activity'
import { mockMapActivityListAPI } from '@/utils/mockData'
import { useLoad } from '@tarojs/taro'

// ⚠️ MOCK 数据开关 - 开发环境使用 mock 数据,生产环境使用真实 API
const USE_MOCK_DATA = process.env.NODE_ENV === 'development'

/**
 * 便民地图列表数据
 */
const mapList = ref([])
const loading = ref(false)

/**
 * 格式化 API 数据为页面所需格式
 * @param {Array} list - API 返回的活动列表
 * @returns {Array} 格式化后的活动列表
 */
const formatMapList = list => {
  return list.map(item => ({
    id: item.id,
    title: item.tittle, // API 返回的是 tittle,映射为 title
    cover: item.cover,
    timeRange: `${item.begin_date}~${item.end_date}`,
    activityId: item.id, // 使用 id 作为 activityId
  }))
}

/**
 * 获取地图活动列表
 */
const fetchMapList = async () => {
  if (loading.value) {
    return
  }

  loading.value = true

  try {
    const params = {}

    // 根据开关选择使用真实 API 或 Mock 数据
    const res = USE_MOCK_DATA ? await mockMapActivityListAPI(params) : await listAPI(params)

    if (res.code === 1 && res.data) {
      mapList.value = formatMapList(res.data)
    } else {
      Taro.showToast({
        title: res.msg || '获取活动列表失败',
        icon: 'none',
      })
    }
  } catch (err) {
    console.error('获取地图活动列表失败:', err)
    Taro.showToast({
      title: '网络异常,请重试',
      icon: 'none',
    })
  } finally {
    loading.value = false
  }
}

/**
 * 处理卡片点击事件
 * @param {Object} item - 卡片数据
 */
const handleCardClick = item => {
  console.log('点击卡片:', item)
  // 可以在这里添加预览或其他交互
}

/**
 * 进入打卡活动
 * @param {Object} item - 活动数据
 */
const handleEnter = item => {
  console.log('进入活动:', item)

  // 跳转到活动详情页,带上活动ID参数
  Taro.navigateTo({
    url: `/pages/ActivitiesDetail/index?activityId=${item.activityId}&title=${encodeURIComponent(item.title)}`,
  })
}

// 页面加载时获取列表
useLoad(() => {
  fetchMapList()
})
</script>

<style lang="less">
.checkin-map-page {
  min-height: 100vh;
  background-color: #54abae; /* 项目主色调 */
  padding-bottom: 160px; /* 为底部导航留出空间 */
}

.page-header {
  padding: 40px 30px 20px;
  text-align: center;
}

.page-title {
  font-size: 48px;
  font-weight: bold;
  color: #ffffff;
}

.map-list {
  display: grid;
  grid-template-columns: repeat(2, 1fr); /* 一行两个 */
  gap: 20px;
  padding: 20px 30px;
}

.map-card {
  background-color: #ffffff;
  border-radius: 16px;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
  transition: transform 0.2s;
}

.map-card:active {
  transform: scale(0.98);
}

.card-cover {
  width: 100%;
  height: 360px;
  overflow: hidden;
}

.cover-image {
  width: 100%;
  height: 100%;
}

.card-content {
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.card-title {
  font-size: 28px;
  font-weight: bold;
  color: #1f2937;
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.card-time-wrapper {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.card-time-label {
  font-size: 22px;
  color: #9ca3af;
  font-weight: 500;
}

.card-time {
  font-size: 24px;
  color: #1f2937;
  font-weight: 600;
}

.enter-btn {
  margin-top: 8px;
  background-color: #54abae;
  color: #ffffff;
  padding: 16px;
  border-radius: 8px;
  text-align: center;
  font-size: 26px;
  font-weight: 500;
}

.enter-btn:active {
  background-color: #4a979a;
}

.enter-btn-text {
  color: #ffffff;
}
</style>