릴리스: v0.1.52 프리뷰 전용화와 썸네일 자동 생성
This commit is contained in:
@@ -10,6 +10,7 @@ const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const { toasts, dismissToast } = useToast()
|
||||
const isAdmin = computed(() => !!auth.user?.isAdmin)
|
||||
const isPreviewMode = computed(() => route.query.preview === '1')
|
||||
const avatarUrl = computed(() => {
|
||||
if (!auth.user?.avatarSrc) return ''
|
||||
return toApiUrl(auth.user.avatarSrc)
|
||||
@@ -57,7 +58,7 @@ async function logout() {
|
||||
|
||||
<template>
|
||||
<div class="app-shell">
|
||||
<header class="app-header">
|
||||
<header v-if="!isPreviewMode" class="app-header">
|
||||
<div class="brand" @click="$router.push('/')">
|
||||
<span class="brand__title">Tier Maker</span>
|
||||
<span class="brand__sub">by zenn</span>
|
||||
@@ -81,10 +82,10 @@ async function logout() {
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="app-main">
|
||||
<main class="app-main" :class="{ 'app-main--preview': isPreviewMode }">
|
||||
<RouterView />
|
||||
</main>
|
||||
<div class="toastStack" aria-live="polite" aria-atomic="true">
|
||||
<div class="toastStack" :class="{ 'toastStack--preview': isPreviewMode }" aria-live="polite" aria-atomic="true">
|
||||
<div v-for="item in toasts" :key="item.id" class="toast" :class="[`toast--${item.type}`, { 'toast--closing': item.isClosing }]">
|
||||
<div class="toast__body">
|
||||
<div class="toast__message">{{ item.message }}</div>
|
||||
@@ -158,6 +159,10 @@ async function logout() {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.app-main--preview {
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.user {
|
||||
position: relative;
|
||||
@@ -219,6 +224,9 @@ async function logout() {
|
||||
gap: 10px;
|
||||
width: min(360px, calc(100vw - 24px));
|
||||
}
|
||||
.toastStack--preview {
|
||||
top: 12px;
|
||||
}
|
||||
.toast {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@@ -662,7 +662,7 @@ function closePreviewModal() {
|
||||
|
||||
function previewTierListUrl(tierList) {
|
||||
if (!tierList?.gameId || !tierList?.id) return ''
|
||||
return `/editor/${tierList.gameId}/${tierList.id}`
|
||||
return `/editor/${tierList.gameId}/${tierList.id}?preview=1`
|
||||
}
|
||||
|
||||
function openTierListImportModal(tierList, items) {
|
||||
@@ -1274,10 +1274,7 @@ async function saveFeaturedOrder() {
|
||||
<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>
|
||||
<div class="modalCard__title">{{ previewTierList?.title || '티어표 미리보기' }}</div>
|
||||
<button class="btn btn--ghost btn--small" @click="closePreviewModal">닫기</button>
|
||||
</div>
|
||||
<iframe
|
||||
|
||||
@@ -14,6 +14,7 @@ const auth = useAuthStore()
|
||||
const toast = useToast()
|
||||
const gameId = computed(() => route.params.gameId)
|
||||
const tierListId = computed(() => route.params.tierListId)
|
||||
const previewMode = computed(() => route.query.preview === '1')
|
||||
const gameName = ref('')
|
||||
|
||||
const groups = ref([
|
||||
@@ -602,6 +603,32 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="previewMode" class="previewOnly">
|
||||
<div class="previewOnly__sheet">
|
||||
<div class="previewOnly__title">{{ effectiveTitle }}</div>
|
||||
<div v-if="description" class="previewOnly__description">{{ description }}</div>
|
||||
<div class="previewOnly__rows">
|
||||
<div v-for="g in groups" :key="g.id" class="previewOnly__row">
|
||||
<div class="previewOnly__label">{{ g.name }}</div>
|
||||
<div class="previewOnly__drop">
|
||||
<div v-for="id in g.itemIds" :key="id" class="previewOnly__cell">
|
||||
<img :src="resolveItemSrc(itemsById[id])" class="thumb" :alt="itemsById[id]?.label || id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="pool.length" class="previewOnly__pool">
|
||||
<div class="previewOnly__poolTitle">남은 아이템</div>
|
||||
<div class="previewOnly__poolGrid">
|
||||
<div v-for="id in pool" :key="id" class="previewOnly__poolItem">
|
||||
<img :src="resolveItemSrc(itemsById[id])" class="thumb" :alt="itemsById[id]?.label || id" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<template v-else>
|
||||
<section class="head">
|
||||
<div class="heroCard">
|
||||
<div class="heroCard__main">
|
||||
@@ -824,6 +851,7 @@ onUnmounted(() => {
|
||||
<button v-if="canEdit" class="btn btn--ghost" @click="openFile">파일 선택</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -832,6 +860,82 @@ onUnmounted(() => {
|
||||
gap: 14px;
|
||||
padding: 6px 2px 14px;
|
||||
}
|
||||
.previewOnly {
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
radial-gradient(circle at top, rgba(96, 165, 250, 0.14), transparent 38%),
|
||||
rgba(11, 18, 32, 0.98);
|
||||
}
|
||||
.previewOnly__sheet {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.previewOnly__title {
|
||||
font-size: 28px;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
.previewOnly__description {
|
||||
margin-top: -8px;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
opacity: 0.76;
|
||||
}
|
||||
.previewOnly__rows {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.previewOnly__row {
|
||||
display: grid;
|
||||
grid-template-columns: 180px 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
.previewOnly__label {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 10px 8px;
|
||||
text-align: center;
|
||||
font-weight: 900;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.06);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
.previewOnly__drop {
|
||||
border-radius: 14px;
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
min-height: calc(var(--thumb-size, 80px) + 24px);
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
align-content: flex-start;
|
||||
}
|
||||
.previewOnly__cell {
|
||||
display: inline-flex;
|
||||
}
|
||||
.previewOnly__pool {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.previewOnly__poolTitle {
|
||||
font-weight: 900;
|
||||
opacity: 0.82;
|
||||
}
|
||||
.previewOnly__poolGrid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.previewOnly__poolItem {
|
||||
display: inline-flex;
|
||||
}
|
||||
.heroCard {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.5fr) minmax(280px, 360px);
|
||||
@@ -1424,6 +1528,9 @@ onUnmounted(() => {
|
||||
border-radius: 14px;
|
||||
}
|
||||
@media (max-width: 980px) {
|
||||
.previewOnly__row {
|
||||
grid-template-columns: 140px 1fr;
|
||||
}
|
||||
.heroCard {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -1461,4 +1568,12 @@ onUnmounted(() => {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.previewOnly {
|
||||
padding: 14px;
|
||||
}
|
||||
.previewOnly__row {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user