v0.0.89: 미디어 선택 토글 가시성, posts·미분류·썸네일 경로 명세
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# 의사결정 이력
|
||||
|
||||
## 2026-05-12 v0.0.89
|
||||
|
||||
### 미디어 선택 토글 가시성
|
||||
|
||||
네이티브 체크박스는 배경·브라우저 기본 스타일 때문에 흰 썸네일 위에서 거의 보이지 않는 사례가 있어, 동일 hit 영역을 유지한 채 명암이 큰 커스텀 토글로 바꿨다. 폴더 트리에 나오는 `posts`·`미분류` 등은 디스크 경로와 DB 메타 규칙을 문서에 적어 운영자가 혼동하지 않도록 했다.
|
||||
|
||||
## 2026-05-12 v0.0.88
|
||||
|
||||
### 관리자 미디어 선택·폴더 UX
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
| pages/admin/pages/index.vue | 페이지 목록 |
|
||||
| pages/admin/pages/new.vue | 페이지 작성, 저장 토스트 |
|
||||
| pages/admin/pages/[id].vue | 페이지 수정, 저장/삭제 토스트 |
|
||||
| pages/admin/media/index.vue | 미디어 관리, 폴더 트리·폴더 추가 모달·폴더 삭제, 썸네일 클릭 미리보기 모달, 체크박스 복수 선택·Shift 범위, 드래그로 폴더 이동 |
|
||||
| pages/admin/media/index.vue | 미디어 관리, 폴더 트리·폴더 추가 모달·폴더 삭제, 썸네일 클릭 미리보기 모달, 좌상단 선택 토글·Shift 범위, 드래그로 폴더 이동 |
|
||||
| pages/admin/navigation/index.vue | 메뉴/네비게이션 관리(변경 시에만 메뉴 저장 활성) |
|
||||
| pages/admin/tags/index.vue | 태그 관리(메인 태그 드래그 정렬·일반 강등, 일반 태그 검색/메인 전환/삭제), 액션 피드백 토스트, 순서 변경 시에만 정렬 저장 활성 |
|
||||
| pages/admin/tags/new.vue | 태그 생성 |
|
||||
|
||||
@@ -554,16 +554,21 @@ components/content/
|
||||
```
|
||||
/uploads/posts/YYYY/MM/filename.webp
|
||||
/uploads/pages/YYYY/MM/filename.webp
|
||||
/uploads/members/avatars/YYYY/MM/filename.webp
|
||||
/uploads/system/logo.png
|
||||
/uploads/system/favicon.png
|
||||
```
|
||||
|
||||
- 디스크 상대 경로의 **첫 번째 디렉터리 이름**(`posts`, `pages`, `members` 등)이 `media_metadata`가 없을 때 미디어 라이브러리에 보이는 **기본 폴더(논리 분류)**가 된다. 관리자 에디터 업로드 API는 현재 `public/uploads/posts/YYYY/MM/`에만 저장하므로, 메타가 없는 새 이미지는 기본적으로 **`posts`** 트리 아래로 잡힌다.
|
||||
- `미분류`는 예약된 논리 폴더이며, 사용자가 폴더를 지정하지 않았거나 폴더 삭제 후 되돌림 등으로 `media_metadata.category`가 `미분류`로 저장된 항목이 여기에 모인다. 디스크 경로가 `posts/...`인데도 화면에 `미분류`로 보이려면 메타가 `미분류`로 저장돼 있어야 한다.
|
||||
- 회원 썸네일은 `public/uploads/members/avatars/YYYY/MM/`에 WebP로 저장되며, 업로드 시 서버가 `media_metadata`에 **`회원/썸네일`** 카테고리를 넣어 미디어 라이브러리에서 회원 영역으로 묶인다(자동으로 `미분류`에 들어가지 않는다).
|
||||
|
||||
- 관리자 이미지 업로드 API는 `image/jpeg`, `image/png`, `image/webp`, `image/gif`만 허용한다.
|
||||
- 업로드 파일 크기 제한은 `MAX_FILE_SIZE` 환경 변수를 따른다.
|
||||
- 로컬 개발 업로드 파일은 `public/uploads/posts/YYYY/MM/` 아래 저장하고 `/uploads/posts/YYYY/MM/filename` URL로 제공한다.
|
||||
- 관리자 미디어 화면은 왼쪽 폴더 트리와 오른쪽 고밀도 썸네일 갤러리, 검색, 파일명 변경, 개별 삭제를 제공한다.
|
||||
- 관리자는 폴더 추가 버튼으로 모달에서 새 폴더·하위 폴더 이름을 입력해 만들 수 있으며, `미분류`를 제외한 폴더는 목록에서 삭제할 수 있다. 폴더 삭제 시 해당 경로 및 하위 경로로 분류돼 있던 미디어 메타는 모두 `미분류`로 되돌린다.
|
||||
- 썸네일 본문(이미지·파일명) 한 번 클릭 시 상세(미리보기) 모달이 열리고, 썸네일 좌측 상단 체크박스로 개별 선택한다. Shift+체크로 범위 선택이 가능하다.
|
||||
- 썸네일 본문(이미지·파일명) 한 번 클릭 시 상세(미리보기) 모달이 열리고, 썸네일 좌측 상단 **선택 토글**로 개별 선택한다. Shift+클릭으로 범위 선택이 가능하다.
|
||||
- 선택한 미디어를 폴더로 드래그하면 해당 미디어들의 폴더 경로가 일괄 변경된다.
|
||||
- 미디어 파일 경로, 사용 현황, 용량 등 세부 정보는 상세 모달에서 표시한다.
|
||||
- 미디어 폴더는 실제 파일 경로를 옮기지 않고 `media_metadata` 테이블에 URL별 경로 메타데이터로 저장한다.
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# 업데이트 이력
|
||||
|
||||
## v0.0.89
|
||||
|
||||
- 관리자 미디어 썸네일 선택 컨트롤을 네이티브 체크박스에서 대비가 분명한 토글 버튼(미선택: 흰 배경·진한 테두리, 선택: 진한 배경·흰 체크)으로 교체.
|
||||
- `docs/spec.md`에 `posts`·`미분류`·`회원/썸네일`과 디스크 경로·`media_metadata` 관계를 명시.
|
||||
|
||||
## v0.0.88
|
||||
|
||||
- 관리자 미디어: 썸네일 본문 클릭 시 상세 모달, 좌측 상단 체크박스로 다중 선택(Shift 범위 유지), 툴바 `현재 폴더로 이동` 제거.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sori.studio",
|
||||
"version": "0.0.88",
|
||||
"version": "0.0.89",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"imports": {
|
||||
|
||||
@@ -534,15 +534,31 @@ const deleteMedia = async (item) => {
|
||||
draggable="true"
|
||||
@dragstart="startMediaDrag($event, item)"
|
||||
>
|
||||
<label class="admin-media__select-label absolute left-1.5 top-1.5 z-10 grid size-6 cursor-pointer place-items-center rounded border border-line bg-white/95 shadow-sm hover:bg-white">
|
||||
<span class="sr-only">{{ item.name }} 선택</span>
|
||||
<input
|
||||
class="admin-media__select-checkbox size-3.5 rounded border-line text-[#15171a] focus:ring-[#15171a]"
|
||||
type="checkbox"
|
||||
:checked="isMediaSelected(item)"
|
||||
@click.stop.prevent="toggleMediaSelection(item, index, $event)"
|
||||
<button
|
||||
type="button"
|
||||
class="admin-media__select-toggle absolute left-1.5 top-1.5 z-10 grid size-7 place-items-center rounded-md border-2 shadow-md outline-none transition focus-visible:ring-2 focus-visible:ring-[#15171a] focus-visible:ring-offset-1"
|
||||
:class="isMediaSelected(item)
|
||||
? 'border-white bg-[#15171a] text-white'
|
||||
: 'border-[#394047] bg-white/95 text-[#15171a] hover:border-[#15171a] hover:bg-white'"
|
||||
:aria-label="`${item.name} 선택`"
|
||||
:aria-pressed="isMediaSelected(item)"
|
||||
@click.stop="toggleMediaSelection(item, index, $event)"
|
||||
>
|
||||
<svg
|
||||
v-if="isMediaSelected(item)"
|
||||
class="size-4 shrink-0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
</label>
|
||||
<path d="M20 6 9 17l-5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
class="admin-media__thumb relative flex w-full flex-col text-left outline-none"
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user