게시물 export 진행도 표시 추가 v1.5.21
This commit is contained in:
@@ -79,6 +79,7 @@ const spamSnapshot = reactive({
|
||||
})
|
||||
let toastTimer = null
|
||||
let scrollSpyFrame = null
|
||||
let postExportRefreshTimer = null
|
||||
|
||||
const { data: settings } = await useFetch('/admin/api/settings')
|
||||
const {
|
||||
@@ -171,6 +172,14 @@ const hasSpamChanges = computed(() => editSpam.value
|
||||
*/
|
||||
const normalizedPostExportJobs = computed(() => Array.isArray(postExportJobs.value) ? postExportJobs.value : [])
|
||||
|
||||
/**
|
||||
* 진행 중인 게시물 export 작업이 있는지 확인한다.
|
||||
* @returns {boolean} 진행 중 작업 여부
|
||||
*/
|
||||
const hasActivePostExportJobs = computed(() => normalizedPostExportJobs.value.some((job) => (
|
||||
job.status === 'queued' || job.status === 'processing'
|
||||
)))
|
||||
|
||||
/**
|
||||
* export 상태 라벨 조회
|
||||
* @param {string} status - export 상태
|
||||
@@ -221,6 +230,65 @@ const formatExportFileSize = (value) => {
|
||||
return `${size.toFixed(size >= 10 || unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`
|
||||
}
|
||||
|
||||
/**
|
||||
* export 작업 진행률을 계산한다.
|
||||
* @param {Object} job - export 작업
|
||||
* @returns {number} 진행률
|
||||
*/
|
||||
const getPostExportProgressPercent = (job) => {
|
||||
const total = Number(job?.postCount || 0)
|
||||
const processed = Number(job?.processedCount || 0)
|
||||
|
||||
if (!total) {
|
||||
return job?.status === 'ready' ? 100 : 0
|
||||
}
|
||||
|
||||
return Math.min(Math.max(Math.round((processed / total) * 100), 0), 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* export 작업 진행 숫자 라벨을 만든다.
|
||||
* @param {Object} job - export 작업
|
||||
* @returns {string} 진행 숫자 라벨
|
||||
*/
|
||||
const getPostExportProgressLabel = (job) => {
|
||||
const total = Number(job?.postCount || 0)
|
||||
const processed = Math.min(Number(job?.processedCount || 0), total)
|
||||
|
||||
if (!total) {
|
||||
return '0 / 0'
|
||||
}
|
||||
|
||||
return `${processed.toLocaleString()} / ${total.toLocaleString()}`
|
||||
}
|
||||
|
||||
/**
|
||||
* export 작업 진행 보조 설명을 만든다.
|
||||
* @param {Object} job - export 작업
|
||||
* @returns {string} 진행 보조 설명
|
||||
*/
|
||||
const getPostExportProgressDescription = (job) => {
|
||||
if (job.progressMessage) {
|
||||
return job.progressMessage
|
||||
}
|
||||
|
||||
if (job.status === 'queued') {
|
||||
return '작업 대기열에 등록되었습니다. 생성 워커가 시작되면 진행 숫자가 갱신됩니다.'
|
||||
}
|
||||
|
||||
if (job.status === 'processing') {
|
||||
return job.currentPartIndex
|
||||
? `${job.currentPartIndex}번째 분할 파일을 생성하는 중입니다.`
|
||||
: '분할 파일을 생성하는 중입니다.'
|
||||
}
|
||||
|
||||
if (job.status === 'ready') {
|
||||
return '생성이 완료되었습니다.'
|
||||
}
|
||||
|
||||
return job.message || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* 가입 금지 닉네임 textarea 바인딩
|
||||
*/
|
||||
@@ -906,6 +974,11 @@ const onGlobalKeydown = (event) => {
|
||||
onMounted(() => {
|
||||
if (import.meta.client) {
|
||||
window.addEventListener('keydown', onGlobalKeydown)
|
||||
postExportRefreshTimer = window.setInterval(() => {
|
||||
if (hasActivePostExportJobs.value) {
|
||||
refreshPostExportJobs()
|
||||
}
|
||||
}, 5000)
|
||||
nextTick(() => {
|
||||
updateActiveSectionFromScroll()
|
||||
})
|
||||
@@ -914,6 +987,7 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.clearTimeout(toastTimer)
|
||||
window.clearInterval(postExportRefreshTimer)
|
||||
if (scrollSpyFrame) {
|
||||
cancelAnimationFrame(scrollSpyFrame)
|
||||
}
|
||||
@@ -1790,9 +1864,6 @@ onBeforeUnmount(() => {
|
||||
<p class="mt-2 text-xs text-[#657080]">
|
||||
요청: {{ formatPostDateTime(job.createdAt) }} · 만료: {{ formatPostDateTime(job.expiresAt) }}
|
||||
</p>
|
||||
<p v-if="job.message" class="mt-2 text-sm text-[#657080]">
|
||||
{{ job.message }}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
class="inline-flex h-9 shrink-0 cursor-not-allowed items-center justify-center rounded-md border border-[#dce0e5] px-3 text-xs font-semibold text-[#9aa3ad]"
|
||||
@@ -1803,6 +1874,27 @@ onBeforeUnmount(() => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div 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) }}
|
||||
</span>
|
||||
<span class="text-[#657080]">
|
||||
{{ getPostExportProgressPercent(job) }}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="h-2 overflow-hidden rounded-full bg-[#e8edf2]">
|
||||
<div
|
||||
class="h-full rounded-full bg-[#15171a] transition-[width] duration-500"
|
||||
:style="{ width: `${getPostExportProgressPercent(job)}%` }"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-2 flex flex-col gap-1 text-xs text-[#657080] md:flex-row md:items-center md:justify-between">
|
||||
<span>{{ getPostExportProgressDescription(job) }}</span>
|
||||
<span>마지막 갱신: {{ formatPostDateTime(job.updatedAt) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="job.files.length > 0" class="admin-settings-screen__export-files mt-4 overflow-hidden rounded-md border border-[#edf0f3]">
|
||||
<div
|
||||
v-for="file in job.files"
|
||||
|
||||
Reference in New Issue
Block a user