내보내기 선택 다운로드 정리 v1.5.32
This commit is contained in:
@@ -23,6 +23,7 @@ const uploadingHomeCoverDark = ref(false)
|
||||
const requestingPostExport = ref(false)
|
||||
const deletingPostExportJobIds = ref([])
|
||||
const downloadingPostExportJobIds = ref([])
|
||||
const selectedPostExportFileIds = ref({})
|
||||
const retryingPostExportJobIds = ref([])
|
||||
const importingPosts = ref(false)
|
||||
const postImportFile = ref(null)
|
||||
@@ -716,7 +717,110 @@ const getReadyPostExportFiles = (job) => Array.isArray(job?.files)
|
||||
: []
|
||||
|
||||
/**
|
||||
* Export 일괄 다운로드 중 여부
|
||||
* Export 작업의 선택된 파일 ID를 가져온다.
|
||||
* @param {string} jobId - 작업 ID
|
||||
* @returns {Array<string>} 선택된 파일 ID 목록
|
||||
*/
|
||||
const getSelectedPostExportFileIds = (jobId) => Array.isArray(selectedPostExportFileIds.value[jobId])
|
||||
? selectedPostExportFileIds.value[jobId]
|
||||
: []
|
||||
|
||||
/**
|
||||
* Export 파일이 선택되었는지 확인한다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @param {Object} file - Export 파일
|
||||
* @returns {boolean} 선택 여부
|
||||
*/
|
||||
const isPostExportFileSelected = (job, file) => getSelectedPostExportFileIds(job?.id).includes(file?.id)
|
||||
|
||||
/**
|
||||
* Export 파일 선택 상태를 변경한다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @param {Object} file - Export 파일
|
||||
* @param {boolean} selected - 선택 여부
|
||||
* @returns {void}
|
||||
*/
|
||||
const setPostExportFileSelected = (job, file, selected) => {
|
||||
if (!job?.id || !file?.id || file.status !== 'ready' || !file.filePath) {
|
||||
return
|
||||
}
|
||||
|
||||
const currentIds = getSelectedPostExportFileIds(job.id)
|
||||
const nextIds = selected
|
||||
? [...new Set([...currentIds, file.id])]
|
||||
: currentIds.filter((fileId) => fileId !== file.id)
|
||||
|
||||
selectedPostExportFileIds.value = {
|
||||
...selectedPostExportFileIds.value,
|
||||
[job.id]: nextIds
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export 작업에서 선택된 다운로드 가능 파일을 가져온다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @returns {Array} 선택된 다운로드 가능 파일 목록
|
||||
*/
|
||||
const getSelectedReadyPostExportFiles = (job) => {
|
||||
const selectedIds = getSelectedPostExportFileIds(job?.id)
|
||||
|
||||
return getReadyPostExportFiles(job).filter((file) => selectedIds.includes(file.id))
|
||||
}
|
||||
|
||||
/**
|
||||
* Export 작업의 모든 다운로드 가능 파일이 선택되었는지 확인한다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @returns {boolean} 전체 선택 여부
|
||||
*/
|
||||
const areAllReadyPostExportFilesSelected = (job) => {
|
||||
const readyFiles = getReadyPostExportFiles(job)
|
||||
const selectedIds = getSelectedPostExportFileIds(job?.id)
|
||||
|
||||
return readyFiles.length > 0 && readyFiles.every((file) => selectedIds.includes(file.id))
|
||||
}
|
||||
|
||||
/**
|
||||
* Export 작업의 다운로드 가능 파일 전체 선택을 전환한다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @returns {void}
|
||||
*/
|
||||
const toggleAllPostExportFiles = (job) => {
|
||||
if (!job?.id) {
|
||||
return
|
||||
}
|
||||
|
||||
const readyFiles = getReadyPostExportFiles(job)
|
||||
const nextIds = areAllReadyPostExportFilesSelected(job)
|
||||
? []
|
||||
: readyFiles.map((file) => file.id)
|
||||
|
||||
selectedPostExportFileIds.value = {
|
||||
...selectedPostExportFileIds.value,
|
||||
[job.id]: nextIds
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export 작업의 파일 선택을 비운다.
|
||||
* @param {string} jobId - 작업 ID
|
||||
* @returns {void}
|
||||
*/
|
||||
const clearSelectedPostExportFiles = (jobId) => {
|
||||
selectedPostExportFileIds.value = {
|
||||
...selectedPostExportFileIds.value,
|
||||
[jobId]: []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export 진행도 영역 표시 여부를 확인한다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @returns {boolean} 진행도 표시 여부
|
||||
*/
|
||||
const shouldShowPostExportProgress = (job) => job?.status === 'queued' || job?.status === 'processing'
|
||||
|
||||
/**
|
||||
* Export 다운로드 중 여부
|
||||
* @param {string} jobId - 작업 ID
|
||||
* @returns {boolean} 다운로드 중 여부
|
||||
*/
|
||||
@@ -730,22 +834,27 @@ const isDownloadingPostExportJob = (jobId) => downloadingPostExportJobIds.value.
|
||||
const isRetryingPostExportJob = (jobId) => retryingPostExportJobIds.value.includes(jobId)
|
||||
|
||||
/**
|
||||
* Export 분할 파일을 브라우저에서 순차 다운로드한다.
|
||||
* 선택한 Export 분할 파일을 브라우저에서 순차 다운로드한다.
|
||||
* @param {Object} job - Export 작업
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const downloadPostExportJobFiles = async (job) => {
|
||||
const readyFiles = getReadyPostExportFiles(job)
|
||||
const selectedFiles = getSelectedReadyPostExportFiles(job)
|
||||
|
||||
if (!job?.id || readyFiles.length === 0 || isDownloadingPostExportJob(job.id)) {
|
||||
if (!job?.id || isDownloadingPostExportJob(job.id)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedFiles.length === 0) {
|
||||
showToast('error', '다운로드할 파일을 선택해 주세요.')
|
||||
return
|
||||
}
|
||||
|
||||
downloadingPostExportJobIds.value = [...downloadingPostExportJobIds.value, job.id]
|
||||
showToast('info', `${readyFiles.length}개 파일을 순차 다운로드합니다.`)
|
||||
showToast('info', `${selectedFiles.length}개 선택 파일을 순차 다운로드합니다.`)
|
||||
|
||||
try {
|
||||
for (const file of readyFiles) {
|
||||
for (const file of selectedFiles) {
|
||||
const link = document.createElement('a')
|
||||
link.href = getPostExportDownloadUrl(file)
|
||||
link.download = file.fileName || ''
|
||||
@@ -755,7 +864,8 @@ const downloadPostExportJobFiles = async (job) => {
|
||||
link.remove()
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 700))
|
||||
}
|
||||
showToast('success', '일괄 다운로드 요청을 보냈습니다.')
|
||||
clearSelectedPostExportFiles(job.id)
|
||||
showToast('success', '선택 파일 다운로드 요청을 보냈습니다.')
|
||||
} finally {
|
||||
downloadingPostExportJobIds.value = downloadingPostExportJobIds.value.filter((id) => id !== job.id)
|
||||
}
|
||||
@@ -2436,21 +2546,10 @@ onBeforeUnmount(() => {
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-[#657080]">
|
||||
요청: {{ formatPostDateTime(job.createdAt) }} · 만료: {{ formatPostDateTime(job.expiresAt) }}
|
||||
</p>
|
||||
<p class="mt-1 text-xs text-[#9aa3ad]">
|
||||
목표 용량 {{ formatExportFileSize(job.maxFileSizeBytes) }} · 최대 {{ job.chunkSize }}개/ZIP
|
||||
만료: {{ formatPostDateTime(job.expiresAt) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<button
|
||||
class="inline-flex h-9 shrink-0 cursor-pointer items-center justify-center rounded-md border border-[#15171a] bg-[#15171a] px-3 text-xs font-semibold text-white transition hover:bg-black disabled:cursor-not-allowed disabled:border-[#dce0e5] disabled:bg-white disabled:text-[#9aa3ad]"
|
||||
type="button"
|
||||
:disabled="getReadyPostExportFiles(job).length === 0 || isDownloadingPostExportJob(job.id)"
|
||||
@click="downloadPostExportJobFiles(job)"
|
||||
>
|
||||
{{ isDownloadingPostExportJob(job.id) ? '다운로드 중' : `일괄 다운로드 ${getReadyPostExportFiles(job).length || ''}` }}
|
||||
</button>
|
||||
<button
|
||||
v-if="job.status === 'failed'"
|
||||
class="inline-flex h-9 shrink-0 cursor-pointer items-center justify-center rounded-md border border-[#dce0e5] px-3 text-xs font-semibold text-[#15171a] transition hover:bg-[#f4f6f8] disabled:cursor-not-allowed disabled:text-[#a6b0bb]"
|
||||
@@ -2471,7 +2570,10 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="admin-settings-screen__export-progress mt-4 rounded-lg border border-[#edf0f3] bg-[#fbfcfd] p-3">
|
||||
<div
|
||||
v-if="shouldShowPostExportProgress(job)"
|
||||
class="admin-settings-screen__export-progress mt-4 rounded-lg border border-[#edf0f3] bg-[#fbfcfd] p-3"
|
||||
>
|
||||
<div class="mb-2 flex items-center justify-between gap-3 text-xs">
|
||||
<span class="font-semibold text-[#15171a]">
|
||||
진행도 {{ getPostExportProgressLabel(job) }}
|
||||
@@ -2503,34 +2605,53 @@ onBeforeUnmount(() => {
|
||||
</details>
|
||||
|
||||
<div v-if="job.files.length > 0" class="admin-settings-screen__export-files mt-4 overflow-hidden rounded-md border border-[#edf0f3]">
|
||||
<div class="flex flex-col gap-3 border-b border-[#edf0f3] bg-[#fbfcfd] px-3 py-3 md:flex-row md:items-center md:justify-between">
|
||||
<label class="inline-flex cursor-pointer items-center gap-2 text-xs font-semibold text-[#394047] has-[:disabled]:cursor-not-allowed has-[:disabled]:text-[#a6b0bb]">
|
||||
<input
|
||||
class="size-4 rounded border-[#cfd6de] text-[#15171a] focus:ring-[#15171a] disabled:cursor-not-allowed"
|
||||
type="checkbox"
|
||||
:checked="areAllReadyPostExportFilesSelected(job)"
|
||||
:disabled="getReadyPostExportFiles(job).length === 0 || isDownloadingPostExportJob(job.id)"
|
||||
@change="toggleAllPostExportFiles(job)"
|
||||
>
|
||||
전체 선택
|
||||
</label>
|
||||
<button
|
||||
class="inline-flex h-9 shrink-0 cursor-pointer items-center justify-center rounded-md border border-[#15171a] bg-[#15171a] px-3 text-xs font-semibold text-white transition hover:bg-black disabled:cursor-not-allowed disabled:border-[#dce0e5] disabled:bg-white disabled:text-[#9aa3ad]"
|
||||
type="button"
|
||||
:disabled="getSelectedReadyPostExportFiles(job).length === 0 || isDownloadingPostExportJob(job.id)"
|
||||
@click="downloadPostExportJobFiles(job)"
|
||||
>
|
||||
{{ isDownloadingPostExportJob(job.id) ? '다운로드 중' : `선택 파일 다운로드 ${getSelectedReadyPostExportFiles(job).length || ''}` }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
v-for="file in job.files"
|
||||
:key="file.id"
|
||||
class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-3 border-b border-[#edf0f3] px-3 py-2 last:border-b-0"
|
||||
class="grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-3 border-b border-[#edf0f3] px-3 py-2 last:border-b-0"
|
||||
>
|
||||
<input
|
||||
class="size-4 rounded border-[#cfd6de] text-[#15171a] focus:ring-[#15171a] disabled:cursor-not-allowed"
|
||||
type="checkbox"
|
||||
:checked="isPostExportFileSelected(job, file)"
|
||||
:disabled="file.status !== 'ready' || !file.filePath || isDownloadingPostExportJob(job.id)"
|
||||
:aria-label="`${file.fileName} 선택`"
|
||||
@change="setPostExportFileSelected(job, file, $event.target.checked)"
|
||||
>
|
||||
<div class="min-w-0">
|
||||
<p class="truncate text-sm font-medium text-[#15171a]">
|
||||
{{ file.fileName }}
|
||||
</p>
|
||||
<p class="mt-0.5 text-xs text-[#9aa3ad]">
|
||||
{{ file.postStart }}-{{ file.postEnd }} · {{ formatExportFileSize(file.fileSizeBytes) }}
|
||||
{{ file.postStart }}-{{ file.postEnd }}
|
||||
</p>
|
||||
</div>
|
||||
<a
|
||||
v-if="file.status === 'ready' && file.filePath"
|
||||
class="inline-flex h-8 cursor-pointer items-center justify-center rounded border border-[#15171a] bg-[#15171a] px-3 text-xs font-semibold text-white transition hover:bg-black"
|
||||
:href="getPostExportDownloadUrl(file)"
|
||||
>
|
||||
다운로드
|
||||
</a>
|
||||
<button
|
||||
v-else
|
||||
class="inline-flex h-8 cursor-not-allowed items-center justify-center rounded border border-[#e1e5ea] px-3 text-xs font-semibold text-[#a6b0bb]"
|
||||
type="button"
|
||||
disabled
|
||||
<span
|
||||
v-if="file.status !== 'ready' || !file.filePath"
|
||||
class="inline-flex h-8 items-center justify-center rounded px-2 text-xs font-semibold text-[#a6b0bb]"
|
||||
>
|
||||
{{ file.status === 'processing' ? '생성 중' : file.status === 'failed' ? '실패' : '다운로드 대기' }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
Reference in New Issue
Block a user