선형 계산(expenseForecast) 제거, AI 코멘트의 forecast/근거 표시. 경과일·총일수 전달. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,8 @@ const categoryList = ref([]) // 분류 목록(대/소분류 매핑용)
|
|||||||
const trendData = ref([]) // [{month, netWorth}]
|
const trendData = ref([]) // [{month, netWorth}]
|
||||||
const aiBullets = ref([]) // AI 재무 코멘트(현황 관찰)
|
const aiBullets = ref([]) // AI 재무 코멘트(현황 관찰)
|
||||||
const aiTips = ref([]) // AI 절약 가이드(실행 제안)
|
const aiTips = ref([]) // AI 절약 가이드(실행 제안)
|
||||||
|
const aiForecast = ref(null) // AI 월말 예상 지출(일회성 큰 지출 제외 추정)
|
||||||
|
const aiForecastNote = ref('') // 예상 지출 근거 문장
|
||||||
const aiLoading = ref(false)
|
const aiLoading = ref(false)
|
||||||
|
|
||||||
const periodLabel = computed(() => `${year.value}년 ${String(month.value).padStart(2, '0')}월`)
|
const periodLabel = computed(() => `${year.value}년 ${String(month.value).padStart(2, '0')}월`)
|
||||||
@@ -65,16 +67,8 @@ const expenseDelta = computed(() => {
|
|||||||
return { prev, diff, pct }
|
return { prev, diff, pct }
|
||||||
})
|
})
|
||||||
|
|
||||||
/* ===== 이번달 예상 지출 (실제 지출 추세로 월말 추정, run-rate) =====
|
/* 월말 예상 지출은 단순 선형(run-rate) 대신 AI가 추정한다(aiForecast).
|
||||||
경과일까지 실제 지출을 일평균으로 환산해 그 달 총일수로 추정. 이번 달에만 표시. */
|
일회성 큰 지출(노트북 등)을 일평균에서 제외해 현실적인 값을 준다. loadAiComment 참고. */
|
||||||
const expenseForecast = computed(() => {
|
|
||||||
const isCurrent = year.value === now.getFullYear() && month.value === now.getMonth() + 1
|
|
||||||
if (!isCurrent) return null // 지난/미래 달은 추정 불필요(이미 확정 또는 데이터 없음)
|
|
||||||
const totalDays = daysInMonth(year.value, month.value)
|
|
||||||
const elapsed = now.getDate()
|
|
||||||
const projected = elapsed > 0 ? Math.round((monthExpense.value / elapsed) * totalDays) : monthExpense.value
|
|
||||||
return { projected, elapsed, totalDays }
|
|
||||||
})
|
|
||||||
|
|
||||||
/* ===== 막대 차트 (기간별 예산 대비 지출) ===== */
|
/* ===== 막대 차트 (기간별 예산 대비 지출) ===== */
|
||||||
const bars = computed(() => {
|
const bars = computed(() => {
|
||||||
@@ -280,7 +274,10 @@ async function loadAiComment() {
|
|||||||
aiLoading.value = true
|
aiLoading.value = true
|
||||||
aiBullets.value = []
|
aiBullets.value = []
|
||||||
aiTips.value = []
|
aiTips.value = []
|
||||||
|
aiForecast.value = null
|
||||||
|
aiForecastNote.value = ''
|
||||||
try {
|
try {
|
||||||
|
const isCurrent = year.value === now.getFullYear() && month.value === now.getMonth() + 1
|
||||||
const res = await accountApi.aiComment({
|
const res = await accountApi.aiComment({
|
||||||
year: year.value,
|
year: year.value,
|
||||||
month: month.value,
|
month: month.value,
|
||||||
@@ -289,13 +286,19 @@ async function loadAiComment() {
|
|||||||
prevExpense: prevMonthExpense.value,
|
prevExpense: prevMonthExpense.value,
|
||||||
budget: budgetTotal.value,
|
budget: budgetTotal.value,
|
||||||
spent: spentTotal.value,
|
spent: spentTotal.value,
|
||||||
|
elapsedDays: isCurrent ? now.getDate() : 0,
|
||||||
|
totalDays: daysInMonth(year.value, month.value),
|
||||||
categories: (catData.value || []).slice(0, 8).map((c) => ({ category: c.category, total: c.total })),
|
categories: (catData.value || []).slice(0, 8).map((c) => ({ category: c.category, total: c.total })),
|
||||||
})
|
})
|
||||||
aiBullets.value = res?.bullets || []
|
aiBullets.value = res?.bullets || []
|
||||||
aiTips.value = res?.tips || []
|
aiTips.value = res?.tips || []
|
||||||
|
aiForecast.value = res?.forecast || null
|
||||||
|
aiForecastNote.value = res?.forecastNote || ''
|
||||||
} catch {
|
} catch {
|
||||||
aiBullets.value = []
|
aiBullets.value = []
|
||||||
aiTips.value = []
|
aiTips.value = []
|
||||||
|
aiForecast.value = null
|
||||||
|
aiForecastNote.value = ''
|
||||||
} finally {
|
} finally {
|
||||||
aiLoading.value = false
|
aiLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -417,17 +420,15 @@ onMounted(async () => {
|
|||||||
{{ expenseDelta.diff >= 0 ? '+' : '' }}{{ won(expenseDelta.diff) }}<template v-if="expenseDelta.pct !== null"> ({{ expenseDelta.pct >= 0 ? '+' : '' }}{{ expenseDelta.pct }}%)</template>
|
{{ expenseDelta.diff >= 0 ? '+' : '' }}{{ won(expenseDelta.diff) }}<template v-if="expenseDelta.pct !== null"> ({{ expenseDelta.pct >= 0 ? '+' : '' }}{{ expenseDelta.pct }}%)</template>
|
||||||
</b>
|
</b>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="expenseForecast" class="ie-row forecast">
|
<div v-if="aiForecast" class="ie-row forecast">
|
||||||
<span>예상 지출 <small>월말 추정</small></span>
|
<span>예상 지출 <small>월말 추정 · AI</small></span>
|
||||||
<b class="expense">{{ won(expenseForecast.projected) }}</b>
|
<b class="expense">{{ won(aiForecast) }}</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="ie-bar">
|
<div class="ie-bar">
|
||||||
<div class="ie-fill income" :style="{ width: incomePct + '%' }"></div>
|
<div class="ie-fill income" :style="{ width: incomePct + '%' }"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ie-row net"><span>수지</span><b :class="monthIncome - monthExpense < 0 ? 'expense' : 'income'">{{ won(monthIncome - monthExpense) }}</b></div>
|
<div class="ie-row net"><span>수지</span><b :class="monthIncome - monthExpense < 0 ? 'expense' : 'income'">{{ won(monthIncome - monthExpense) }}</b></div>
|
||||||
<p v-if="expenseForecast" class="forecast-note">
|
<p v-if="aiForecastNote" class="forecast-note">{{ aiForecastNote }}</p>
|
||||||
최근 추세 기준({{ expenseForecast.elapsed }}/{{ expenseForecast.totalDays }}일 · 현재 {{ won(monthExpense) }})<template v-if="budgetTotal > 0">, 예산 대비 {{ Math.round((expenseForecast.projected / budgetTotal) * 100) }}%</template>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user