Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94152f22b2 |
@@ -1,5 +1,9 @@
|
|||||||
# 의사결정 이력
|
# 의사결정 이력
|
||||||
|
|
||||||
|
## 2026-03-26 v0.1.39
|
||||||
|
- 티어표 편집 헤더는 게임명 kicker보다 제목과 설명이 더 중요하므로, 좌측 입력 중심 구조로 재배치하고 썸네일은 우측 보조 카드로 분리하는 편이 더 자연스럽다고 판단했다.
|
||||||
|
- 썸네일 조작 버튼은 모바일에서도 카드와 함께 유지되는 편이 흐름이 덜 끊기므로, 미리보기 아래 별도 줄로 떨어뜨리기보다 카드 내부의 짧은 액션 행으로 묶기로 결정했다.
|
||||||
|
|
||||||
## 2026-03-26 v0.1.38
|
## 2026-03-26 v0.1.38
|
||||||
- 관리자 기본 아이템은 업로드 시점에만 이름을 정할 수 있으면 운영 중 수정이 어려우므로, 목록에서 직접 이름을 바꾸고 저장할 수 있게 하기로 결정했다.
|
- 관리자 기본 아이템은 업로드 시점에만 이름을 정할 수 있으면 운영 중 수정이 어려우므로, 목록에서 직접 이름을 바꾸고 저장할 수 있게 하기로 결정했다.
|
||||||
- 게임별 티어표 목록도 식별성이 중요하므로, 사용자가 편집 시 별도 썸네일을 지정할 수 있게 하고 목록 카드에서는 게임 카드와 비슷한 상단 썸네일 구조를 사용하기로 결정했다.
|
- 게임별 티어표 목록도 식별성이 중요하므로, 사용자가 편집 시 별도 썸네일을 지정할 수 있게 하고 목록 카드에서는 게임 카드와 비슷한 상단 썸네일 구조를 사용하기로 결정했다.
|
||||||
|
|||||||
@@ -118,6 +118,7 @@
|
|||||||
- 제목이 비어 있는 상태로 저장하면 내부 제목은 현재 게임명을 기본값으로 사용한다.
|
- 제목이 비어 있는 상태로 저장하면 내부 제목은 현재 게임명을 기본값으로 사용한다.
|
||||||
- 제목 입력이 비어 있는 동안에는 무분별한 도배 방지를 위한 관리자 임의 삭제 가능성 안내 문구를 표시한다.
|
- 제목 입력이 비어 있는 동안에는 무분별한 도배 방지를 위한 관리자 임의 삭제 가능성 안내 문구를 표시한다.
|
||||||
- 티어표는 편집 화면에서 16:9 썸네일 이미지를 별도로 선택해 저장할 수 있고, 목록 카드에서는 그 이미지를 상단 대표 썸네일로 사용한다.
|
- 티어표는 편집 화면에서 16:9 썸네일 이미지를 별도로 선택해 저장할 수 있고, 목록 카드에서는 그 이미지를 상단 대표 썸네일로 사용한다.
|
||||||
|
- 편집 화면 상단 헤더는 좌측 제목/설명, 우측 썸네일 카드 구조를 사용하며 모바일에서는 한 열로 접힌다.
|
||||||
- 티어표 편집기의 아이콘 기본 크기는 `80px`이며, 사용자가 `48 / 60 / 80 / 100 / 120` 단계로 즉시 조절할 수 있다.
|
- 티어표 편집기의 아이콘 기본 크기는 `80px`이며, 사용자가 `48 / 60 / 80 / 100 / 120` 단계로 즉시 조절할 수 있다.
|
||||||
- 공개 티어표 목록과 `내 티어표` 목록은 제목 옆에 작성자 아바타와 표시명을 함께 보여준다.
|
- 공개 티어표 목록과 `내 티어표` 목록은 제목 옆에 작성자 아바타와 표시명을 함께 보여준다.
|
||||||
- 작성자 아바타 이미지가 없을 경우 목록 썸네일 fallback은 닉네임이 아니라 계정명 기준 첫 글자를 사용한다.
|
- 작성자 아바타 이미지가 없을 경우 목록 썸네일 fallback은 닉네임이 아니라 계정명 기준 첫 글자를 사용한다.
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# 업데이트 로그
|
# 업데이트 로그
|
||||||
|
|
||||||
|
## 2026-03-26 v0.1.39
|
||||||
|
- **에디터 헤더 재구성**: 티어표 편집 상단에서 게임명 kicker를 제거하고, 좌측 제목/설명 입력과 우측 썸네일 카드가 나란히 보이는 구조로 재정리
|
||||||
|
- **썸네일 영역 UX 개선**: 썸네일 미리보기와 선택/제거 버튼을 하나의 카드 안에 묶고, 모바일에서도 버튼이 카드 아래로 무너지지 않도록 밀도 있게 조정
|
||||||
|
|
||||||
## 2026-03-26 v0.1.38
|
## 2026-03-26 v0.1.38
|
||||||
- **관리자 기본 아이템 이름 수정 추가**: 게임 관리 화면의 현재 기본 아이템 목록에서 이름을 직접 수정하고 저장할 수 있도록 API와 UI를 보강
|
- **관리자 기본 아이템 이름 수정 추가**: 게임 관리 화면의 현재 기본 아이템 목록에서 이름을 직접 수정하고 저장할 수 있도록 API와 UI를 보강
|
||||||
- **티어표 썸네일 추가**: 티어표 편집 화면에서 별도 썸네일 이미지를 선택해 저장할 수 있도록 업로드 흐름을 추가하고, 게임별 공개 티어표/내 티어표 목록은 게임 카드처럼 상단 썸네일 + 하단 제목/작성자 정보 카드 구조로 변경
|
- **티어표 썸네일 추가**: 티어표 편집 화면에서 별도 썸네일 이미지를 선택해 저장할 수 있도록 업로드 흐름을 추가하고, 게임별 공개 티어표/내 티어표 목록은 게임 카드처럼 상단 썸네일 + 하단 제목/작성자 정보 카드 구조로 변경
|
||||||
|
|||||||
@@ -482,34 +482,41 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="head">
|
<section class="head">
|
||||||
<div class="head__meta">
|
<div class="heroCard">
|
||||||
<div class="kicker">{{ gameName || gameId }}</div>
|
<div class="heroCard__main">
|
||||||
<input v-model="title" class="titleInput" placeholder="티어표 이름을 입력하세요" :readonly="!canEdit" />
|
<input v-model="title" class="titleInput" placeholder="티어표 이름을 입력하세요" :readonly="!canEdit" />
|
||||||
<div v-if="untitledWarning" class="titleNotice">{{ untitledWarning }}</div>
|
<div v-if="untitledWarning" class="titleNotice">{{ untitledWarning }}</div>
|
||||||
<div v-if="canEdit" class="thumbComposer">
|
<input
|
||||||
<input ref="thumbnailFileEl" type="file" accept="image/*" class="hidden" @change="onThumbnailChange" />
|
v-model="description"
|
||||||
<div class="thumbComposer__preview">
|
class="descInput"
|
||||||
<img v-if="displayThumbnailUrl" class="thumbComposer__image" :src="displayThumbnailUrl" alt="썸네일 미리보기" />
|
placeholder="설명(선택): 이 티어표의 기준/룰"
|
||||||
<div v-else class="thumbComposer__empty">썸네일 없음</div>
|
:readonly="!canEdit"
|
||||||
</div>
|
/>
|
||||||
<div class="thumbComposer__actions">
|
<div class="hint">
|
||||||
<button class="btn btn--ghost" @click="openThumbnailFile">썸네일 선택</button>
|
<template v-if="canEdit">
|
||||||
<button class="btn btn--danger" :disabled="!pendingThumbnailFile && !thumbnailSrc" @click="clearThumbnail">썸네일 제거</button>
|
그룹 이름/순서 변경과 아이템 드래그&드롭이 가능합니다. 저장하려면 <b>저장</b>을 누르세요.
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
공개된 티어표를 보는 중입니다. 로그인한 작성자만 수정할 수 있어요.
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<div class="heroCard__side">
|
||||||
v-model="description"
|
<div class="thumbComposer">
|
||||||
class="descInput"
|
<input ref="thumbnailFileEl" type="file" accept="image/*" class="hidden" @change="onThumbnailChange" />
|
||||||
placeholder="설명(선택): 이 티어표의 기준/룰"
|
<div class="thumbComposer__header">
|
||||||
:readonly="!canEdit"
|
<div class="thumbComposer__eyebrow">대표 썸네일</div>
|
||||||
/>
|
<div class="thumbComposer__caption">목록 카드 상단에 표시됩니다.</div>
|
||||||
<div class="hint">
|
</div>
|
||||||
<template v-if="canEdit">
|
<div class="thumbComposer__preview">
|
||||||
그룹 이름/순서 변경과 아이템 드래그&드롭이 가능합니다. 저장하려면 <b>저장</b>을 누르세요.
|
<img v-if="displayThumbnailUrl" class="thumbComposer__image" :src="displayThumbnailUrl" alt="썸네일 미리보기" />
|
||||||
</template>
|
<div v-else class="thumbComposer__empty">썸네일 없음</div>
|
||||||
<template v-else>
|
</div>
|
||||||
공개된 티어표를 보는 중입니다. 로그인한 작성자만 수정할 수 있어요.
|
<div v-if="canEdit" class="thumbComposer__actions">
|
||||||
</template>
|
<button class="btn btn--ghost thumbComposer__button" @click="openThumbnailFile">썸네일 선택</button>
|
||||||
|
<button class="btn btn--danger thumbComposer__button" :disabled="!pendingThumbnailFile && !thumbnailSrc" @click="clearThumbnail">제거</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
@@ -630,30 +637,41 @@ onUnmounted(() => {
|
|||||||
gap: 14px;
|
gap: 14px;
|
||||||
padding: 6px 2px 14px;
|
padding: 6px 2px 14px;
|
||||||
}
|
}
|
||||||
.head__meta {
|
.heroCard {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
grid-template-columns: minmax(0, 1.5fr) minmax(280px, 360px);
|
||||||
|
gap: 18px;
|
||||||
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
.kicker {
|
.heroCard__main,
|
||||||
font-size: 12px;
|
.heroCard__side {
|
||||||
opacity: 0.7;
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.heroCard__main {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.heroCard__side {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.titleInput {
|
.titleInput {
|
||||||
width: min(100%, 920px);
|
width: 100%;
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
font-weight: 800;
|
font-weight: 800;
|
||||||
letter-spacing: -0.02em;
|
letter-spacing: -0.02em;
|
||||||
padding: 10px 12px;
|
padding: 14px 16px;
|
||||||
border-radius: 14px;
|
border-radius: 18px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: linear-gradient(180deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.04));
|
||||||
color: rgba(255, 255, 255, 0.92);
|
color: rgba(255, 255, 255, 0.92);
|
||||||
outline: none;
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.descInput {
|
.descInput {
|
||||||
width: min(100%, 920px);
|
width: 100%;
|
||||||
padding: 10px 12px;
|
min-height: 92px;
|
||||||
border-radius: 14px;
|
padding: 14px 16px;
|
||||||
|
border-radius: 18px;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
background: rgba(0, 0, 0, 0.18);
|
background: rgba(0, 0, 0, 0.18);
|
||||||
color: rgba(255, 255, 255, 0.92);
|
color: rgba(255, 255, 255, 0.92);
|
||||||
@@ -663,24 +681,47 @@ onUnmounted(() => {
|
|||||||
.hint {
|
.hint {
|
||||||
opacity: 0.78;
|
opacity: 0.78;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
.titleNotice {
|
.titleNotice {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
color: rgba(251, 191, 36, 0.94);
|
color: rgba(251, 191, 36, 0.94);
|
||||||
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
.thumbComposer {
|
.thumbComposer {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
width: min(100%, 920px);
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 22px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
background:
|
||||||
|
radial-gradient(circle at top right, rgba(96, 165, 250, 0.12), transparent 46%),
|
||||||
|
rgba(255, 255, 255, 0.04);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.thumbComposer__header {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.thumbComposer__eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
.thumbComposer__caption {
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.68;
|
||||||
}
|
}
|
||||||
.thumbComposer__preview {
|
.thumbComposer__preview {
|
||||||
width: min(100%, 360px);
|
width: 100%;
|
||||||
aspect-ratio: 16 / 9;
|
aspect-ratio: 16 / 9;
|
||||||
border-radius: 16px;
|
border-radius: 18px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
background: rgba(255, 255, 255, 0.04);
|
background: rgba(11, 18, 32, 0.78);
|
||||||
}
|
}
|
||||||
.thumbComposer__image {
|
.thumbComposer__image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -693,11 +734,16 @@ onUnmounted(() => {
|
|||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
color: rgba(255, 255, 255, 0.62);
|
color: rgba(255, 255, 255, 0.62);
|
||||||
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
.thumbComposer__actions {
|
.thumbComposer__actions {
|
||||||
display: flex;
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
flex-wrap: wrap;
|
}
|
||||||
|
.thumbComposer__button {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.actions {
|
.actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -774,7 +820,7 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
.btn--ghost {
|
.btn--ghost {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 10px;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.layout {
|
.layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -1073,6 +1119,9 @@ onUnmounted(() => {
|
|||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
}
|
}
|
||||||
@media (max-width: 980px) {
|
@media (max-width: 980px) {
|
||||||
|
.heroCard {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
.layout {
|
.layout {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
@@ -1089,5 +1138,13 @@ onUnmounted(() => {
|
|||||||
.row {
|
.row {
|
||||||
grid-template-columns: 150px 1fr;
|
grid-template-columns: 150px 1fr;
|
||||||
}
|
}
|
||||||
|
.thumbComposer {
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 18px;
|
||||||
|
}
|
||||||
|
.titleInput,
|
||||||
|
.descInput {
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user