Merge branch 'dev'
CI / build (push) Failing after 11m31s
Deploy / deploy (push) Failing after 15m5s

This commit is contained in:
ByungCheol
2026-06-07 16:29:54 +09:00
+94 -3
View File
@@ -37,10 +37,47 @@ const form = reactive({
const submitting = ref(false) const submitting = ref(false)
const formError = ref(null) const formError = ref(null)
// 드롭다운(계좌별 내역) // 드롭다운(계좌별 내역) — 월별로 표시
const expandedId = ref(null) const expandedId = ref(null)
const entriesByWallet = reactive({}) const entriesByWallet = reactive({})
const loadingEntries = ref(false) 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' const isLiability = (t) => t === 'CARD' || t === 'LOAN'
@@ -109,6 +146,9 @@ async function toggleExpand(w) {
loadingEntries.value = true loadingEntries.value = true
try { try {
entriesByWallet[w.id] = await accountApi.walletEntries(w.id) entriesByWallet[w.id] = await accountApi.walletEntries(w.id)
// 기본 선택 월 = 내역이 있는 가장 최근 월(없으면 이번 달)
const ms = monthList(w)
entryMonth[w.id] = ms.length ? ms[ms.length - 1] : currentYm()
} catch { } catch {
entriesByWallet[w.id] = [] entriesByWallet[w.id] = []
} finally { } finally {
@@ -366,8 +406,18 @@ onBeforeUnmount(() => sortable?.destroy())
<InvestPortfolio v-else-if="w.type === 'INVEST'" :wallet-id="w.id" @changed="load" /> <InvestPortfolio v-else-if="w.type === 'INVEST'" :wallet-id="w.id" @changed="load" />
<template v-else> <template v-else>
<p v-if="loadingEntries" class="drop-msg">불러오는 중...</p> <p v-if="loadingEntries" class="drop-msg">불러오는 중...</p>
<ul v-else-if="(entriesByWallet[w.id] || []).length" class="drop-list"> <template v-else-if="(entriesByWallet[w.id] || []).length">
<li v-for="e in entriesByWallet[w.id]" :key="e.id" class="drop-row"> <!-- 네비게이션 + 합계 -->
<div class="month-nav">
<button type="button" class="mn-btn" :disabled="!canPrev(w)" aria-label="이전 " @click="shiftMonth(w, -1)"></button>
<span class="mn-label">{{ ymLabel(entryMonth[w.id]) }}</span>
<button type="button" class="mn-btn" :disabled="!canNext(w)" aria-label="다음 " @click="shiftMonth(w, 1)"></button>
<span class="mn-sum" :class="monthSum(w) < 0 ? 'neg' : 'pos'">
합계 {{ monthSum(w) >= 0 ? '+' : '' }}{{ won(monthSum(w)) }}
</span>
</div>
<ul v-if="monthEntries(w).length" class="drop-list">
<li v-for="e in monthEntries(w)" :key="e.id" class="drop-row">
<span class="d-date">{{ entryDate(e.entryDate) }}</span> <span class="d-date">{{ entryDate(e.entryDate) }}</span>
<span class="d-desc">{{ entryDesc(e, w) }}</span> <span class="d-desc">{{ entryDesc(e, w) }}</span>
<span class="d-amount" :class="effectAmount(e, w.id) < 0 ? 'neg' : 'pos'"> <span class="d-amount" :class="effectAmount(e, w.id) < 0 ? 'neg' : 'pos'">
@@ -375,6 +425,8 @@ onBeforeUnmount(() => sortable?.destroy())
</span> </span>
</li> </li>
</ul> </ul>
<p v-else class="drop-msg"> 내역이 없습니다.</p>
</template>
<p v-else class="drop-msg">연관 내역이 없습니다.</p> <p v-else class="drop-msg">연관 내역이 없습니다.</p>
</template> </template>
</div> </div>
@@ -576,6 +628,45 @@ button.primary {
font-size: 0.78rem; font-size: 0.78rem;
margin: 0; 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 { .drop-list {
list-style: none; list-style: none;
} }