release
This commit is contained in:
commit
336f7dd8eb
@ -1,100 +1,48 @@
|
|||||||
//* src/views/ContentManagementView.vue
|
//* src/views/ContentManagementView.vue
|
||||||
<template>
|
<template>
|
||||||
<v-container fluid class="pa-4">
|
<v-container fluid class="pa-4">
|
||||||
<!-- 콘텐츠 타입 필터 - 칩 형태로 변경 -->
|
<!-- 상단 헤더 - 제목과 생성 버튼만 -->
|
||||||
<div class="mb-6">
|
<div class="d-flex justify-space-between align-center mb-6">
|
||||||
<div class="d-flex align-center flex-wrap ga-2">
|
<!-- 새 콘텐츠 생성 버튼 -->
|
||||||
<span class="text-subtitle-2 font-weight-medium mr-3">콘텐츠 타입:</span>
|
<v-btn
|
||||||
|
color="primary"
|
||||||
<!-- 전체 필터 -->
|
size="large"
|
||||||
<v-chip
|
prepend-icon="mdi-plus"
|
||||||
:color="selectedContentType === 'all' ? 'primary' : 'default'"
|
@click="$router.push('/content/create')"
|
||||||
:variant="selectedContentType === 'all' ? 'flat' : 'outlined'"
|
>
|
||||||
class="cursor-pointer"
|
새 콘텐츠 생성
|
||||||
@click="selectContentType('all')"
|
</v-btn>
|
||||||
>
|
|
||||||
<v-icon start size="16">mdi-view-grid</v-icon>
|
|
||||||
전체 ({{ getTotalCount() }})
|
|
||||||
</v-chip>
|
|
||||||
|
|
||||||
<!-- Instagram 필터 -->
|
|
||||||
<v-chip
|
|
||||||
:color="selectedContentType === 'instagram' ? 'pink' : 'default'"
|
|
||||||
:variant="selectedContentType === 'instagram' ? 'flat' : 'outlined'"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="selectContentType('instagram')"
|
|
||||||
>
|
|
||||||
<v-icon start size="16" color="pink">mdi-instagram</v-icon>
|
|
||||||
Instagram ({{ getTypeCount('instagram') }})
|
|
||||||
</v-chip>
|
|
||||||
|
|
||||||
<!-- 네이버 블로그 필터 -->
|
|
||||||
<v-chip
|
|
||||||
:color="selectedContentType === 'blog' ? 'green' : 'default'"
|
|
||||||
:variant="selectedContentType === 'blog' ? 'flat' : 'outlined'"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="selectContentType('blog')"
|
|
||||||
>
|
|
||||||
<v-icon start size="16" color="green">mdi-blogger</v-icon>
|
|
||||||
네이버 블로그 ({{ getTypeCount('blog') }})
|
|
||||||
</v-chip>
|
|
||||||
|
|
||||||
<!-- 포스터 필터 -->
|
|
||||||
<v-chip
|
|
||||||
:color="selectedContentType === 'poster' ? 'orange' : 'default'"
|
|
||||||
:variant="selectedContentType === 'poster' ? 'flat' : 'outlined'"
|
|
||||||
class="cursor-pointer"
|
|
||||||
@click="selectContentType('poster')"
|
|
||||||
>
|
|
||||||
<v-icon start size="16" color="orange">mdi-file-image</v-icon>
|
|
||||||
포스터 ({{ getTypeCount('poster') }})
|
|
||||||
</v-chip>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 추가 필터 및 정렬 -->
|
<!-- 필터 영역 - 통합된 필터 -->
|
||||||
<v-card class="mb-6">
|
<v-card class="mb-6">
|
||||||
<v-card-text>
|
<v-card-text>
|
||||||
<v-row align="center">
|
<v-row align="center">
|
||||||
<!-- 추가 필터 -->
|
<!-- 콘텐츠 타입 필터 -->
|
||||||
<v-col cols="12" md="6">
|
<v-col cols="12" md="3">
|
||||||
<!-- 검색 -->
|
<v-select
|
||||||
<v-spacer />
|
v-model="selectedContentType"
|
||||||
|
:items="contentTypeOptions"
|
||||||
|
label="콘텐츠 타입"
|
||||||
|
variant="outlined"
|
||||||
|
density="compact"
|
||||||
|
prepend-inner-icon="mdi-filter-variant"
|
||||||
|
@update:model-value="applyFilters"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
|
||||||
|
<!-- 제목 검색 -->
|
||||||
|
<v-col cols="12" md="4">
|
||||||
<v-text-field
|
<v-text-field
|
||||||
v-model="searchQuery"
|
v-model="searchQuery"
|
||||||
label="제목, 해시태그로 검색"
|
label="제목, 해시태그로 검색"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
density="compact"
|
density="compact"
|
||||||
style="min-width: 300px;"
|
|
||||||
clearable
|
clearable
|
||||||
@update:model-value="applyFilters"
|
@update:model-value="applyFilters"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<!-- 정렬 및 기간 필터 -->
|
|
||||||
<v-col cols="12" md="3">
|
|
||||||
<v-select
|
|
||||||
v-model="filters.period"
|
|
||||||
:items="periodOptions"
|
|
||||||
label="생성 기간"
|
|
||||||
variant="outlined"
|
|
||||||
density="compact"
|
|
||||||
@update:model-value="applyFilters"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col cols="12" md="3">
|
|
||||||
<!-- 새 콘텐츠 생성 버튼 -->
|
|
||||||
<v-btn
|
|
||||||
color="primary"
|
|
||||||
size="large"
|
|
||||||
prepend-icon="mdi-plus"
|
|
||||||
@click="$router.push('/content/create')"
|
|
||||||
>
|
|
||||||
새 콘텐츠 생성
|
|
||||||
</v-btn>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
@ -135,7 +83,7 @@
|
|||||||
</v-btn>
|
</v-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 리스트 뷰 - 테이블 형태 -->
|
<!-- 리스트 뷰 - 정렬 가능한 테이블 -->
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<v-table>
|
<v-table>
|
||||||
<thead>
|
<thead>
|
||||||
@ -149,7 +97,27 @@
|
|||||||
</th>
|
</th>
|
||||||
<th width="450">제목</th>
|
<th width="450">제목</th>
|
||||||
<th width="150">플랫폼</th>
|
<th width="150">플랫폼</th>
|
||||||
<th width="200">프로모션 기간</th>
|
<!-- 프로모션 기간 - 정렬 가능 -->
|
||||||
|
<th
|
||||||
|
width="200"
|
||||||
|
class="sortable-header cursor-pointer"
|
||||||
|
@click="sortByPromotionDate"
|
||||||
|
>
|
||||||
|
<div class="d-flex align-center">
|
||||||
|
<span>프로모션 기간</span>
|
||||||
|
<v-icon
|
||||||
|
:color="promotionSortOrder === 'none' ? 'grey-lighten-1' : 'primary'"
|
||||||
|
size="16"
|
||||||
|
class="ml-1"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
promotionSortOrder === 'asc' ? 'mdi-arrow-up' :
|
||||||
|
promotionSortOrder === 'desc' ? 'mdi-arrow-down' :
|
||||||
|
'mdi-unfold-more-horizontal'
|
||||||
|
}}
|
||||||
|
</v-icon>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
<th width="120">액션</th>
|
<th width="120">액션</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -429,6 +397,7 @@ import { useContentStore } from '@/store/content'
|
|||||||
* - 생성된 콘텐츠 목록 조회
|
* - 생성된 콘텐츠 목록 조회
|
||||||
* - 필터링 및 검색
|
* - 필터링 및 검색
|
||||||
* - 콘텐츠 상세 보기, 수정, 삭제
|
* - 콘텐츠 상세 보기, 수정, 삭제
|
||||||
|
* - 프로모션 기간 정렬 기능
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 스토어 및 라우터
|
// 스토어 및 라우터
|
||||||
@ -446,16 +415,18 @@ const itemsPerPage = ref(20)
|
|||||||
// 콘텐츠 타입 필터
|
// 콘텐츠 타입 필터
|
||||||
const selectedContentType = ref('all')
|
const selectedContentType = ref('all')
|
||||||
|
|
||||||
// 기존 필터 상태
|
// 기존 필터 상태 (생성 기간 제거)
|
||||||
const filters = ref({
|
const filters = ref({
|
||||||
published: false,
|
published: false,
|
||||||
draft: false,
|
draft: false
|
||||||
period: '전체'
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 정렬 상태
|
// 정렬 상태
|
||||||
const sortBy = ref('latest')
|
const sortBy = ref('latest')
|
||||||
|
|
||||||
|
// 프로모션 기간 정렬 상태
|
||||||
|
const promotionSortOrder = ref('none') // 'none', 'asc', 'desc'
|
||||||
|
|
||||||
// 다이얼로그 상태
|
// 다이얼로그 상태
|
||||||
const showDetailDialog = ref(false)
|
const showDetailDialog = ref(false)
|
||||||
const selectedContent = ref(null)
|
const selectedContent = ref(null)
|
||||||
@ -472,6 +443,13 @@ const successMessage = ref('')
|
|||||||
const errorMessage = ref('')
|
const errorMessage = ref('')
|
||||||
|
|
||||||
// 옵션 데이터
|
// 옵션 데이터
|
||||||
|
const contentTypeOptions = [
|
||||||
|
{ title: '전체', value: 'all' },
|
||||||
|
{ title: 'Instagram', value: 'instagram' },
|
||||||
|
{ title: '네이버 블로그', value: 'blog' },
|
||||||
|
{ title: '포스터', value: 'poster' }
|
||||||
|
]
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
{ title: '발행됨', value: 'published' },
|
{ title: '발행됨', value: 'published' },
|
||||||
{ title: '임시저장', value: 'draft' },
|
{ title: '임시저장', value: 'draft' },
|
||||||
@ -485,14 +463,6 @@ const sortOptions = [
|
|||||||
{ title: '조회수순', value: 'views' }
|
{ title: '조회수순', value: 'views' }
|
||||||
]
|
]
|
||||||
|
|
||||||
const periodOptions = [
|
|
||||||
{ title: '전체', value: '전체' },
|
|
||||||
{ title: '최근 1주일', value: '1주일' },
|
|
||||||
{ title: '최근 1개월', value: '1개월' },
|
|
||||||
{ title: '최근 3개월', value: '3개월' },
|
|
||||||
{ title: '최근 6개월', value: '6개월' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const titleRules = [
|
const titleRules = [
|
||||||
v => !!v || '제목을 입력해주세요',
|
v => !!v || '제목을 입력해주세요',
|
||||||
v => v.length <= 100 || '제목은 100자 이내로 입력해주세요'
|
v => v.length <= 100 || '제목은 100자 이내로 입력해주세요'
|
||||||
@ -525,46 +495,35 @@ const filteredContents = computed(() => {
|
|||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기간 필터
|
// 정렬 (프로모션 기간 정렬이 활성화되어 있지 않을 때만)
|
||||||
if (filters.value.period !== '전체') {
|
if (promotionSortOrder.value === 'none') {
|
||||||
const now = new Date()
|
switch (sortBy.value) {
|
||||||
const startDate = new Date()
|
case 'latest':
|
||||||
|
contents.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
||||||
switch (filters.value.period) {
|
|
||||||
case '1주일':
|
|
||||||
startDate.setDate(now.getDate() - 7)
|
|
||||||
break
|
break
|
||||||
case '1개월':
|
case 'oldest':
|
||||||
startDate.setMonth(now.getMonth() - 1)
|
contents.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))
|
||||||
break
|
break
|
||||||
case '3개월':
|
case 'title':
|
||||||
startDate.setMonth(now.getMonth() - 3)
|
contents.sort((a, b) => a.title.localeCompare(b.title))
|
||||||
break
|
break
|
||||||
case '6개월':
|
case 'views':
|
||||||
startDate.setMonth(now.getMonth() - 6)
|
contents.sort((a, b) => (b.views || 0) - (a.views || 0))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
contents = contents.filter(content =>
|
// 프로모션 기간 정렬
|
||||||
new Date(content.createdAt) >= startDate
|
contents.sort((a, b) => {
|
||||||
)
|
const dateA = new Date(a.startDate || 0)
|
||||||
}
|
const dateB = new Date(b.startDate || 0)
|
||||||
|
|
||||||
// 정렬
|
if (promotionSortOrder.value === 'asc') {
|
||||||
switch (sortBy.value) {
|
return dateA - dateB
|
||||||
case 'latest':
|
} else {
|
||||||
contents.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
return dateB - dateA
|
||||||
break
|
}
|
||||||
case 'oldest':
|
})
|
||||||
contents.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))
|
|
||||||
break
|
|
||||||
case 'title':
|
|
||||||
contents.sort((a, b) => a.title.localeCompare(b.title))
|
|
||||||
break
|
|
||||||
case 'views':
|
|
||||||
contents.sort((a, b) => (b.views || 0) - (a.views || 0))
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return contents
|
return contents
|
||||||
@ -675,17 +634,35 @@ const formatDateRange = (startDate, endDate) => {
|
|||||||
return `${start} ~ ${end}`
|
return `${start} ~ ${end}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 필터 관련 메서드
|
// 정렬 관련 메서드
|
||||||
const selectContentType = (type) => {
|
const sortByPromotionDate = () => {
|
||||||
selectedContentType.value = type
|
// 프로모션 기간 정렬 토글
|
||||||
|
if (promotionSortOrder.value === 'none') {
|
||||||
|
promotionSortOrder.value = 'asc'
|
||||||
|
} else if (promotionSortOrder.value === 'asc') {
|
||||||
|
promotionSortOrder.value = 'desc'
|
||||||
|
} else {
|
||||||
|
promotionSortOrder.value = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 다른 정렬 옵션을 리셋
|
||||||
|
if (promotionSortOrder.value !== 'none') {
|
||||||
|
sortBy.value = 'latest' // 기본값으로 리셋
|
||||||
|
}
|
||||||
|
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 필터 관련 메서드
|
||||||
const applyFilters = () => {
|
const applyFilters = () => {
|
||||||
|
// 필터 적용 시 프로모션 정렬 리셋
|
||||||
|
promotionSortOrder.value = 'none'
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const applySorting = () => {
|
const applySorting = () => {
|
||||||
|
// 다른 정렬 적용 시 프로모션 정렬 리셋
|
||||||
|
promotionSortOrder.value = 'none'
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,6 +784,14 @@ onMounted(async () => {
|
|||||||
watch(selectedItems, (newVal) => {
|
watch(selectedItems, (newVal) => {
|
||||||
selectAll.value = newVal.length === paginatedContents.value.length && newVal.length > 0
|
selectAll.value = newVal.length === paginatedContents.value.length && newVal.length > 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 프로모션 정렬 상태가 변경될 때 다른 정렬 옵션 리셋
|
||||||
|
watch(promotionSortOrder, (newVal) => {
|
||||||
|
if (newVal !== 'none') {
|
||||||
|
// 프로모션 정렬이 활성화될 때는 다른 정렬 옵션을 비활성화
|
||||||
|
console.log(`프로모션 기간 정렬: ${newVal === 'asc' ? '오름차순' : '내림차순'}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -820,6 +805,19 @@ watch(selectedItems, (newVal) => {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 정렬 가능한 헤더 스타일 */
|
||||||
|
.sortable-header {
|
||||||
|
transition: background-color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-header:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-header .v-icon {
|
||||||
|
transition: color 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
/* 버튼 hover 효과 */
|
/* 버튼 hover 효과 */
|
||||||
.v-btn {
|
.v-btn {
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
@ -855,18 +853,6 @@ watch(selectedItems, (newVal) => {
|
|||||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 칩 필터 스타일링 */
|
|
||||||
.v-chip.cursor-pointer:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
transition: transform 0.2s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 플랫폼 칩 특별 스타일링 */
|
|
||||||
.v-chip.v-chip--variant-flat {
|
|
||||||
font-weight: 600;
|
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 비활성화된 입력 필드 스타일 */
|
/* 비활성화된 입력 필드 스타일 */
|
||||||
.bg-grey-lighten-4 {
|
.bg-grey-lighten-4 {
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
@ -883,25 +869,52 @@ watch(selectedItems, (newVal) => {
|
|||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #495057;
|
color: #495057;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-table tbody td {
|
.v-table tbody td {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 필터 카드 스타일 */
|
||||||
|
.v-card {
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
/* 반응형 디자인 */
|
/* 반응형 디자인 */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.d-flex.align-center.flex-wrap.ga-2 {
|
.d-flex.justify-space-between.align-center {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d-flex.align-center.flex-wrap.ga-2 .v-chip {
|
.v-row .v-col {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-text-field {
|
.v-table {
|
||||||
min-width: 100% !important;
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-table th,
|
||||||
|
.v-table td {
|
||||||
|
padding: 8px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-header {
|
||||||
|
font-size: 0.8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 정렬 아이콘 애니메이션 */
|
||||||
|
@keyframes sortActive {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.1); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sortable-header .v-icon[style*="color: rgb(25, 118, 210)"] {
|
||||||
|
animation: sortActive 0.3s ease-in-out;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user