2025-06-18 15:58:13 +09:00

318 lines
8.1 KiB
Vue

//* src/components/poster/PosterPreview.vue
<template>
<div class="poster-preview">
<!-- 로딩 상태 -->
<div v-if="loading" class="text-center py-12">
<v-progress-circular
indeterminate
size="64"
color="primary"
class="mb-4"
/>
<div class="text-h6">포스터 생성 ...</div>
<div class="text-body-2 text-grey-600 mt-2">AI가 멋진 포스터를 만들고 있어요</div>
</div>
<!-- 포스터 없음 -->
<div v-else-if="!posterData" class="text-center py-12">
<v-icon size="80" color="grey-lighten-2" class="mb-4">
mdi-image-outline
</v-icon>
<div class="text-h6 mb-2">포스터 미리보기</div>
<div class="text-body-2 text-grey-600">
좌측 폼을 작성하고 'AI 포스터 생성' 버튼을 클릭하세요
</div>
</div>
<!-- 생성된 포스터 -->
<div v-else class="poster-result">
<!-- 포스터 이미지 -->
<div class="poster-image-container mb-4">
<v-img
:src="posterData.posterImage || '/images/placeholder-poster.jpg'"
:alt="posterData.title"
cover
class="rounded-lg elevation-4"
style="aspect-ratio: 3/4; max-height: 400px;"
>
<template v-slot:placeholder>
<div class="d-flex align-center justify-center fill-height">
<v-progress-circular indeterminate />
</div>
</template>
<template v-slot:error>
<div class="d-flex align-center justify-center fill-height bg-grey-lighten-3">
<v-icon size="48" color="grey">mdi-image-broken</v-icon>
</div>
</template>
</v-img>
<!-- 이미지 액션 버튼 -->
<div class="image-actions mt-3">
<v-btn
size="small"
variant="outlined"
prepend-icon="mdi-download"
@click="downloadPoster"
class="mr-2"
>
다운로드
</v-btn>
<v-btn
size="small"
variant="outlined"
prepend-icon="mdi-share-variant"
@click="sharePoster"
>
공유
</v-btn>
</div>
</div>
<!-- 포스터 정보 -->
<v-card variant="outlined" class="mb-4">
<v-card-title class="text-h6">
{{ posterData.title }}
</v-card-title>
<v-card-text>
<div v-if="posterData.content" class="mb-3">
<div class="text-subtitle-2 mb-1">포스터 내용:</div>
<div class="text-body-2">{{ posterData.content }}</div>
</div>
<div class="d-flex align-center mb-2">
<v-chip
:color="getStatusColor(posterData.status)"
size="small"
class="mr-2"
>
{{ getStatusText(posterData.status) }}
</v-chip>
<v-chip
color="primary"
size="small"
variant="outlined"
>
{{ posterData.contentType }}
</v-chip>
</div>
<div v-if="posterData.imageStyle" class="text-caption text-grey-600">
스타일: {{ posterData.imageStyle }}
</div>
</v-card-text>
</v-card>
<!-- 포스터 사이즈 옵션 -->
<v-card v-if="posterData.posterSizes && Object.keys(posterData.posterSizes).length > 0" variant="outlined">
<v-card-title class="text-subtitle-1">
<v-icon class="mr-2">mdi-resize</v-icon>
다양한 사이즈
</v-card-title>
<v-card-text>
<div class="d-flex flex-wrap gap-2">
<v-chip
v-for="(url, size) in posterData.posterSizes"
:key="size"
@click="viewPosterSize(size, url)"
class="cursor-pointer"
variant="outlined"
>
{{ size }}
</v-chip>
</div>
</v-card-text>
</v-card>
<!-- 원본 이미지들 -->
<div v-if="posterData.originalImages && posterData.originalImages.length > 0" class="mt-4">
<div class="text-subtitle-2 mb-2">사용된 원본 이미지:</div>
<v-row>
<v-col
v-for="(image, index) in posterData.originalImages"
:key="index"
cols="6"
sm="4"
>
<v-img
:src="image"
:alt="`원본 이미지 ${index + 1}`"
cover
height="80"
class="rounded"
/>
</v-col>
</v-row>
</div>
</div>
<!-- 포스터 사이즈 보기 다이얼로그 -->
<v-dialog v-model="showSizeDialog" max-width="600">
<v-card>
<v-card-title>
포스터 사이즈: {{ selectedSize }}
</v-card-title>
<v-card-text class="text-center">
<v-img
:src="selectedSizeUrl"
:alt="`포스터 ${selectedSize}`"
contain
max-height="400"
/>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="primary"
prepend-icon="mdi-download"
@click="downloadSelectedSize"
>
다운로드
</v-btn>
<v-btn @click="showSizeDialog = false">
닫기
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script setup>
import { ref } from 'vue'
/**
* 포스터 미리보기 컴포넌트
* - 생성된 포스터 표시
* - 다운로드 및 공유 기능
* - 다양한 사이즈 보기
*/
// Props 정의
const props = defineProps({
posterData: {
type: Object,
default: null
},
loading: {
type: Boolean,
default: false
}
})
// 반응형 데이터
const showSizeDialog = ref(false)
const selectedSize = ref('')
const selectedSizeUrl = ref('')
/**
* 상태 색상 반환
*/
const getStatusColor = (status) => {
const colors = {
'DRAFT': 'orange',
'PUBLISHED': 'success',
'ARCHIVED': 'grey'
}
return colors[status] || 'primary'
}
/**
* 상태 텍스트 반환
*/
const getStatusText = (status) => {
const texts = {
'DRAFT': '임시저장',
'PUBLISHED': '발행',
'ARCHIVED': '보관'
}
return texts[status] || status
}
/**
* 포스터 다운로드
*/
const downloadPoster = () => {
if (!props.posterData?.posterImage) return
const link = document.createElement('a')
link.href = props.posterData.posterImage
link.download = `${props.posterData.title || '포스터'}.jpg`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
/**
* 포스터 공유
*/
const sharePoster = async () => {
if (!props.posterData?.posterImage) return
if (navigator.share) {
try {
await navigator.share({
title: props.posterData.title,
text: '생성된 홍보 포스터를 확인해보세요!',
url: props.posterData.posterImage
})
} catch (error) {
console.log('공유 취소됨')
}
} else {
// 클립보드에 URL 복사
try {
await navigator.clipboard.writeText(props.posterData.posterImage)
// 성공 알림 표시 (부모 컴포넌트에서 처리)
} catch (error) {
console.error('클립보드 복사 실패:', error)
}
}
}
/**
* 특정 사이즈 포스터 보기
*/
const viewPosterSize = (size, url) => {
selectedSize.value = size
selectedSizeUrl.value = url
showSizeDialog.value = true
}
/**
* 선택된 사이즈 포스터 다운로드
*/
const downloadSelectedSize = () => {
const link = document.createElement('a')
link.href = selectedSizeUrl.value
link.download = `${props.posterData.title || '포스터'}_${selectedSize.value}.jpg`
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
</script>
<style scoped>
.poster-preview {
min-height: 400px;
}
.poster-image-container {
position: relative;
}
.image-actions {
display: flex;
justify-content: center;
}
.cursor-pointer {
cursor: pointer;
}
</style>