ActivityHistoryPage.vue 9.52 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 v-if="!userInfo.has_activity_registration"
      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 block color="#0052D9" class="!rounded-lg !mb-3 !h-[44px] !text-base !font-bold"
        @click="handleCollectCoins">
        收集星球币
      </van-button>

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

    <!-- Missing Activity Popup -->
    <van-popup v-model:show="showMissingPopup" round position="bottom" :style="{ height: '60%' }" class="flex flex-col">
      <div class="p-4 flex-1 flex flex-col">
        <h3 class="text-center font-bold text-lg mb-6 text-[#333]">补充活动信息</h3>

        <div class="flex-1 bg-[#F7F8FA] rounded-lg p-3 mb-6">
          <textarea v-model="missingInfo"
            class="w-full h-full bg-transparent border-none outline-none resize-none text-sm text-[#333] placeholder:text-[#999]"
            placeholder="请把缺失的星球活动,补充在这里,留下活动的名称、时间、地点、参加人数等信息"></textarea>
        </div>

        <van-button block color="#0052D9" class="!rounded-lg !h-[44px] !text-base !font-bold"
          @click="handleSubmitMissing">
          提交
        </van-button>
      </div>
    </van-popup>

  </div>
</template>

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

import { userInfoAPI, searchOldActivityAPI, getSupplementAPI, editSupplementAPI } 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 showMissingPopup = ref(false)
const missingInfo = ref('')

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

const handleCollectCoins = async () => {
  // 从缓存获取用户信息
  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 handleSubmitMissing = async () => {
  if (!missingInfo.value) {
    showToast('请输入补充信息')
    return
  }

  const res = await editSupplementAPI({
    note: missingInfo.value
  })

  if (res.code) {
    showToast('提交成功')
    showMissingPopup.value = false
  }
}

// 获取补充信息
const fetchSupplementInfo = async () => {
  const res = await getSupplementAPI()
  if (res.code && res.data && res.data.note) {
    missingInfo.value = res.data.note
  }
}

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

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

onMounted(async () => {
  // 获取补充信息
  fetchSupplementInfo()

  // 从缓存获取用户信息
  const cachedUserInfo = localStorage.getItem('cached_user_info')
  if (cachedUserInfo) {
    const userInfo = JSON.parse(cachedUserInfo)
    if (userInfo.name && userInfo.phone && userInfo.idCard) {
      // 通过用户信息获取活动历史信息
      const activityRes = await searchOldActivityAPI({
        name: userInfo.name,
        mobile: userInfo.phone,
        idcard: userInfo.idCard
      })
      if (activityRes.code) {
        activityInfo.value = activityRes.data || {}
        // 获取历史列表数据
        campaign_info.value = activityRes.data?.campaign_info || []
        if (campaign_info.value.length) {
          activities.value = campaign_info.value.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
          }))
          // 遍历日期把列表date字段从小到大排序, 获取到其中日期段 比如 2020-2025
          const sortedDates = activities.value.map(item => item.date).sort((a, b) => new Date(a) - new Date(b))
          if (sortedDates[0] !== sortedDates[sortedDates.length - 1]) {
            recordDate.value = sortedDates[0] + '-' + sortedDates[sortedDates.length - 1] || ''
          } else {
            recordDate.value = sortedDates[0] || ''
          }
        }
      }
    }
  } else {
    // 如果是收集完成没有缓存字段的情况, 直接查接口
    const activityRes = await searchOldActivityAPI()
    if (activityRes.code) {
      // 获取历史列表数据
      campaign_info.value = activityRes.data?.campaign_info || []
      if (campaign_info.value.length) {
        activities.value = campaign_info.value.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
        }));
        const sortedDates = activities.value.map(item => item.date).sort((a, b) => new Date(a) - new Date(b))
        if (sortedDates[0] !== sortedDates[sortedDates.length - 1]) {
          recordDate.value = sortedDates[0] + '-' + sortedDates[sortedDates.length - 1] || ''
        } else {
          recordDate.value = sortedDates[0] || ''
        }
      }
    }
  }

  // 获取用户信息
  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>