추천 티어표 분리와 관리자 추천 지정 기능 추가
This commit is contained in:
@@ -12,6 +12,7 @@ const auth = useAuthStore()
|
||||
|
||||
const topicId = computed(() => route.params.topicId)
|
||||
const topicName = ref('')
|
||||
const featuredTierLists = ref([])
|
||||
const tierLists = ref([])
|
||||
const error = ref('')
|
||||
const query = ref('')
|
||||
@@ -19,6 +20,7 @@ const brokenThumbnailIds = ref({})
|
||||
const isTopicLoading = ref(false)
|
||||
const isListView = computed(() => route.query.view === 'list')
|
||||
const topicTitle = computed(() => topicName.value || (isTopicLoading.value ? '주제 불러오는 중...' : ''))
|
||||
const publicTierLists = computed(() => tierLists.value.filter((tierList) => !tierList.isFeatured))
|
||||
|
||||
function fmt(ts) {
|
||||
return new Date(ts).toLocaleDateString(undefined, {
|
||||
@@ -59,6 +61,7 @@ async function loadTierLists() {
|
||||
])
|
||||
topicName.value = topicRes.topic?.name || ''
|
||||
brokenThumbnailIds.value = {}
|
||||
featuredTierLists.value = listRes.featuredTierLists || []
|
||||
tierLists.value = listRes.tierLists || []
|
||||
} catch (e) {
|
||||
error.value = '주제 정보를 불러오지 못했어요.'
|
||||
@@ -110,10 +113,65 @@ watch(
|
||||
</section>
|
||||
|
||||
<div v-if="error" class="error">{{ error }}</div>
|
||||
<section v-if="featuredTierLists.length" class="featuredPanel">
|
||||
<div class="featuredHead">
|
||||
<div>
|
||||
<div class="featuredHead__eyebrow">Featured</div>
|
||||
<h3 class="featuredHead__title">추천 티어표</h3>
|
||||
</div>
|
||||
<div class="featuredHead__count">{{ featuredTierLists.length }}개</div>
|
||||
</div>
|
||||
<div class="list featuredList" :class="{ 'list--table': isListView }">
|
||||
<article
|
||||
v-for="t in featuredTierLists"
|
||||
:key="`featured-${t.id}`"
|
||||
class="boardCard boardCard--featured"
|
||||
:class="{ 'boardCard--list': isListView }"
|
||||
>
|
||||
<button class="boardCard__body" :class="{ 'boardCard__body--list': isListView }" @click="openTierList(t.id)">
|
||||
<div class="boardCard__thumbWrap">
|
||||
<img
|
||||
v-if="tierListThumbnailUrl(t)"
|
||||
class="boardCard__thumb"
|
||||
:src="tierListThumbnailUrl(t)"
|
||||
alt=""
|
||||
draggable="false"
|
||||
@error="handleThumbnailError(t.id)"
|
||||
/>
|
||||
<div v-else class="boardCard__thumbPlaceholder">대표 썸네일</div>
|
||||
</div>
|
||||
<div class="boardCard__head">
|
||||
<div class="boardCard__titleRow">
|
||||
<div class="boardCard__title">{{ t.title }}</div>
|
||||
<div class="favoriteStat" :title="t.isFavorited ? '이미 즐겨찾기한 티어표' : '즐겨찾기 수'">
|
||||
{{ t.isFavorited ? '♥' : '♡' }} {{ t.favoriteCount || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="boardCard__metaRow">
|
||||
<div class="boardCard__author">
|
||||
<img
|
||||
v-if="avatarSrcOf(t)"
|
||||
class="boardCard__avatar"
|
||||
:src="avatarSrcOf(t)"
|
||||
:alt="displayNameOf(t)"
|
||||
draggable="false"
|
||||
/>
|
||||
<div v-else class="boardCard__avatar boardCard__avatar--fallback">{{ avatarFallbackOf(t) }}</div>
|
||||
<span class="boardCard__authorName">{{ displayNameOf(t) }}</span>
|
||||
</div>
|
||||
<div class="boardCard__date">{{ fmt(t.updatedAt) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div v-if="tierLists.length === 0" class="empty">아직 공개 티어표가 없어요.</div>
|
||||
<div class="sectionLabel">전체 공개 티어표</div>
|
||||
<div v-if="publicTierLists.length === 0" class="empty">아직 일반 공개 티어표가 없어요.</div>
|
||||
<div v-else class="list" :class="{ 'list--table': isListView }">
|
||||
<article v-for="t in tierLists" :key="t.id" class="boardCard" :class="{ 'boardCard--list': isListView }">
|
||||
<article v-for="t in publicTierLists" :key="t.id" class="boardCard" :class="{ 'boardCard--list': isListView }">
|
||||
<button class="boardCard__body" :class="{ 'boardCard__body--list': isListView }" @click="openTierList(t.id)">
|
||||
<div class="boardCard__thumbWrap">
|
||||
<img v-if="tierListThumbnailUrl(t)" class="boardCard__thumb" :src="tierListThumbnailUrl(t)" alt="" draggable="false" @error="handleThumbnailError(t.id)" />
|
||||
@@ -148,6 +206,44 @@ watch(
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.featuredPanel {
|
||||
margin-bottom: 28px;
|
||||
padding: 24px;
|
||||
border-radius: 28px;
|
||||
border: 1px solid var(--theme-card-border);
|
||||
background: linear-gradient(180deg, var(--theme-surface-soft) 0%, var(--theme-surface) 100%);
|
||||
box-shadow: inset 0 1px 0 var(--theme-card-shadow);
|
||||
}
|
||||
.featuredHead {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.featuredHead__eyebrow,
|
||||
.sectionLabel {
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--theme-text-faint);
|
||||
}
|
||||
.featuredHead__title {
|
||||
margin: 6px 0 0;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
color: var(--theme-text);
|
||||
}
|
||||
.featuredHead__count {
|
||||
flex: 0 0 auto;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
color: var(--theme-text-muted);
|
||||
}
|
||||
.sectionLabel {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
@@ -206,6 +302,12 @@ watch(
|
||||
background: var(--theme-card-bg-hover);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.boardCard--featured {
|
||||
border-color: color-mix(in srgb, var(--theme-accent) 35%, var(--theme-card-border));
|
||||
background:
|
||||
linear-gradient(180deg, color-mix(in srgb, var(--theme-accent) 7%, transparent), transparent 55%),
|
||||
var(--theme-card-bg);
|
||||
}
|
||||
.boardCard__body {
|
||||
min-width: 0;
|
||||
text-align: left;
|
||||
@@ -361,6 +463,17 @@ watch(
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.featuredPanel {
|
||||
padding: 18px;
|
||||
border-radius: 22px;
|
||||
}
|
||||
|
||||
.featuredHead {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.list {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user