fix: 분류 아코디언 화면 교정 — 분류관리 롤백 · 내역추가 적용
- CategoryView(분류 관리): 잘못 적용된 대분류 아코디언 제거, 소분류 항상 표시로 복원 - AccountView(내역 추가/수정): 2단 드롭다운 → 대분류 아코디언으로 교체 · 대분류 클릭 → 소분류 펼침(없으면 바로 선택) · 소분류 클릭 → 즉시 선택 / 선택 항목 초록 하이라이트 · 수정 진입 시 해당 대분류 자동 펼침 유지 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -276,25 +276,27 @@ async function loadCategories() {
|
||||
categories.value = []
|
||||
}
|
||||
}
|
||||
// 현재 구분(수입/지출)에 맞는 분류 + 편집 중 현재 값 보존
|
||||
// ===== 분류: 대분류 → 소분류 2단 선택 (긴 드롭다운 스크롤 방지) =====
|
||||
const categoryMajor = ref('') // 선택된 대분류 이름
|
||||
// 현재 구분의 대분류(부모 없음) 이름 + 편집 중 현재 대분류 보존
|
||||
const majorOptions = computed(() => {
|
||||
const names = categories.value
|
||||
.filter((c) => c.type === form.type && c.parentId == null)
|
||||
.map((c) => c.name)
|
||||
if (categoryMajor.value && !names.includes(categoryMajor.value)) names.unshift(categoryMajor.value)
|
||||
return names
|
||||
})
|
||||
// 선택된 대분류의 소분류 이름 목록 (없으면 대분류 자체가 분류)
|
||||
const subOptions = computed(() => {
|
||||
const major = categories.value.find(
|
||||
(c) => c.type === form.type && c.parentId == null && c.name === categoryMajor.value,
|
||||
)
|
||||
if (!major) return []
|
||||
return categories.value.filter((c) => c.parentId === major.id).map((c) => c.name)
|
||||
})
|
||||
// ===== 분류: 대분류 아코디언 → 소분류 선택 =====
|
||||
const categoryMajor = ref('') // 펼쳐진 대분류 이름
|
||||
// 현재 구분의 대분류 객체 목록
|
||||
const majorOptionsData = computed(() =>
|
||||
categories.value.filter((c) => c.type === form.type && c.parentId == null)
|
||||
)
|
||||
// 특정 대분류의 소분류 객체 목록
|
||||
function subsByMajor(majorId) {
|
||||
return categories.value.filter((c) => c.parentId === majorId)
|
||||
}
|
||||
// 대분류 클릭: 소분류 있으면 펼침 토글, 없으면 바로 선택
|
||||
function onMajorClick(major) {
|
||||
if (categoryMajor.value === major.name) {
|
||||
categoryMajor.value = ''
|
||||
form.category = ''
|
||||
} else {
|
||||
categoryMajor.value = major.name
|
||||
if (!subsByMajor(major.id).length) form.category = major.name
|
||||
else form.category = ''
|
||||
}
|
||||
}
|
||||
// form.category 로부터 대분류 추론 (편집 진입 시)
|
||||
function syncCategoryMajor() {
|
||||
const cur = form.category
|
||||
@@ -310,10 +312,7 @@ function syncCategoryMajor() {
|
||||
categoryMajor.value = cur
|
||||
}
|
||||
}
|
||||
// 대분류 변경: 소분류 있으면 소분류 선택 대기 / 없으면 대분류가 곧 분류
|
||||
function onMajorChange() {
|
||||
form.category = subOptions.value.length ? '' : categoryMajor.value
|
||||
}
|
||||
|
||||
// 필터용 전체 분류명 (중복 제거)
|
||||
const allCategoryNames = computed(() => [...new Set(categories.value.map((c) => c.name))])
|
||||
|
||||
@@ -1121,16 +1120,28 @@ onMounted(async () => {
|
||||
</template>
|
||||
|
||||
<label v-if="form.type === 'INCOME' || form.type === 'EXPENSE'">분류
|
||||
<div v-if="!addingCategory" class="cat-input cascade">
|
||||
<select v-model="categoryMajor" :disabled="submitting" @change="onMajorChange" title="대분류">
|
||||
<option value="">대분류 선택</option>
|
||||
<option v-for="m in majorOptions" :key="m" :value="m">{{ m }}</option>
|
||||
</select>
|
||||
<select v-if="subOptions.length" v-model="form.category" :disabled="submitting" title="소분류">
|
||||
<option value="">소분류 선택</option>
|
||||
<option v-for="s in subOptions" :key="s" :value="s">{{ s }}</option>
|
||||
</select>
|
||||
<button type="button" class="cat-add-btn" :disabled="submitting" @click="startAddCategory">+ 추가</button>
|
||||
<div v-if="!addingCategory" class="cat-accordion">
|
||||
<div v-for="m in majorOptionsData" :key="m.id" class="acc-major">
|
||||
<button
|
||||
type="button" class="acc-major-btn"
|
||||
:class="{ active: form.category === m.name && !subsByMajor(m.id).length, expanded: categoryMajor === m.name && subsByMajor(m.id).length }"
|
||||
:disabled="submitting"
|
||||
@click="onMajorClick(m)"
|
||||
>
|
||||
<span class="acc-arrow">{{ subsByMajor(m.id).length ? (categoryMajor === m.name ? '▼' : '▶') : '' }}</span>
|
||||
{{ m.name }}
|
||||
</button>
|
||||
<div v-if="categoryMajor === m.name && subsByMajor(m.id).length" class="acc-subs">
|
||||
<button
|
||||
v-for="s in subsByMajor(m.id)" :key="s.id"
|
||||
type="button" class="acc-sub-btn"
|
||||
:class="{ active: form.category === s.name }"
|
||||
:disabled="submitting"
|
||||
@click="form.category = s.name"
|
||||
>{{ s.name }}</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="cat-add-btn acc-add" :disabled="submitting" @click="startAddCategory">+ 추가</button>
|
||||
</div>
|
||||
<div v-else class="cat-input">
|
||||
<input
|
||||
@@ -1871,13 +1882,6 @@ button.primary {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.cat-input.cascade {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.cat-input.cascade select {
|
||||
flex: 1 1 40%;
|
||||
}
|
||||
.cat-input select,
|
||||
.cat-input input {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
@@ -1892,6 +1896,79 @@ button.primary {
|
||||
border-color: hsla(160, 100%, 37%, 1);
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
}
|
||||
/* 분류 아코디언 */
|
||||
.cat-accordion {
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.acc-major-btn {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.45rem 0.7rem;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font-size: 0.88rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
.acc-major-btn:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.acc-major-btn.active,
|
||||
.acc-major-btn.expanded {
|
||||
background: var(--color-background-soft);
|
||||
font-weight: 600;
|
||||
}
|
||||
.acc-major-btn.active {
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
}
|
||||
.acc-arrow {
|
||||
font-size: 0.6rem;
|
||||
flex: none;
|
||||
width: 0.9rem;
|
||||
text-align: center;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.acc-subs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.acc-sub-btn {
|
||||
padding: 0.38rem 0.7rem 0.38rem 2rem;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: var(--color-background-soft);
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font-size: 0.84rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
.acc-sub-btn:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.acc-sub-btn.active {
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
font-weight: 600;
|
||||
}
|
||||
.acc-add {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-top: 1px dashed var(--color-border);
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
color: var(--color-text);
|
||||
opacity: 0.6;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
.tag-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user