From c128948d39f29d3cbb4a62ee9805b1ba8c7bca89 Mon Sep 17 00:00:00 2001 From: ByungCheol Date: Sun, 7 Jun 2026 16:29:54 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B3=84=EC=A2=8C=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EC=9B=94=EB=B3=84=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=E2=80=94=20=EA=B8=B8=EC=96=B4=EC=A7=80=EB=8A=94=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=20=ED=95=B4=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 계좌(은행/카드) 펼침 시 전체 평면 리스트 대신 월 단위로 표시 · ◀ YYYY.MM ▶ 월 네비게이션(데이터 있는 월만, 빈 달 건너뜀) + 그 달 합계 · 기본은 내역이 있는 가장 최근 월, 월 필터는 클라이언트단(walletEntries 재사용, 백엔드 무변경) Co-Authored-By: Claude Opus 4.8 --- src/views/account/AccountWalletView.vue | 109 ++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 9 deletions(-) diff --git a/src/views/account/AccountWalletView.vue b/src/views/account/AccountWalletView.vue index 3028055..6a29137 100644 --- a/src/views/account/AccountWalletView.vue +++ b/src/views/account/AccountWalletView.vue @@ -37,10 +37,47 @@ const form = reactive({ const submitting = ref(false) const formError = ref(null) -// 드롭다운(계좌별 내역) +// 드롭다운(계좌별 내역) — 월별로 표시 const expandedId = ref(null) const entriesByWallet = reactive({}) const loadingEntries = ref(false) +const entryMonth = reactive({}) // 계좌별 선택 월(YYYY-MM) + +function currentYm() { + const d = new Date() + return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}` +} +function ymLabel(ym) { + return ym ? ym.replace('-', '.') : '' +} +// 데이터가 있는 월 목록(오름차순) +function monthList(w) { + const set = new Set((entriesByWallet[w.id] || []).map((e) => (e.entryDate || '').slice(0, 7)).filter(Boolean)) + return [...set].sort() +} +function monthEntries(w) { + const ym = entryMonth[w.id] + return (entriesByWallet[w.id] || []).filter((e) => (e.entryDate || '').slice(0, 7) === ym) +} +function monthSum(w) { + return monthEntries(w).reduce((s, e) => s + effectAmount(e, w.id), 0) +} +function canPrev(w) { + return monthList(w).indexOf(entryMonth[w.id]) > 0 +} +function canNext(w) { + const ms = monthList(w) + const i = ms.indexOf(entryMonth[w.id]) + return i >= 0 && i < ms.length - 1 +} +// 데이터가 있는 월 사이로만 이동(빈 달 건너뜀) +function shiftMonth(w, delta) { + const ms = monthList(w) + if (!ms.length) return + const i = ms.indexOf(entryMonth[w.id]) + const j = Math.min(ms.length - 1, Math.max(0, (i === -1 ? ms.length - 1 : i) + delta)) + entryMonth[w.id] = ms[j] +} const isLiability = (t) => t === 'CARD' || t === 'LOAN' @@ -109,6 +146,9 @@ async function toggleExpand(w) { loadingEntries.value = true try { entriesByWallet[w.id] = await accountApi.walletEntries(w.id) + // 기본 선택 월 = 내역이 있는 가장 최근 월(없으면 이번 달) + const ms = monthList(w) + entryMonth[w.id] = ms.length ? ms[ms.length - 1] : currentYm() } catch { entriesByWallet[w.id] = [] } finally { @@ -366,15 +406,27 @@ onBeforeUnmount(() => sortable?.destroy()) @@ -576,6 +628,45 @@ button.primary { font-size: 0.78rem; margin: 0; } +.month-nav { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.3rem 0 0.4rem; + border-bottom: 1px solid var(--color-border); + margin-bottom: 0.3rem; +} +.mn-btn { + width: 1.8rem; + height: 1.8rem; + border: 1px solid var(--color-border); + border-radius: 4px; + background: var(--color-background-soft); + color: var(--color-text); + cursor: pointer; + line-height: 1; +} +.mn-btn:disabled { + opacity: 0.35; + cursor: not-allowed; +} +.mn-label { + font-weight: 700; + font-size: 0.9rem; + min-width: 4.2rem; + text-align: center; +} +.mn-sum { + margin-left: auto; + font-size: 0.82rem; + font-weight: 600; +} +.mn-sum.pos { + color: #2e7d32; +} +.mn-sum.neg { + color: #c0392b; +} .drop-list { list-style: none; }