223 lines
4.3 KiB
Vue
223 lines
4.3 KiB
Vue
<template>
|
||
<div class="couplet-display-modal" v-if="visible">
|
||
<div class="modal-overlay"></div>
|
||
<div class="modal-content couplet-content">
|
||
<div class="modal-body">
|
||
<div class="couplet-display">
|
||
<!-- 显示海报图片(后端直接返回) -->
|
||
<div class="poster-image" v-if="couplet?.image_url">
|
||
<!-- 加载提示 -->
|
||
<div class="image-loading" v-if="isLoading">
|
||
<div class="loading-spinner"></div>
|
||
<div class="loading-text">海报加载中...</div>
|
||
</div>
|
||
<img
|
||
:src="base64Image"
|
||
alt="春联海报"
|
||
style="width: 100%; max-width: 300px; height: auto;"
|
||
@load="isLoading = false"
|
||
@error="isLoading = false"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
<div class="close-button-area">
|
||
<div class="share-hint">长按海报分享</div>
|
||
|
||
<img
|
||
src="/static/images/btn_close.png"
|
||
class="close-button-image"
|
||
@click="$emit('close')"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, watch } from 'vue'
|
||
|
||
const props = defineProps({
|
||
visible: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
couplet: {
|
||
type: Object,
|
||
default: () => ({
|
||
share_url: '',
|
||
image_url: ''
|
||
})
|
||
}
|
||
})
|
||
|
||
const emit = defineEmits(['close'])
|
||
|
||
// 图片加载状态
|
||
const isLoading = ref(true)
|
||
// base64格式的图片
|
||
const base64Image = ref('')
|
||
|
||
// 将图片URL转换为base64
|
||
const convertImageToBase64 = (imageUrl) => {
|
||
if (!imageUrl) {
|
||
base64Image.value = ''
|
||
isLoading.value = false
|
||
return
|
||
}
|
||
|
||
isLoading.value = true
|
||
|
||
const img = new Image()
|
||
img.crossOrigin = 'Anonymous' // 解决跨域问题
|
||
|
||
img.onload = () => {
|
||
const canvas = document.createElement('canvas')
|
||
canvas.width = img.width
|
||
canvas.height = img.height
|
||
|
||
const ctx = canvas.getContext('2d')
|
||
ctx.drawImage(img, 0, 0)
|
||
|
||
// 将canvas转换为base64
|
||
base64Image.value = canvas.toDataURL('image/jpeg', 0.9)
|
||
isLoading.value = false
|
||
}
|
||
|
||
img.onerror = () => {
|
||
console.error('图片加载失败:', imageUrl)
|
||
base64Image.value = ''
|
||
isLoading.value = false
|
||
}
|
||
|
||
img.src = imageUrl
|
||
}
|
||
|
||
// 监听couplet变化,重新开始加载
|
||
watch(() => props.couplet?.image_url, (newImageUrl) => {
|
||
if (newImageUrl) {
|
||
convertImageToBase64(newImageUrl)
|
||
}
|
||
})
|
||
|
||
// 监听visible变化,当显示时重置加载状态
|
||
watch(() => props.visible, (newVisible) => {
|
||
if (newVisible && props.couplet?.image_url) {
|
||
convertImageToBase64(props.couplet.image_url)
|
||
}
|
||
})
|
||
|
||
// 组件挂载时初始化
|
||
if (props.couplet?.image_url) {
|
||
convertImageToBase64(props.couplet.image_url)
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.couplet-display-modal {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
z-index: 999;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.modal-overlay {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
}
|
||
|
||
.modal-content {
|
||
position: relative;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.couplet-content {
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.couplet-display {
|
||
text-align: center;
|
||
}
|
||
|
||
.poster-image {
|
||
position: relative;
|
||
display: inline-block;
|
||
}
|
||
|
||
/* 图片加载提示 */
|
||
.image-loading {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(255, 255, 255, 0.9);
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
z-index: 1;
|
||
}
|
||
|
||
.loading-spinner {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 4px solid #f3f3f3;
|
||
border-top: 4px solid #3498db;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.loading-text {
|
||
font-size: 14px;
|
||
color: #333;
|
||
text-align: center;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
.share-hint {
|
||
margin-bottom: 10rpx;
|
||
font-size: 18px;
|
||
color: #fff;
|
||
text-align: center;
|
||
|
||
}
|
||
|
||
.close-button-area {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
padding-top: 20rpx;
|
||
z-index: 99;
|
||
}
|
||
|
||
.close-button-image {
|
||
width: 61rpx;
|
||
height: 60rpx;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.close-button-image:hover {
|
||
transform: scale(1.1);
|
||
}
|
||
</style> |