feat: 분류 대/소분류 UI — 관리·입력·예산·고정지출·통계
CI / build (push) Failing after 12m0s

- 분류 관리: 대분류 선택·소분류 들여쓰기(드래그 정렬 유지)
- 입력/예산/고정지출 분류 드롭다운 대분류 optgroup 그룹핑(값은 소분류명)
- 대시보드: 파이는 소분류 기준 + 대분류 합계 롤업 표시
- 릴리스 노트 33

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-06-22 23:16:46 +09:00
parent b1f85cee52
commit 6ab8d331e3
6 changed files with 182 additions and 9 deletions
+25 -1
View File
@@ -234,6 +234,23 @@ const categoryOptions = computed(() => {
if (form.category && !names.includes(form.category)) names.unshift(form.category)
return names
})
// 대분류로 그룹핑(소분류 = optgroup 옵션 / 자식없는 대분류 = 단독 옵션). 값은 분류명(소분류) 그대로.
const categoryGroups = computed(() => {
const list = categories.value.filter((c) => c.type === form.type)
const groups = []
if (form.category && !list.some((c) => c.name === form.category)) {
groups.push({ key: '_cur', label: null, options: [form.category] })
}
for (const m of list.filter((c) => c.parentId == null)) {
const children = list.filter((c) => c.parentId === m.id)
if (children.length) {
groups.push({ key: 'g' + m.id, label: m.name, options: children.map((c) => c.name) })
} else {
groups.push({ key: 'i' + m.id, label: null, options: [m.name] })
}
}
return groups
})
// 필터용 전체 분류명 (중복 제거)
const allCategoryNames = computed(() => [...new Set(categories.value.map((c) => c.name))])
@@ -811,7 +828,14 @@ onMounted(async () => {
<div v-if="!addingCategory" class="cat-input">
<select v-model="form.category" :disabled="submitting">
<option value="">(선택)</option>
<option v-for="c in categoryOptions" :key="c" :value="c">{{ c }}</option>
<template v-for="g in categoryGroups" :key="g.key">
<optgroup v-if="g.label" :label="g.label">
<option v-for="n in g.options" :key="n" :value="n">{{ n }}</option>
</optgroup>
<template v-else>
<option v-for="n in g.options" :key="n" :value="n">{{ n }}</option>
</template>
</template>
</select>
<button type="button" class="cat-add-btn" :disabled="submitting" @click="startAddCategory">+ 추가</button>
</div>