변경3
This commit is contained in:
@@ -57,6 +57,39 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 필터/정렬 영역 -->
|
||||
<div v-if="documents.length > 0" class="filter-section">
|
||||
<div class="filter-group">
|
||||
<span class="filter-label">상태:</span>
|
||||
<label
|
||||
v-for="status in statusOptions"
|
||||
:key="status.value"
|
||||
class="filter-checkbox"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:value="status.value"
|
||||
v-model="selectedStatuses"
|
||||
/>
|
||||
<span :class="['status-tag', status.value.toLowerCase()]">
|
||||
{{ status.label }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="sort-group">
|
||||
<span class="filter-label">정렬:</span>
|
||||
<select v-model="sortOption" class="sort-select">
|
||||
<option value="uploadDesc">업로드순 (최신)</option>
|
||||
<option value="uploadAsc">업로드순 (오래된)</option>
|
||||
<option value="nameAsc">파일명 (오름차순)</option>
|
||||
<option value="nameDesc">파일명 (내림차순)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-info">
|
||||
{{ filteredDocuments.length }} / {{ documents.length }}개
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 문서 목록 -->
|
||||
<div class="doc-list">
|
||||
<div v-if="documents.length === 0" class="empty-docs">
|
||||
@@ -64,8 +97,13 @@
|
||||
<p class="hint">PDF, DOCX, TXT 파일을 업로드해보세요.</p>
|
||||
</div>
|
||||
|
||||
<div v-else-if="filteredDocuments.length === 0" class="empty-docs">
|
||||
<p>필터 조건에 맞는 문서가 없습니다.</p>
|
||||
<button class="reset-filter-btn" @click="resetFilters">필터 초기화</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="doc in documents"
|
||||
v-for="doc in filteredDocuments"
|
||||
:key="doc.docId"
|
||||
class="doc-item"
|
||||
>
|
||||
@@ -238,7 +276,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
||||
import { ref, reactive, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { topicApi, docApi } from '@/api'
|
||||
|
||||
// 상태
|
||||
@@ -246,6 +284,52 @@ const topics = ref([])
|
||||
const selectedTopic = ref(null)
|
||||
const documents = ref([])
|
||||
|
||||
// 필터/정렬 상태
|
||||
const selectedStatuses = ref(['PENDING', 'PROCESSING', 'INDEXED', 'FAILED']) // 기본 전체 선택
|
||||
const sortOption = ref('uploadDesc') // 기본: 업로드순 (최신)
|
||||
|
||||
// 상태 옵션
|
||||
const statusOptions = [
|
||||
{ value: 'PENDING', label: '⏳ 대기중' },
|
||||
{ value: 'PROCESSING', label: '⚙️ 처리중' },
|
||||
{ value: 'INDEXED', label: '✅ 완료' },
|
||||
{ value: 'FAILED', label: '❌ 실패' }
|
||||
]
|
||||
|
||||
// 필터링 + 정렬된 문서 목록
|
||||
const filteredDocuments = computed(() => {
|
||||
let result = [...documents.value]
|
||||
|
||||
// 상태 필터
|
||||
if (selectedStatuses.value.length > 0 && selectedStatuses.value.length < 4) {
|
||||
result = result.filter(doc => selectedStatuses.value.includes(doc.docStatus))
|
||||
}
|
||||
|
||||
// 정렬
|
||||
switch (sortOption.value) {
|
||||
case 'uploadDesc':
|
||||
result.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
||||
break
|
||||
case 'uploadAsc':
|
||||
result.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt))
|
||||
break
|
||||
case 'nameAsc':
|
||||
result.sort((a, b) => (a.originalName || '').localeCompare(b.originalName || '', 'ko'))
|
||||
break
|
||||
case 'nameDesc':
|
||||
result.sort((a, b) => (b.originalName || '').localeCompare(a.originalName || '', 'ko'))
|
||||
break
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
// 필터 초기화
|
||||
const resetFilters = () => {
|
||||
selectedStatuses.value = ['PENDING', 'PROCESSING', 'INDEXED', 'FAILED']
|
||||
sortOption.value = 'uploadDesc'
|
||||
}
|
||||
|
||||
// 폴링 관련
|
||||
const pollingInterval = ref(null)
|
||||
const processingDocIds = ref(new Set())
|
||||
@@ -788,6 +872,79 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
// 필터/정렬 영역
|
||||
.filter-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 12px 24px;
|
||||
background: white;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
|
||||
.filter-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.filter-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
input[type="checkbox"] {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
cursor: pointer;
|
||||
accent-color: #667eea;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
|
||||
&.pending { background: #fff3e0; color: #e65100; }
|
||||
&.processing { background: #e3f2fd; color: #1565c0; }
|
||||
&.indexed { background: #e8f5e9; color: #2e7d32; }
|
||||
&.failed { background: #ffebee; color: #c62828; }
|
||||
}
|
||||
}
|
||||
|
||||
.sort-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sort-select {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
|
||||
&:focus {
|
||||
border-color: #667eea;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-info {
|
||||
margin-left: auto;
|
||||
font-size: 13px;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
.doc-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
@@ -804,6 +961,19 @@ onUnmounted(() => {
|
||||
font-size: 13px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.reset-filter-btn {
|
||||
margin-top: 16px;
|
||||
padding: 8px 16px;
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
|
||||
&:hover {
|
||||
background: #5a6fd6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.doc-item {
|
||||
|
||||
Reference in New Issue
Block a user