hookehuyr

fix(海报生成): 修复二维码加载问题并优化加载状态处理

替换 axios 为 fetch 加载二维码以避免拦截器导致的 CORS 问题
添加加载状态处理防止未准备好时渲染组件
同时监听二维码 URL 变化触发重新生成
......@@ -21,6 +21,7 @@
<script setup>
import { ref, watch, nextTick, onMounted } from 'vue'
import { showToast } from 'vant'
// import request from '@/utils/axios' // 移除 axios 依赖,改用 fetch
const props = defineProps({
bgUrl: {
......@@ -150,13 +151,37 @@ const generatePoster = async () => {
ctx.fillStyle = '#ffffff'
ctx.fillRect(0, 0, width, height)
// 处理 API 形式的二维码 URL (如 /admin/?m=srv...)
// 如果是相对路径或特定API,使用 fetch 获取 blob 以通过鉴权并避免跨域/拦截器问题
let qrUrlToLoad = props.qrUrl
let qrBlobUrl = null
// 兼容完整 URL 和相对路径,只要包含特定特征或是相对路径
if (qrUrlToLoad && (qrUrlToLoad.startsWith('/') || qrUrlToLoad.includes('m=srv') || qrUrlToLoad.includes('http'))) {
try {
// 使用 fetch 替代 axios,避免拦截器自动添加 header 导致 CORS 预检失败
const res = await fetch(qrUrlToLoad)
if (res.ok) {
const blob = await res.blob()
qrBlobUrl = URL.createObjectURL(blob)
qrUrlToLoad = qrBlobUrl
}
} catch (err) {
console.warn('QR Code API fetch failed, falling back to direct load', err)
}
}
// 2. 并行加载所有图片资源
const [bgImg, logoImg, qrImg] = await Promise.all([
loadImage(props.bgUrl),
loadImage(props.logoUrl),
loadImage(props.qrUrl)
loadImage(qrUrlToLoad)
])
// 清理 Blob URL
if (qrBlobUrl) {
URL.revokeObjectURL(qrBlobUrl)
}
// 3. 绘制背景图 (Object-Cover 效果)
if (bgImg) {
// 计算裁剪
......@@ -321,7 +346,7 @@ const generatePoster = async () => {
}
}
watch(() => props.bgUrl, () => {
watch(() => [props.bgUrl, props.qrUrl], () => {
generatePoster()
})
......
<!--
* @Date: 2025-12-23 15:50:59
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-12-24 20:35:33
* @LastEditTime: 2025-12-25 13:07:01
* @FilePath: /mlaj/src/views/recall/PosterPage.vue
* @Description: 分享海报页面
-->
......@@ -11,11 +11,15 @@
<!-- 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) -->
......@@ -52,10 +56,11 @@ 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'
const qrCodeUrl = 'https://cdn.ipadbiz.cn/mlaj/recall/poster/%E4%BA%8C%E7%BB%B4%E7%A0%81@2x.png'
// State
const posterBg = ref(defaultBg)
const qrCodeUrl = ref('')
const isReady = ref(false)
/**
* 获取文件哈希(与七牛云ETag一致)
......@@ -163,15 +168,19 @@ const title = ref($route.query.title || '活动海报')
onMounted(async () => {
if (stu_uid.value && campaign_id.value) {
const { data } = await getPosterAPI({
const { code, data } = await getPosterAPI({
stu_uid: stu_uid.value,
campaign_id: campaign_id.value
})
if (data) {
if (code) {
posterBg.value = data.background_image + '?imageMogr2/thumbnail/800x/strip/quality/80' || defaultBg
qrCodeUrl.value = data.qrcode
qrCodeUrl.value = data?.qrcode || ''
isReady.value = true
}
} else {
// 如果没有参数,也设置为 ready,显示默认背景
isReady.value = true
}
})
</script>
......