PosterPage.vue 5.83 KB
<!--
 * @Date: 2025-12-23 15:50:59
 * @LastEditors: hookehuyr hookehuyr@gmail.com
 * @LastEditTime: 2025-12-31 17:04:10
 * @FilePath: /mlaj/src/views/recall/PosterPage.vue
 * @Description: 分享海报页面
-->
<template>
  <div class="min-h-screen bg-[#1E40C8] relative flex flex-col">

    <!-- Poster Container (Scrollable Area) -->
    <div class="flex-1 overflow-y-auto px-6 pt-6 pb-32 flex flex-col items-center">
      <RecallPoster
        v-if="isReady"
        :bg-url="posterBg"
        :title="title"
        :logo-url="logoUrl"
        :qr-url="qrCodeUrl"
      />
      <div v-else class="w-full h-[62vh] min-h-[400px] flex items-center justify-center">
        <van-loading color="#ffffff" vertical>加载中...</van-loading>
      </div>
    </div>

    <!-- Buttons (Fixed at Bottom) -->
    <div class="fixed bottom-0 left-0 right-0 px-8 py-3 z-50 max-w-md mx-auto w-full">
      <div class="flex gap-4">
        <van-uploader :after-read="afterRead" accept="image/*" class="flex-1 w-full" :show-upload="false">
          <template #default>
            <van-button block color="#0052D9" plain
              class="!rounded-lg !h-[44px] !text-[15px] !font-bold w-full !border-[#0052D9] !bg-white">
              更换图片
            </van-button>
          </template>
        </van-uploader>
      </div>
      <div class="mt-2 text-[12px] leading-[16px] text-white/80 text-center">
        上传您参加活动时的照片,制作独有海报
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core'
import RecallPoster from '@/components/poster/RecallPoster.vue'
import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common'
import { showToast, showLoadingToast } from 'vant'
import { qiniuFileHash } from '@/utils/qiniuFileHash'
import { useAuth } from '@/contexts/auth'

import { getPosterAPI, editPosterAPI, trackingAPI } from '@/api/recall_users'

const $route = useRoute();
const $router = useRouter();
const { currentUser } = useAuth();
useTitle('分享海报');

// Assets
const defaultBg = 'https://cdn.ipadbiz.cn/mlaj/images/test-bgg03.jpg?imageMogr2/thumbnail/800x/strip/quality/80'
const logoUrl = 'https://cdn.ipadbiz.cn/mlaj/recall/poster/kai@2x.png'

// State
const posterBg = ref(defaultBg)
const qrCodeUrl = ref('')
const isReady = ref(false)

/**
 * 获取文件哈希(与七牛云ETag一致)
 */
const getFileMD5 = async (file) => {
    return await qiniuFileHash(file)
}

// 上传到七牛云
const uploadToQiniu = async (file, token, fileName) => {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('token', token)
  formData.append('key', fileName)

  const config = {
    headers: { 'Content-Type': 'multipart/form-data' }
  }

  // 根据协议选择上传地址
  const qiniuUploadUrl = window.location.protocol === 'https:'
    ? 'https://up.qbox.me'
    : 'http://upload.qiniu.com'

  return await qiniuUploadAPI(qiniuUploadUrl, formData, config)
}

// Actions
const afterRead = async (file) => {
  const toast = showLoadingToast({
    message: '上传中...',
    forbidClick: true,
    duration: 0 // 持续展示
  });

  try {
    // 获取MD5值
    const md5 = await getFileMD5(file.file)

    // 获取七牛token
    const tokenResult = await qiniuTokenAPI({
      name: file.file.name,
      hash: md5
    })

    // 文件已存在,直接使用
    if (tokenResult.data) {
      posterBg.value = tokenResult.data.src + '?imageMogr2/thumbnail/800x/strip/quality/80';
      // 编辑海报配置
      await editPosterAPI({
        stu_uid: stu_uid.value,
        campaign_id: campaign_id.value,
        background_image: tokenResult.data.src
      })
      showToast('图片上传成功');
      return;
    }

    // 新文件上传
    if (tokenResult.token) {
      const suffix = /.[^.]+$/.exec(file.file.name) || ''
      // 使用 recall/poster 路径
      const mobile = currentUser.value?.mobile || 'guest'
      const fileName = `mlaj/upload/recall/poster/${mobile}/${md5}${suffix}`

      const { filekey, image_info } = await uploadToQiniu(
        file.file,
        tokenResult.token,
        fileName
      )

      if (filekey) {
        // 保存文件信息
        const { data } = await saveFileAPI({
          name: file.file.name,
          filekey,
          hash: md5,
          height: image_info?.height,
          width: image_info?.width
        })

        if (data) {
          posterBg.value = data.src + '?imageMogr2/thumbnail/800x/strip/quality/80';
          // 编辑海报配置
          await editPosterAPI({
            stu_uid: stu_uid.value,
            campaign_id: campaign_id.value,
            background_image: data.src
          })
          showToast('图片上传成功');
        }
      }
    }
  } catch (error) {
    console.error('图片上传失败:', error)
    showToast('图片上传失败,请重试')
  } finally {
    toast.close();
  }
}

const stu_uid = ref($route.query.stu_uid || '')
const campaign_id = ref($route.query.campaign_id || '')
const title = ref($route.query.title || '活动海报')

onMounted(async () => {
  // 埋点
  trackingAPI({
    event_type: 'share_poster',
    campaign_id: campaign_id.value,
    stu_uid: stu_uid.value
  })

  if (stu_uid.value && campaign_id.value) {
    const { code, data } = await getPosterAPI({
      stu_uid: stu_uid.value,
      campaign_id: campaign_id.value
    })

    if (code === 1) {
      posterBg.value = data.background_image ? data.background_image + '?imageMogr2/thumbnail/800x/strip/quality/80' : defaultBg
      qrCodeUrl.value = data?.qrcode || ''
      isReady.value = true
    }
  } else {
      // 如果没有参数,也设置为 ready,显示默认背景
      isReady.value = true
  }
})
</script>

<style lang="less" scoped>
:deep(.van-uploader__input-wrapper) {
  width: 100%;
}
</style>