diff --git a/components/admin/AdminFilterPillGroup.vue b/components/admin/AdminFilterPillGroup.vue
new file mode 100644
index 0000000..f2d0de7
--- /dev/null
+++ b/components/admin/AdminFilterPillGroup.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
diff --git a/components/admin/AdminSelectInput.vue b/components/admin/AdminSelectInput.vue
index c5bee96..e78180f 100644
--- a/components/admin/AdminSelectInput.vue
+++ b/components/admin/AdminSelectInput.vue
@@ -2,7 +2,7 @@
const props = defineProps({
/** 선택 입력 값 */
modelValue: {
- type: String,
+ type: [String, Number],
default: ''
},
/** 선택 옵션 목록 */
@@ -34,13 +34,22 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue'])
+/**
+ * option에 전달할 실제 값을 반환한다.
+ * @param {Object} option - 선택 옵션
+ * @returns {string|number} 선택 값
+ */
+const getOptionValue = (option) => option.value
+
/**
* 선택 값을 상위 컴포넌트에 전달한다.
* @param {Event} event - 변경 이벤트
* @returns {void}
*/
const updateValue = (event) => {
- emit('update:modelValue', event.target?.value || '')
+ const selectedValue = event.target?.value || ''
+ const selectedOption = props.options.find((option) => String(option.value) === selectedValue)
+ emit('update:modelValue', selectedOption ? selectedOption.value : selectedValue)
}
@@ -55,7 +64,7 @@ const updateValue = (event) => {
:aria-label="label"
@change="updateValue"
>
-
diff --git a/docs/map.md b/docs/map.md
index 451bd62..0addff4 100644
--- a/docs/map.md
+++ b/docs/map.md
@@ -189,7 +189,8 @@
| components/admin/AdminConfirmModal.vue | 관리자 공통 확인 모달(제목·본문 슬롯·확인/취소 버튼, primary/danger 톤) |
| components/admin/AdminToast.vue | 관리자 공통 우측 상단 토스트 표시(성공·오류·안내 톤, 화면별 상단 위치 조정) |
| components/admin/AdminSearchInput.vue | 관리자 공통 검색 입력(돋보기 아이콘, IME 검색 이벤트 전달, 외부 focus/select 노출) |
-| components/admin/AdminSelectInput.vue | 관리자 공통 select 입력(기본 필터·정렬 옵션, chevron 아이콘, `v-model` 전달) |
+| components/admin/AdminSelectInput.vue | 관리자 공통 select 입력(문자·숫자 옵션 값, 기본 필터·정렬 옵션, chevron 아이콘, `v-model` 전달) |
+| components/admin/AdminFilterPillGroup.vue | 관리자 공통 pill 필터 그룹(선택 상태·개수 배지·aria-pressed) |
| components/admin/AdminPagination.vue | 관리자 공통 번호형 페이지네이션(이전·다음 화살표, 첫/끝/현재 주변 페이지, ellipsis 접기) |
| components/admin/AdminAnalyticsTrendChart.vue | 관리자 통계 추이 막대 차트(축 라벨·증감 툴팁·기간 페이지 이동) |
| lib/analytics-chart-ui.js | 통계 차트 라벨·페이지네이션·증감 계산 유틸 |
diff --git a/docs/todo.md b/docs/todo.md
index 02582fb..81db33e 100644
--- a/docs/todo.md
+++ b/docs/todo.md
@@ -10,7 +10,7 @@
- `pages/admin/media/index.vue`: 미디어 목록, 폴더 트리, 상세 모달, 재생성 액션을 작은 단위로 분리.
- 관리자 UI 공통 컴포넌트 정리.
- 모달 패널, 필터 팝오버의 radius·shadow·z-index·색상 규칙을 공통화.
- - 게시글/페이지/미디어 목록의 고급 필터 레이아웃을 같은 패턴으로 유지.
+ - 멤버 고급 필터와 설정 화면 select 입력의 공통 컴포넌트 적용 범위 검토.
- 최근 브라우저 회귀 테스트 추가.
- 태그 추천 선택 후 다음 태그 후보가 포커스 해제 없이 유지되는지 확인.
- 관리자 미디어 대표 이미지/본문 이미지 탭 전환 중 역할 배지가 노출되지 않는지 확인.
diff --git a/docs/update.md b/docs/update.md
index dca7878..917d88d 100644
--- a/docs/update.md
+++ b/docs/update.md
@@ -1,5 +1,11 @@
# 업데이트 이력
+## v1.11.9
+
+- 관리자 공용 select 입력이 숫자 옵션 값을 보존하도록 보완.
+- 관리자 미디어 표시 개수 select를 공용 `AdminSelectInput`으로 교체.
+- 관리자 미디어 종류·사용 상태 필터를 공용 `AdminFilterPillGroup` 컴포넌트로 분리.
+
## v1.11.8
- 관리자 목록 select 입력을 공용 `AdminSelectInput` 컴포넌트로 분리.
diff --git a/package-lock.json b/package-lock.json
index ab6b7c2..454d917 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "sori.studio",
- "version": "1.11.8",
+ "version": "1.11.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "sori.studio",
- "version": "1.11.8",
+ "version": "1.11.9",
"hasInstallScript": true,
"dependencies": {
"@nuxtjs/tailwindcss": "^6.14.0",
diff --git a/package.json b/package.json
index c76b5b3..2d407ef 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "sori.studio",
- "version": "1.11.8",
+ "version": "1.11.9",
"private": true,
"type": "module",
"imports": {
diff --git a/pages/admin/media/index.vue b/pages/admin/media/index.vue
index fe4c1ea..8d57d83 100644
--- a/pages/admin/media/index.vue
+++ b/pages/admin/media/index.vue
@@ -23,7 +23,10 @@ definePageMeta({
const MEDIA_THUMBNAIL_ROOT = '썸네일'
/** 관리자 미디어 한 페이지 표시 개수 옵션 */
-const mediaPageSizeOptions = [50, 100, 200]
+const mediaPageSizeOptions = [50, 100, 200].map((size) => ({
+ value: size,
+ label: `${size}개`
+}))
/** 회원 등급 코드 → 미디어 배지 라벨 */
const memberRoleBadgeLabels = {
@@ -489,11 +492,11 @@ const mediaKindFilterOptions = computed(() => {
const countByKind = mediaCounts.value.kinds || {}
return [
- { id: 'all', label: '전체', count: countByKind.all || 0 },
- { id: 'image', label: '이미지', count: countByKind.image || 0 },
- { id: 'video', label: '영상', count: countByKind.video || 0 },
- { id: 'audio', label: '음악', count: countByKind.audio || 0 },
- { id: 'file', label: '파일', count: countByKind.file || 0 }
+ { value: 'all', label: '전체', count: countByKind.all || 0 },
+ { value: 'image', label: '이미지', count: countByKind.image || 0 },
+ { value: 'video', label: '영상', count: countByKind.video || 0 },
+ { value: 'audio', label: '음악', count: countByKind.audio || 0 },
+ { value: 'file', label: '파일', count: countByKind.file || 0 }
]
})
@@ -501,8 +504,8 @@ const mediaUsageFilterOptions = computed(() => {
const usageCounts = mediaCounts.value.usage || {}
return [
- { id: 'all', label: '전체', count: usageCounts.all || 0, unusedOnly: false },
- { id: 'unused', label: '미사용', count: usageCounts.unused || 0, unusedOnly: true }
+ { value: false, id: 'all', label: '전체', count: usageCounts.all || 0 },
+ { value: true, id: 'unused', label: '미사용', count: usageCounts.unused || 0 }
]
})
@@ -676,11 +679,11 @@ const setMediaPage = (page) => {
/**
* 관리자 미디어 페이지 표시 개수를 변경한다.
- * @param {Event} event - select 변경 이벤트
+ * @param {number} pageSize - 변경할 표시 개수
* @returns {void}
*/
-const setMediaPageSize = (event) => {
- mediaPageSize.value = Number(event.target.value) || 50
+const setMediaPageSize = (pageSize) => {
+ mediaPageSize.value = Number(pageSize) || 50
mediaPage.value = 1
clearMediaSelection()
closeMediaDetail()
@@ -1411,31 +1414,21 @@ watch(() => mediaPagination.value.page, (page) => {
@@ -1536,24 +1529,17 @@ watch(() => mediaPagination.value.page, (page) => {