feat: 통계 분류별 집계를 대분류 기준으로 (소분류 펼침 제거로 스크롤 단축)
- catByMajor: 소분류를 부모 대분류로 합산해 파이/범례 표기 - 중복되던 '대분류 합계' 섹션 제거 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -114,12 +114,32 @@ function hideTip() {
|
||||
const PIE_R = 46 // viewBox 120 기준, 선두께 20(hover 24) 포함해도 잘리지 않도록 (46+12=58 ≤ 60)
|
||||
const PIE_C = 2 * Math.PI * PIE_R
|
||||
const PIE_COLORS = ['#3b82a6', '#e67e22', '#2e7d32', '#9b59b6', '#c0392b', '#16a085', '#f39c12', '#8e44ad', '#27ae60', '#d35400', '#2980b9', '#7f8c8d']
|
||||
const catTotal = computed(() => catData.value.reduce((s, d) => s + d.total, 0))
|
||||
// 분류별 집계를 대분류 기준으로 묶는다 (소분류까지 펼치면 항목이 너무 많아 스크롤이 길어짐).
|
||||
// 소분류는 부모(대분류)명으로 합산, 매핑이 없으면 자기 자신을 대분류로 본다.
|
||||
const catByMajor = computed(() => {
|
||||
const list = categoryList.value.filter((c) => c.type === catType.value)
|
||||
const byId = {}
|
||||
list.forEach((c) => (byId[c.id] = c))
|
||||
const toMajor = (name) => {
|
||||
const c = list.find((x) => x.name === name)
|
||||
if (c && c.parentId != null && byId[c.parentId]) return byId[c.parentId].name
|
||||
return name
|
||||
}
|
||||
const map = {}
|
||||
for (const d of catData.value) {
|
||||
const major = toMajor(d.category)
|
||||
map[major] = (map[major] || 0) + d.total
|
||||
}
|
||||
return Object.entries(map)
|
||||
.map(([category, total]) => ({ category, total }))
|
||||
.sort((a, b) => b.total - a.total)
|
||||
})
|
||||
const catTotal = computed(() => catByMajor.value.reduce((s, d) => s + d.total, 0))
|
||||
const pieSegments = computed(() => {
|
||||
const total = catTotal.value
|
||||
if (total <= 0) return []
|
||||
let acc = 0
|
||||
return catData.value.map((d, i) => {
|
||||
return catByMajor.value.map((d, i) => {
|
||||
const len = (d.total / total) * PIE_C
|
||||
const seg = {
|
||||
category: d.category,
|
||||
@@ -196,26 +216,6 @@ async function loadCategoryList() {
|
||||
categoryList.value = []
|
||||
}
|
||||
}
|
||||
// 소분류명 → 대분류명 매핑(현재 구분). 소분류가 아니면 자기 자신.
|
||||
const majorRollup = computed(() => {
|
||||
const list = categoryList.value.filter((c) => c.type === catType.value)
|
||||
const byId = {}
|
||||
list.forEach((c) => (byId[c.id] = c))
|
||||
const toMajor = (name) => {
|
||||
const c = list.find((x) => x.name === name)
|
||||
if (c && c.parentId != null && byId[c.parentId]) return byId[c.parentId].name
|
||||
return name
|
||||
}
|
||||
const map = {}
|
||||
for (const d of catData.value) {
|
||||
const major = toMajor(d.category)
|
||||
map[major] = (map[major] || 0) + d.total
|
||||
}
|
||||
const rows = Object.entries(map).map(([category, total]) => ({ category, total })).sort((a, b) => b.total - a.total)
|
||||
// 실제로 묶인 게 있을 때만(소분류 존재) 노출
|
||||
const hasSub = list.some((c) => c.parentId != null) && rows.length < catData.value.length
|
||||
return hasSub ? rows : []
|
||||
})
|
||||
function setCatType(t) {
|
||||
catType.value = t
|
||||
loadCat()
|
||||
@@ -422,16 +422,7 @@ onMounted(async () => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="majorRollup.length" class="major-rollup">
|
||||
<div class="mr-head">대분류 합계</div>
|
||||
<ul class="mr-list">
|
||||
<li v-for="m in majorRollup" :key="m.category">
|
||||
<span class="mr-cat">{{ m.category }}</span>
|
||||
<span class="mr-amt">{{ won(m.total) }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p v-else class="empty">{{ catType === 'EXPENSE' ? '지출' : '수입' }} 내역이 없습니다.</p>
|
||||
<p v-if="!pieSegments.length" class="empty">{{ catType === 'EXPENSE' ? '지출' : '수입' }} 내역이 없습니다.</p>
|
||||
</div>
|
||||
|
||||
<!-- 기간별 예산 대비 지출 막대 차트 -->
|
||||
|
||||
Reference in New Issue
Block a user