edit views
This commit is contained in:
parent
9d1d11108f
commit
1d06854f94
@ -16,12 +16,12 @@
|
|||||||
:items="stepperItems"
|
:items="stepperItems"
|
||||||
alt-labels
|
alt-labels
|
||||||
>
|
>
|
||||||
|
<!-- Stepper Window로 각 단계 구현 -->
|
||||||
|
<v-stepper-window>
|
||||||
|
|
||||||
<!-- Step 1: 콘텐츠 타입 선택 -->
|
<!-- Step 1: 콘텐츠 타입 선택 -->
|
||||||
<template v-slot:item.1>
|
<v-stepper-window-item value="1">
|
||||||
<v-card
|
<v-card class="pa-4" flat>
|
||||||
class="pa-4"
|
|
||||||
flat
|
|
||||||
>
|
|
||||||
<h3 class="text-h6 mb-4">어떤 콘텐츠를 만들까요?</h3>
|
<h3 class="text-h6 mb-4">어떤 콘텐츠를 만들까요?</h3>
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
@ -54,26 +54,17 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
<!-- Step 2: 홍보 대상 선택 -->
|
<!-- Step 2: 홍보 대상 선택 -->
|
||||||
<template v-slot:item.2>
|
<v-stepper-window-item value="2">
|
||||||
<v-card class="pa-4" flat>
|
<v-card class="pa-4" flat>
|
||||||
<h3 class="text-h6 mb-4">무엇을 홍보할까요?</h3>
|
<h3 class="text-h6 mb-4">무엇을 홍보할까요?</h3>
|
||||||
|
|
||||||
<v-radio-group v-model="contentData.target">
|
<v-radio-group v-model="contentData.target">
|
||||||
<v-radio
|
<v-radio label="🍜 메뉴 홍보" value="menu" />
|
||||||
label="🍜 메뉴 홍보"
|
<v-radio label="🏪 매장 소개" value="store" />
|
||||||
value="menu"
|
<v-radio label="🎉 이벤트 홍보" value="event" />
|
||||||
/>
|
|
||||||
<v-radio
|
|
||||||
label="🏪 매장 소개"
|
|
||||||
value="store"
|
|
||||||
/>
|
|
||||||
<v-radio
|
|
||||||
label="🎉 이벤트 홍보"
|
|
||||||
value="event"
|
|
||||||
/>
|
|
||||||
</v-radio-group>
|
</v-radio-group>
|
||||||
|
|
||||||
<!-- 메뉴 선택 -->
|
<!-- 메뉴 선택 -->
|
||||||
@ -98,10 +89,10 @@
|
|||||||
class="mt-4"
|
class="mt-4"
|
||||||
/>
|
/>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
<!-- Step 3: 이미지 업로드 -->
|
<!-- Step 3: 이미지 업로드 -->
|
||||||
<template v-slot:item.3>
|
<v-stepper-window-item value="3">
|
||||||
<v-card class="pa-4" flat>
|
<v-card class="pa-4" flat>
|
||||||
<h3 class="text-h6 mb-4">이미지를 업로드하세요</h3>
|
<h3 class="text-h6 mb-4">이미지를 업로드하세요</h3>
|
||||||
|
|
||||||
@ -126,19 +117,15 @@
|
|||||||
md="3"
|
md="3"
|
||||||
>
|
>
|
||||||
<v-card class="pa-2">
|
<v-card class="pa-2">
|
||||||
<v-img
|
<v-img :src="url" height="100" cover />
|
||||||
:src="url"
|
|
||||||
height="100"
|
|
||||||
cover
|
|
||||||
/>
|
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
<!-- Step 4: 세부 옵션 -->
|
<!-- Step 4: 세부 옵션 -->
|
||||||
<template v-slot:item.4>
|
<v-stepper-window-item value="4">
|
||||||
<v-card class="pa-4" flat>
|
<v-card class="pa-4" flat>
|
||||||
<h3 class="text-h6 mb-4">세부 옵션을 설정하세요</h3>
|
<h3 class="text-h6 mb-4">세부 옵션을 설정하세요</h3>
|
||||||
|
|
||||||
@ -212,10 +199,10 @@
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
<!-- Step 5: 생성 결과 -->
|
<!-- Step 5: 생성 결과 -->
|
||||||
<template v-slot:item.5>
|
<v-stepper-window-item value="5">
|
||||||
<v-card class="pa-4" flat>
|
<v-card class="pa-4" flat>
|
||||||
<h3 class="text-h6 mb-4">생성된 콘텐츠</h3>
|
<h3 class="text-h6 mb-4">생성된 콘텐츠</h3>
|
||||||
|
|
||||||
@ -274,7 +261,9 @@
|
|||||||
<p class="text-body-2">{{ generationError }}</p>
|
<p class="text-body-2">{{ generationError }}</p>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
|
</v-stepper-window>
|
||||||
</v-stepper>
|
</v-stepper>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
|
|
||||||
@ -347,244 +336,3 @@
|
|||||||
</v-snackbar>
|
</v-snackbar>
|
||||||
</v-container>
|
</v-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, computed, watch, onMounted } from 'vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { useContentStore } from '@/store/content'
|
|
||||||
import { useStoreStore } from '@/store/store'
|
|
||||||
import { TONE_OPTIONS, EMOTION_INTENSITY, PROMOTION_OPTIONS, PLATFORMS } from '@/utils/constants'
|
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const contentStore = useContentStore()
|
|
||||||
const storeStore = useStoreStore()
|
|
||||||
|
|
||||||
// 스테퍼 설정
|
|
||||||
const currentStep = ref(1)
|
|
||||||
const stepperItems = [
|
|
||||||
{ title: '타입 선택', value: 1 },
|
|
||||||
{ title: '홍보 대상', value: 2 },
|
|
||||||
{ title: '이미지 업로드', value: 3 },
|
|
||||||
{ title: '세부 옵션', value: 4 },
|
|
||||||
{ title: '생성 완료', value: 5 }
|
|
||||||
]
|
|
||||||
|
|
||||||
// 콘텐츠 데이터
|
|
||||||
const contentData = ref({
|
|
||||||
type: '',
|
|
||||||
target: '',
|
|
||||||
selectedMenu: null,
|
|
||||||
eventName: '',
|
|
||||||
images: [],
|
|
||||||
toneAndManner: '친근함',
|
|
||||||
emotionIntensity: '보통',
|
|
||||||
promotion: '없음',
|
|
||||||
platform: 'INSTAGRAM',
|
|
||||||
startDate: '',
|
|
||||||
endDate: ''
|
|
||||||
})
|
|
||||||
|
|
||||||
// 상태 관리
|
|
||||||
const generating = ref(false)
|
|
||||||
const saving = ref(false)
|
|
||||||
const generatedContent = ref(null)
|
|
||||||
const generationError = ref('')
|
|
||||||
const showSuccess = ref(false)
|
|
||||||
const showError = ref(false)
|
|
||||||
const errorMessage = ref('')
|
|
||||||
|
|
||||||
// 이미지 미리보기
|
|
||||||
const imagePreviewUrls = ref([])
|
|
||||||
|
|
||||||
// 계산된 속성
|
|
||||||
const canProceed = computed(() => {
|
|
||||||
switch (currentStep.value) {
|
|
||||||
case 1:
|
|
||||||
return !!contentData.value.type
|
|
||||||
case 2:
|
|
||||||
if (contentData.value.target === 'menu') {
|
|
||||||
return !!contentData.value.selectedMenu
|
|
||||||
} else if (contentData.value.target === 'event') {
|
|
||||||
return !!contentData.value.eventName
|
|
||||||
}
|
|
||||||
return !!contentData.value.target
|
|
||||||
case 3:
|
|
||||||
return contentData.value.images.length > 0
|
|
||||||
case 4:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const menuOptions = computed(() => {
|
|
||||||
return storeStore.menus.map(menu => ({
|
|
||||||
text: menu.menuName,
|
|
||||||
value: menu.id
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
|
|
||||||
const platformOptions = [
|
|
||||||
{ text: '인스타그램', value: 'INSTAGRAM' },
|
|
||||||
{ text: '네이버 블로그', value: 'NAVER_BLOG' }
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 다음 단계로 이동
|
|
||||||
*/
|
|
||||||
const nextStep = async () => {
|
|
||||||
if (currentStep.value === 4) {
|
|
||||||
await generateContent()
|
|
||||||
} else {
|
|
||||||
currentStep.value++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 콘텐츠 생성
|
|
||||||
*/
|
|
||||||
const generateContent = async () => {
|
|
||||||
generating.value = true
|
|
||||||
generationError.value = ''
|
|
||||||
currentStep.value = 5
|
|
||||||
|
|
||||||
try {
|
|
||||||
let response
|
|
||||||
|
|
||||||
if (contentData.value.type === 'SNS_POST') {
|
|
||||||
response = await contentStore.generateSNSContent({
|
|
||||||
target: contentData.value.target,
|
|
||||||
selectedMenu: contentData.value.selectedMenu,
|
|
||||||
eventName: contentData.value.eventName,
|
|
||||||
images: contentData.value.images,
|
|
||||||
toneAndManner: contentData.value.toneAndManner,
|
|
||||||
emotionIntensity: contentData.value.emotionIntensity,
|
|
||||||
promotion: contentData.value.promotion,
|
|
||||||
platform: contentData.value.platform,
|
|
||||||
startDate: contentData.value.startDate,
|
|
||||||
endDate: contentData.value.endDate
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
response = await contentStore.generatePosterContent({
|
|
||||||
target: contentData.value.target,
|
|
||||||
selectedMenu: contentData.value.selectedMenu,
|
|
||||||
eventName: contentData.value.eventName,
|
|
||||||
images: contentData.value.images,
|
|
||||||
toneAndManner: contentData.value.toneAndManner,
|
|
||||||
promotion: contentData.value.promotion,
|
|
||||||
startDate: contentData.value.startDate,
|
|
||||||
endDate: contentData.value.endDate
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
generatedContent.value = response
|
|
||||||
} catch (error) {
|
|
||||||
console.error('콘텐츠 생성 실패:', error)
|
|
||||||
generationError.value = '콘텐츠 생성 중 오류가 발생했습니다.'
|
|
||||||
|
|
||||||
// 샘플 데이터로 대체
|
|
||||||
generatedContent.value = {
|
|
||||||
title: '신메뉴 떡볶이 출시!',
|
|
||||||
content: `🔥 새로운 맛의 떡볶이가 출시되었어요! 🔥
|
|
||||||
|
|
||||||
매콤달콤한 특제 소스로 만든 우리 매장만의 시그니처 떡볶이를 맛보세요!
|
|
||||||
|
|
||||||
신선한 떡과 정성스럽게 끓인 국물의 조화가 일품입니다.
|
|
||||||
|
|
||||||
지금 방문하시면 특별 할인가로 만나보실 수 있어요! ✨`,
|
|
||||||
hashtags: ['#떡볶이', '#신메뉴', '#분식맛집', '#김사장님분식점', '#매운맛', '#달콤한맛']
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
generating.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 콘텐츠 재생성
|
|
||||||
*/
|
|
||||||
const regenerateContent = async () => {
|
|
||||||
generatedContent.value = null
|
|
||||||
await generateContent()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 콘텐츠 저장
|
|
||||||
*/
|
|
||||||
const saveContent = async () => {
|
|
||||||
saving.value = true
|
|
||||||
try {
|
|
||||||
const saveData = {
|
|
||||||
...contentData.value,
|
|
||||||
...generatedContent.value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contentData.value.type === 'SNS_POST') {
|
|
||||||
await contentStore.saveSNSContent(saveData)
|
|
||||||
} else {
|
|
||||||
await contentStore.savePosterContent(saveData)
|
|
||||||
}
|
|
||||||
|
|
||||||
showSuccess.value = true
|
|
||||||
|
|
||||||
// 잠시 후 콘텐츠 관리 페이지로 이동
|
|
||||||
setTimeout(() => {
|
|
||||||
router.push({ name: 'ContentManagement' })
|
|
||||||
}, 2000)
|
|
||||||
} catch (error) {
|
|
||||||
console.error('콘텐츠 저장 실패:', error)
|
|
||||||
errorMessage.value = '콘텐츠 저장 중 오류가 발생했습니다.'
|
|
||||||
showError.value = true
|
|
||||||
} finally {
|
|
||||||
saving.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 이미지 파일 변경 감지
|
|
||||||
watch(() => contentData.value.images, (newImages) => {
|
|
||||||
imagePreviewUrls.value = []
|
|
||||||
|
|
||||||
if (newImages && newImages.length > 0) {
|
|
||||||
newImages.forEach(file => {
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.onload = (e) => {
|
|
||||||
imagePreviewUrls.value.push(e.target.result)
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 컴포넌트 마운트시 데이터 로드
|
|
||||||
onMounted(async () => {
|
|
||||||
try {
|
|
||||||
await storeStore.fetchMenus()
|
|
||||||
} catch (error) {
|
|
||||||
console.error('메뉴 데이터 로드 실패:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 기본 날짜 설정
|
|
||||||
const today = new Date().toISOString().split('T')[0]
|
|
||||||
const nextWeek = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]
|
|
||||||
|
|
||||||
contentData.value.startDate = today
|
|
||||||
contentData.value.endDate = nextWeek
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.content-type-card {
|
|
||||||
transition: all 0.3s;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-type-card:hover {
|
|
||||||
transform: translateY(-4px);
|
|
||||||
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-type-card.selected {
|
|
||||||
border-color: #1976D2;
|
|
||||||
background-color: #E3F2FD;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -979,7 +979,8 @@ onMounted(async () => {
|
|||||||
|
|
||||||
.content-preview {
|
.content-preview {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
-webkit-line-clamp: 3;
|
line-clamp: 3; /* 표준 속성 추가 */
|
||||||
|
-webkit-line-clamp: 3; /* 웹킷 fallback */
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user