262 lines
9.1 KiB
Vue
262 lines
9.1 KiB
Vue
<script setup>
|
|
/**
|
|
* 게시물 내보내기 요청 카드
|
|
* @property {boolean} expanded - 펼침 여부
|
|
* @property {string} dateRangeMode - 내보내기 범위 모드
|
|
* @property {number} year - 내보내기 연도
|
|
* @property {number} month - 내보내기 월
|
|
* @property {string} dateFrom - 직접 범위 시작일
|
|
* @property {string} dateTo - 직접 범위 종료일
|
|
* @property {number} maxFileSizeMb - 목표 ZIP 용량
|
|
* @property {number} chunkSize - ZIP당 최대 게시물 수
|
|
* @property {Array} dateRangeModeOptions - 범위 옵션
|
|
* @property {Array} yearOptions - 연도 옵션
|
|
* @property {Array} monthOptions - 월 옵션
|
|
* @property {boolean} canRequest - 요청 가능 여부
|
|
* @property {boolean} requesting - 요청 중 여부
|
|
* @property {boolean} hasActiveJobs - 진행 중 작업 여부
|
|
* @property {string} requestTitle - 요청 버튼 안내 문구
|
|
*/
|
|
defineProps({
|
|
expanded: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
dateRangeMode: {
|
|
type: String,
|
|
default: 'all'
|
|
},
|
|
year: {
|
|
type: [Number, String],
|
|
default: () => new Date().getFullYear()
|
|
},
|
|
month: {
|
|
type: [Number, String],
|
|
default: 1
|
|
},
|
|
dateFrom: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
dateTo: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
maxFileSizeMb: {
|
|
type: [Number, String],
|
|
default: 500
|
|
},
|
|
chunkSize: {
|
|
type: [Number, String],
|
|
default: 500
|
|
},
|
|
dateRangeModeOptions: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
yearOptions: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
monthOptions: {
|
|
type: Array,
|
|
default: () => []
|
|
},
|
|
canRequest: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
requesting: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
hasActiveJobs: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
requestTitle: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits([
|
|
'toggle',
|
|
'request',
|
|
'update:dateRangeMode',
|
|
'update:year',
|
|
'update:month',
|
|
'update:dateFrom',
|
|
'update:dateTo',
|
|
'update:maxFileSizeMb',
|
|
'update:chunkSize'
|
|
])
|
|
|
|
/**
|
|
* 입력 이벤트 값을 문자열로 전달한다.
|
|
* @param {Event} event - 입력 이벤트
|
|
* @param {string} eventName - 전달할 이벤트 이름
|
|
* @returns {void}
|
|
*/
|
|
const emitTextInputValue = (event, eventName) => {
|
|
emit(eventName, event.target?.value || '')
|
|
}
|
|
|
|
/**
|
|
* 숫자 입력 이벤트 값을 전달한다.
|
|
* @param {Event} event - 입력 이벤트
|
|
* @param {string} eventName - 전달할 이벤트 이름
|
|
* @returns {void}
|
|
*/
|
|
const emitNumberInputValue = (event, eventName) => {
|
|
const value = event.target?.value ?? ''
|
|
emit(eventName, value === '' ? '' : Number(value))
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<section
|
|
id="admin-settings-section-export"
|
|
class="admin-post-export-request-card admin-settings-screen__card rounded-xl border border-[#e6e8eb] bg-white p-6 shadow-[0_1px_2px_rgba(15,23,42,0.04)]"
|
|
>
|
|
<div class="admin-settings-screen__card-head flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
|
|
<div class="min-w-0">
|
|
<h2 class="text-lg font-semibold text-[#15171a]">
|
|
게시물 내보내기
|
|
</h2>
|
|
<p class="mt-1 text-sm leading-relaxed text-[#657080]">
|
|
게시물을 Obsidian 호환 ZIP 백업으로 만들고, 준비된 분할 파일을 내려받습니다.
|
|
</p>
|
|
</div>
|
|
<button
|
|
class="inline-flex h-10 shrink-0 cursor-pointer items-center justify-center rounded-md bg-[#15171a] px-4 text-sm font-semibold text-white transition hover:bg-black"
|
|
type="button"
|
|
:aria-expanded="expanded"
|
|
aria-controls="admin-settings-export-panel"
|
|
@click="emit('toggle')"
|
|
>
|
|
{{ expanded ? '접기' : '내보내기' }}
|
|
</button>
|
|
</div>
|
|
|
|
<div
|
|
v-if="expanded"
|
|
id="admin-settings-export-panel"
|
|
class="admin-post-export-request-card__actions admin-settings-screen__export-actions mt-4 grid gap-4 rounded-lg border border-[#e6e8eb] bg-[#fbfcfd] p-4"
|
|
>
|
|
<div class="min-w-0">
|
|
<p class="text-sm font-semibold text-[#15171a]">
|
|
Obsidian 호환 백업 준비
|
|
</p>
|
|
<p class="mt-1 text-sm leading-relaxed text-[#657080]">
|
|
전체·연도·월·직접 범위로 게시물을 골라 목표 용량 기준 ZIP 백업을 만듭니다.
|
|
</p>
|
|
</div>
|
|
<div class="grid gap-3">
|
|
<div class="admin-post-export-request-card__range admin-settings-screen__export-range grid gap-2 md:grid-cols-4">
|
|
<div class="grid gap-1 text-xs font-semibold text-[#5d6673]">
|
|
<span>범위</span>
|
|
<AdminSelectInput
|
|
:model-value="dateRangeMode"
|
|
:options="dateRangeModeOptions"
|
|
label="내보내기 범위"
|
|
select-class="admin-settings-screen__export-select h-10 rounded-md border-[#dce0e5] text-sm font-semibold text-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
width-class="w-full"
|
|
@update:model-value="emit('update:dateRangeMode', $event)"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="dateRangeMode === 'year' || dateRangeMode === 'month'"
|
|
class="grid gap-1 text-xs font-semibold text-[#5d6673]"
|
|
>
|
|
<span>연도</span>
|
|
<AdminSelectInput
|
|
:model-value="year"
|
|
:options="yearOptions"
|
|
label="내보내기 연도"
|
|
select-class="admin-settings-screen__export-select h-10 rounded-md border-[#dce0e5] text-sm font-semibold text-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
width-class="w-full"
|
|
@update:model-value="emit('update:year', $event)"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="dateRangeMode === 'month'"
|
|
class="grid gap-1 text-xs font-semibold text-[#5d6673]"
|
|
>
|
|
<span>월</span>
|
|
<AdminSelectInput
|
|
:model-value="month"
|
|
:options="monthOptions"
|
|
label="내보내기 월"
|
|
select-class="admin-settings-screen__export-select h-10 rounded-md border-[#dce0e5] text-sm font-semibold text-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
width-class="w-full"
|
|
@update:model-value="emit('update:month', $event)"
|
|
/>
|
|
</div>
|
|
<label
|
|
v-if="dateRangeMode === 'custom'"
|
|
class="grid gap-1 text-xs font-semibold text-[#5d6673]"
|
|
>
|
|
시작일
|
|
<input
|
|
:value="dateFrom"
|
|
class="h-10 rounded-md border border-[#dce0e5] bg-white px-3 text-sm font-semibold text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
type="date"
|
|
@input="emitTextInputValue($event, 'update:dateFrom')"
|
|
>
|
|
</label>
|
|
<label
|
|
v-if="dateRangeMode === 'custom'"
|
|
class="grid gap-1 text-xs font-semibold text-[#5d6673]"
|
|
>
|
|
종료일
|
|
<input
|
|
:value="dateTo"
|
|
class="h-10 rounded-md border border-[#dce0e5] bg-white px-3 text-sm font-semibold text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
type="date"
|
|
@input="emitTextInputValue($event, 'update:dateTo')"
|
|
>
|
|
</label>
|
|
</div>
|
|
<div class="admin-post-export-request-card__split admin-settings-screen__export-split grid gap-2 md:grid-cols-2">
|
|
<label class="grid gap-1 text-xs font-semibold text-[#5d6673]">
|
|
목표 ZIP 용량 (MB)
|
|
<input
|
|
:value="maxFileSizeMb"
|
|
class="h-10 rounded-md border border-[#dce0e5] bg-white px-3 text-sm font-semibold text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
type="number"
|
|
min="10"
|
|
max="2048"
|
|
step="10"
|
|
@input="emitNumberInputValue($event, 'update:maxFileSizeMb')"
|
|
>
|
|
</label>
|
|
<label class="grid gap-1 text-xs font-semibold text-[#5d6673]">
|
|
ZIP당 최대 게시물 수
|
|
<input
|
|
:value="chunkSize"
|
|
class="h-10 rounded-md border border-[#dce0e5] bg-white px-3 text-sm font-semibold text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
|
type="number"
|
|
min="1"
|
|
max="500"
|
|
step="1"
|
|
@input="emitNumberInputValue($event, 'update:chunkSize')"
|
|
>
|
|
</label>
|
|
</div>
|
|
<div class="flex items-center justify-end">
|
|
<button
|
|
class="admin-post-export-request-card__request admin-settings-screen__export-request inline-flex h-10 shrink-0 cursor-pointer items-center justify-center rounded-md bg-[#15171a] px-4 text-sm font-semibold text-white transition hover:bg-black disabled:cursor-not-allowed disabled:bg-[#c7cdd4]"
|
|
type="button"
|
|
:disabled="!canRequest"
|
|
:title="requestTitle"
|
|
@click="emit('request')"
|
|
>
|
|
{{ requesting ? '요청 중...' : hasActiveJobs ? '내보내기 진행 중' : '내보내기 요청' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|