ActivityHistoryPage.vue 8.4 KB
<template>
  <div class="activity-history-page w-full min-h-screen bg-[#F7F8FA] relative pb-[140px]">
    <!-- Header Banner -->
    <div class="w-full relative h-[180px] overflow-hidden">
      <img :src="historyBg" class="w-full h-full object-cover" alt="History Banner" />
      <div class="absolute inset-0 flex flex-col justify-center items-center text-center px-4">
        <h1 class="text-[#FFDD01] text-3xl font-bold mb-2 tracking-wider drop-shadow-md mt-3">{{ recordDate }}</h1>
        <h2 class="text-white text-2xl font-bold mb-4 tracking-wider drop-shadow-md">您的活动历史</h2>

        <div class="w-full max-w-md rounded-lg px-4 py-2"
          style="background: linear-gradient(96deg, rgba(82,88,254,0) 0%, #5258FE 47%, rgba(92,106,241,0) 100%);">
          <p class="text-white text-xs leading-tight opacity-90">
            查看并确认您参与的星球活动记录,<br>
            如果有缺失,点击没找到我的星球活动,进行补充
          </p>
        </div>
      </div>
    </div>

    <!-- Filter Bar -->
    <div class="bg-white px-4 py-3 flex justify-between items-center sticky top-0 z-20 shadow-sm">
      <span class="text-[#333] font-medium text-sm">活动记录</span>
      <div class="flex items-center text-[#666] text-sm">
        <span>{{ activities.length }}条</span>
      </div>
    </div>

    <!-- List -->
    <div class="px-4 py-4 space-y-4">
      <div v-for="item in activities" :key="item.id"
        class="bg-white rounded-xl py-4 pr-4 pl-0 shadow-sm relative overflow-hidden flex">

        <!-- Blue Marker (Absolute or Flex) -->
        <!-- Based on image, it is sticking to the left edge but inside the card padding area potentially, or flush left -->
        <!-- Looking at image, it is flush left to the card container -->
        <div class="w-1 h-4 bg-[#0052D9] rounded-r-sm mt-1 shrink-0 self-start"></div>

        <div class="pl-3 flex-1">
          <h3 class="text-[#333] font-bold text-base leading-snug">
            {{ item.title }}
          </h3>

          <div class="border-t border-dashed border-gray-200 my-3"></div>

          <div class="flex justify-between items-center text-xs text-[#666] mb-2">
            <span>单价: ¥{{ item.price.toFixed(2) }}/人</span>
            <!-- <span>人数: {{ item.count }}</span> -->
            <span class="text-[#FF3B30] font-bold">实付金额: ¥{{ item.price.toFixed(2) }}</span>
          </div>

          <div class="flex justify-between items-center text-xs text-[#666] mb-4">
            <!-- <span>下单时间: {{ item.date }}</span> -->
            <!-- <span class="text-[#FF3B30] font-bold">实付金额: ¥{{ item.total.toFixed(0) }}</span> -->
          </div>

          <div class="flex justify-end">
            <van-button size="small" plain color="#0052D9" class="!rounded-full !px-4 !h-[28px] !text-xs"
              @click="handleGeneratePoster(item)">
              生成活动海报
            </van-button>
          </div>
        </div>
      </div>
    </div>

    <!-- Fixed Bottom Buttons -->
    <div
      class="fixed bottom-0 left-0 right-0 bg-white/60 backdrop-blur-md p-4 pb-8 z-30 shadow-[0_-2px_10px_rgba(0,0,0,0.05)]">
      <van-button v-if="!userInfo.has_activity_registration" block color="#0052D9" class="!rounded-lg !mb-3 !h-[44px] !text-base !font-bold"
        @click="handleCollectCoins">
        收集星球币
      </van-button>
      <van-button v-else block color="#0052D9" class="!rounded-lg !mb-3 !h-[44px] !text-base !font-bold"
        @click="handleViewCoins">
        查看星球币
      </van-button>

      <van-button block plain color="#0052D9" class="!rounded-lg !h-[44px] !text-base !font-medium"
        @click="showApplyHistoryPopup = true">
        <template #icon>
          <van-icon name="question-o" class="mr-1" />
        </template>
        没找到我的星球活动
      </van-button>
    </div>

    <ActivityApplyHistoryPopup v-model:show="showApplyHistoryPopup" />

  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core'
import { showToast } from 'vant'

import ActivityApplyHistoryPopup from '@/components/ui/ActivityApplyHistoryPopup.vue'
import { userInfoAPI, searchOldActivityAPI } from '@/api/recall_users'
import { oldActivityBatchActivityRegistrationAPI } from '@/api/points'

