릴리스: v0.1.51 관리자 미리보기와 요청 조건 정리
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Sortable from 'sortablejs'
|
||||
import { api } from '../lib/api'
|
||||
import { toApiUrl } from '../lib/runtime'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import { useToast } from '../composables/useToast'
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const toast = useToast()
|
||||
const isAdmin = computed(() => !!auth.user?.isAdmin)
|
||||
@@ -42,6 +40,8 @@ const importModalItems = ref([])
|
||||
const importModalTargetGameId = ref('')
|
||||
const importModalNewGameId = ref('')
|
||||
const importModalNewGameName = ref('')
|
||||
const previewModalOpen = ref(false)
|
||||
const previewTierList = ref(null)
|
||||
|
||||
const users = ref([])
|
||||
|
||||
@@ -651,7 +651,18 @@ function tierListVisibilityLabel(tierList) {
|
||||
}
|
||||
|
||||
function openAdminTierList(tierList) {
|
||||
router.push(`/editor/${tierList.gameId}/${tierList.id}`)
|
||||
previewTierList.value = tierList
|
||||
previewModalOpen.value = true
|
||||
}
|
||||
|
||||
function closePreviewModal() {
|
||||
previewModalOpen.value = false
|
||||
previewTierList.value = null
|
||||
}
|
||||
|
||||
function previewTierListUrl(tierList) {
|
||||
if (!tierList?.gameId || !tierList?.id) return ''
|
||||
return `/editor/${tierList.gameId}/${tierList.id}`
|
||||
}
|
||||
|
||||
function openTierListImportModal(tierList, items) {
|
||||
@@ -1259,6 +1270,24 @@ async function saveFeaturedOrder() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="previewModalOpen" class="modalOverlay" @click.self="closePreviewModal">
|
||||
<div class="modalCard modalCard--preview" role="dialog" aria-modal="true">
|
||||
<div class="modalCard__titleRow">
|
||||
<div>
|
||||
<div class="modalCard__title">{{ previewTierList?.title || '티어표 미리보기' }}</div>
|
||||
<div class="modalCard__desc">관리 화면을 벗어나지 않고 완성본만 확인할 수 있어요.</div>
|
||||
</div>
|
||||
<button class="btn btn--ghost btn--small" @click="closePreviewModal">닫기</button>
|
||||
</div>
|
||||
<iframe
|
||||
v-if="previewTierList"
|
||||
class="previewFrame"
|
||||
:src="previewTierListUrl(previewTierList)"
|
||||
title="티어표 미리보기"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
@@ -2128,6 +2157,16 @@ async function saveFeaturedOrder() {
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
background: rgba(11, 18, 32, 0.96);
|
||||
}
|
||||
.modalCard--preview {
|
||||
width: min(1200px, 100%);
|
||||
}
|
||||
.modalCard__titleRow {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.modalCard__title {
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
@@ -2146,6 +2185,13 @@ async function saveFeaturedOrder() {
|
||||
justify-content: flex-end;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.previewFrame {
|
||||
width: 100%;
|
||||
min-height: min(80vh, 820px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
}
|
||||
.importModeTabs {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
@@ -91,9 +91,8 @@ const customItems = computed(() =>
|
||||
.filter((item) => item?.origin === 'custom')
|
||||
.sort((a, b) => (a.label || '').localeCompare(b.label || '', 'ko'))
|
||||
)
|
||||
const hasPlacedItems = computed(() => groups.value.some((group) => (group.itemIds || []).length > 0))
|
||||
const canRequestTemplateCreate = computed(
|
||||
() => canEdit.value && !isNewTierList.value && gameId.value === 'freeform' && !hasPlacedItems.value && customItems.value.length > 0
|
||||
() => canEdit.value && !isNewTierList.value && gameId.value === 'freeform' && customItems.value.length > 0
|
||||
)
|
||||
const canRequestTemplateUpdate = computed(
|
||||
() => canEdit.value && !isNewTierList.value && gameId.value !== 'freeform' && customItems.value.length > 0
|
||||
@@ -104,11 +103,6 @@ const templateRequestChecks = computed(() => [
|
||||
label: '티어표 이름(게임 이름)을 직접 입력했는지',
|
||||
passed: !!(title.value || '').trim() && (title.value || '').trim() !== (gameName.value || '').trim(),
|
||||
},
|
||||
{
|
||||
id: 'empty-board',
|
||||
label: '등록한 이미지를 티어에 배치하지 않은 원본 상태인지',
|
||||
passed: !hasPlacedItems.value,
|
||||
},
|
||||
])
|
||||
const canSubmitTemplateCreateRequest = computed(() => templateRequestChecks.value.every((item) => item.passed))
|
||||
|
||||
@@ -530,10 +524,6 @@ async function requestTemplate(type) {
|
||||
toast.error('이미 처리 대기 중인 같은 요청이 있어요.')
|
||||
return
|
||||
}
|
||||
if (e?.status === 400 && e?.data?.error === 'board_must_be_empty') {
|
||||
toast.error('템플릿 등록 요청은 보드를 비운 상태에서만 보낼 수 있어요.')
|
||||
return
|
||||
}
|
||||
if (e?.status === 400 && e?.data?.error === 'custom_items_required') {
|
||||
toast.error('먼저 커스텀 아이템을 추가한 뒤 요청해주세요.')
|
||||
return
|
||||
@@ -712,7 +702,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<div class="requestChecklist__hint">
|
||||
제목이 명확하고, 보드는 비워둔 채 원본 아이템만 정리되어 있을수록 관리자가 새 게임 템플릿으로 빠르게 등록하기 쉬워져요.
|
||||
제목만 명확하게 적어두면 관리자가 어떤 게임 템플릿 요청인지 빠르게 파악할 수 있어요. 여러 사용자가 비슷한 주제로 요청할 수 있으니 게임 이름을 구체적으로 적어주세요.
|
||||
</div>
|
||||
<div class="modalCard__actions">
|
||||
<button class="btn btn--ghost" @click="closeTemplateRequestModal">취소</button>
|
||||
|
||||
Reference in New Issue
Block a user