diff --git a/src/views/account/AccountView.vue b/src/views/account/AccountView.vue index a71c13c..178bf41 100644 --- a/src/views/account/AccountView.vue +++ b/src/views/account/AccountView.vue @@ -229,28 +229,43 @@ async function loadCategories() { } } // 현재 구분(수입/지출)에 맞는 분류 + 편집 중 현재 값 보존 -const categoryOptions = computed(() => { - const names = categories.value.filter((c) => c.type === form.type).map((c) => c.name) - if (form.category && !names.includes(form.category)) names.unshift(form.category) +// ===== 분류: 대분류 → 소분류 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 }) -// 대분류로 그룹핑(소분류 = 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 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) }) +// form.category 로부터 대분류 추론 (편집 진입 시) +function syncCategoryMajor() { + const cur = form.category + if (!cur) { + categoryMajor.value = '' + return + } + const c = categories.value.find((x) => x.type === form.type && x.name === cur) + if (c && c.parentId != null) { + const parent = categories.value.find((x) => x.id === c.parentId) + categoryMajor.value = parent ? parent.name : cur + } else { + categoryMajor.value = cur + } +} +// 대분류 변경: 소분류 있으면 소분류 선택 대기 / 없으면 대분류가 곧 분류 +function onMajorChange() { + form.category = subOptions.value.length ? '' : categoryMajor.value +} // 필터용 전체 분류명 (중복 제거) const allCategoryNames = computed(() => [...new Set(categories.value.map((c) => c.name))]) @@ -270,15 +285,22 @@ async function confirmAddCategory() { const name = newCategoryName.value.trim() if (!name) return catSubmitting.value = true + // 대분류가 선택돼 있으면 그 아래 소분류로 추가, 아니면 대분류로 추가 + const parent = categories.value.find( + (c) => c.type === form.type && c.parentId == null && c.name === categoryMajor.value, + ) + const parentId = parent ? parent.id : null try { - await accountApi.createCategory({ type: form.type, name }) + await accountApi.createCategory({ type: form.type, name, parentId }) await loadCategories() form.category = name + if (!parentId) categoryMajor.value = name // 새 대분류면 대분류로 선택 cancelAddCategory() } catch (e) { if (e.response?.status === 409) { // 이미 있는 분류면 그대로 선택 form.category = name + syncCategoryMajor() cancelAddCategory() } else { alert(e.response?.data?.message || '분류 추가에 실패했습니다.') @@ -287,6 +309,11 @@ async function confirmAddCategory() { catSubmitting.value = false } } +// 구분(수입/지출/이체) 변경 시 분류 초기화 +function onTypeChange() { + form.category = '' + categoryMajor.value = '' +} // 태그 (태그 관리에 등록된 태그 선택) const tagOptions = ref([]) // [{ id, name }] @@ -412,6 +439,7 @@ function openCreate() { editingPending.value = false Object.assign(form, { entryDate: todayStr(), type: 'EXPENSE', category: '', amount: null, memo: '', walletKind: 'BANK', walletId: '', toWalletKind: '', toWalletId: '', principal: null, interest: null, annualFee: null, installmentMonths: '' }) selectedTagIds.value = [] + syncCategoryMajor() cancelAddCategory() formError.value = null formOpen.value = true @@ -441,6 +469,7 @@ function openEdit(e) { const nameToId = {} tagOptions.value.forEach((t) => (nameToId[t.name] = t.id)) selectedTagIds.value = (e.tags || []).map((n) => nameToId[n]).filter(Boolean) + syncCategoryMajor() cancelAddCategory() formError.value = null formOpen.value = true @@ -756,7 +785,7 @@ onMounted(async () => {