const historyBg = 'https://cdn.ipadbiz.cn/mlaj/recall/img/history_bg@2x.png'

const router = useRouter()
useTitle('活动历史')


const activities = ref([])

// State
const showApplyHistoryPopup = ref(false)

// 处理生成海报
const handleGeneratePoster = (item) => {
  router.push({
    path: '/recall/poster',
    query: {
      title: item.title, stu_uid: item.stu_uid, campaign_id: item.id
    }
  })
}

const handleCollectCoins = async () => {
  if (activities.value.length === 0) {
    showToast({
      message: '暂无活动可收集',
    })
    return
  }
  // 从缓存获取用户信息
  const cachedUserInfo = localStorage.getItem('cached_user_info')
  if (cachedUserInfo) {
    const userInfo = JSON.parse(cachedUserInfo)
    // 调用积分接口
    const res = await oldActivityBatchActivityRegistrationAPI({
      name: userInfo.name || '',
      mobile: userInfo.phone || '',
      idcard: userInfo.idCard || '',
      old_activity_data: activityInfo.value
    })
    if (res.code) {
      showToast({
        message: '收集星球币成功',
        icon: 'success'
      })
      router.push({ path: '/recall/points' })
    } else {
      showToast({
        message: '收集星球币失败',
        icon: 'error'
      })
    }
  }
}

/**
 * 查看星球币
 */
const handleViewCoins = () => {
  router.push({ path: '/recall/points' })
}

const userInfo = ref({});
const campaign_info = ref([]);
const activityInfo = ref({})

// 记录日期
const recordDate = ref('')

/**
 * 映射活动数据
 * @param campaign_list 活动列表
 * @returns 映射后的活动列表
 */
const mapCampaignToActivities = (campaign_list) => {
  return (campaign_list || []).map(item => ({
    id: item.campaign_id || 0,
    stu_uid: item.stu_uid || '',
    title: item.campaign_name || '',
    price: item.fee_stu || 0,
    count: item.stu_cnt || 0,
    date: item.campaign_year || '',
    total: item.fee_stu * item.stu_cnt || 0
  }))
}

/**
 * 设置记录日期
 * @param activity_list 活动列表
 */
const setRecordDateByActivities = (activity_list) => {
  const sorted_dates = (activity_list || []).map(item => item.date).sort((a, b) => new Date(a) - new Date(b))
  recordDate.value = sorted_dates.length ? `${sorted_dates[0]}-2025` : ''
}

/**
 * 处理活动响应
 * @param activity_res 活动响应
 * @param should_set_activity_info 是否设置活动信息
 */
const applyActivityResponse = (activity_res, should_set_activity_info) => {
  if (!activity_res || !activity_res.code) return

  if (should_set_activity_info) {
    activityInfo.value = activity_res.data || {}
  }

  campaign_info.value = activity_res.data?.campaign_info || []
  if (!campaign_info.value.length) return

  activities.value = mapCampaignToActivities(campaign_info.value)
  setRecordDateByActivities(activities.value)
}

/**
 * 获取缓存用户参数
 * @returns 缓存用户参数
 */
const getCachedUserParams = () => {
  const cached_user_info = localStorage.getItem('cached_user_info')
  if (!cached_user_info) return { has_cache: false, params: null }

  try {
    const user_info = JSON.parse(cached_user_info)
    if (user_info?.name && user_info?.phone && user_info?.idCard) {
      return {
        has_cache: true,
        params: {
          name: user_info.name,
          mobile: user_info.phone,
          idcard: user_info.idCard
        }
      }
    }
  } catch (e) {
    return { has_cache: true, params: null }
  }

  return { has_cache: true, params: null }
}

onMounted(async () => {
  // 从缓存获取用户信息
  const { has_cache, params } = getCachedUserParams()
  if (has_cache) {
    if (params) {
      const activity_res = await searchOldActivityAPI(params)
      applyActivityResponse(activity_res, true)
    }
  } else {
    const activity_res = await searchOldActivityAPI()
    applyActivityResponse(activity_res, false)
  }

  // 获取用户信息
  const userInfoRes = await userInfoAPI()
  if (userInfoRes.code) {
    userInfo.value = userInfoRes.data.user || {};
  }
})
</script>

<style lang="less" scoped>
// Custom Scrollbar hide for cleaner look if needed
::-webkit-scrollbar {
  width: 0px;
  background: transparent;
}
</style>