v1.4.1: 관리자 미디어 업로드 한도·라이브 에디터 UX 개선

종류별 업로드 크기 한도와 413 안내를 추가하고, 임베드·미디어 라이브 프리뷰·제목 Enter 포커스·스크롤 동작을 보정한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-21 15:33:23 +09:00
parent f8e04003fd
commit 095a8fa5f0
25 changed files with 1445 additions and 103 deletions

View File

@@ -57,6 +57,13 @@ const isThumbnailDiskItem = (item) => Boolean(item?.url?.includes('/members/avat
*/
const isMediaItemLocked = (item) => Boolean(item?.usage?.length) || Boolean(item?.avatarOwner)
/**
* 미디어 항목 종류를 반환한다.
* @param {Object} item - 미디어 항목
* @returns {'image'|'video'|'audio'|'file'} 미디어 종류
*/
const getMediaItemKind = (item) => item?.kind || 'image'
const libraryMediaItems = computed(() => (mediaItems.value || []).filter((item) => !isThumbnailDiskItem(item)))
const thumbnailMediaItems = computed(() => (mediaItems.value || []).filter((item) => isThumbnailDiskItem(item)))
@@ -720,7 +727,18 @@ const deleteMedia = async (item) => {
type="button"
@click="openMediaDetail(item)"
>
<img class="admin-media__image aspect-square w-full bg-surface object-cover" :src="item.url" :alt="item.title">
<img
v-if="getMediaItemKind(item) === 'image'"
class="admin-media__image aspect-square w-full bg-surface object-cover"
:src="item.url"
:alt="item.title"
>
<span
v-else
class="admin-media__image flex aspect-square w-full items-center justify-center bg-surface text-xs font-bold uppercase tracking-[0.18em] text-muted"
>
{{ getMediaItemKind(item) }}
</span>
<span
v-if="item.avatarOwner"
class="admin-media__usage-badge pointer-events-none absolute right-1.5 top-1.5 rounded bg-emerald-800 px-1.5 py-0.5 text-[10px] font-semibold text-white"
@@ -796,7 +814,32 @@ const deleteMedia = async (item) => {
>
<section class="admin-media__modal-panel grid max-h-[86vh] w-full max-w-5xl overflow-hidden bg-white text-ink shadow-xl lg:grid-cols-[minmax(0,1fr)_22rem]">
<div class="admin-media__preview grid min-h-[20rem] place-items-center bg-[#f5f5f2] p-5">
<img class="admin-media__preview-image max-h-[72vh] max-w-full object-contain" :src="selectedMedia.url" :alt="selectedMedia.title">
<img
v-if="getMediaItemKind(selectedMedia) === 'image'"
class="admin-media__preview-image max-h-[72vh] max-w-full object-contain"
:src="selectedMedia.url"
:alt="selectedMedia.title"
>
<video
v-else-if="getMediaItemKind(selectedMedia) === 'video'"
class="admin-media__preview-image max-h-[72vh] max-w-full bg-black"
:src="selectedMedia.url"
controls
preload="metadata"
/>
<audio
v-else-if="getMediaItemKind(selectedMedia) === 'audio'"
class="w-full max-w-xl"
:src="selectedMedia.url"
controls
preload="metadata"
/>
<span
v-else
class="rounded border border-line bg-white px-5 py-4 text-sm font-semibold text-muted"
>
{{ selectedMedia.name }}
</span>
</div>
<aside class="admin-media__detail grid max-h-[86vh] content-start gap-5 overflow-y-auto border-l border-line p-5">