- 미확인(pending) 내역에 '확인필요' 배지 + 행 강조 + '확인' 버튼(즉시 확정) - 수정 저장 시 미확인 건은 확정 처리 - 가계부 헤더에 '확인 필요 N건' 카운트 배지 - accountApi: pendingCount/confirmEntry Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,26 @@ const entries = ref([])
|
||||
const summary = ref({ totalIncome: 0, totalExpense: 0, balance: 0 })
|
||||
const loading = ref(false)
|
||||
const error = ref(null)
|
||||
const pendingCount = ref(0) // 확인 필요(카드 알림 자동인식) 건수
|
||||
const editingPending = ref(false) // 수정 중인 항목이 미확인 건인지
|
||||
|
||||
async function loadPendingCount() {
|
||||
try {
|
||||
pendingCount.value = (await accountApi.pendingCount()).count || 0
|
||||
} catch {
|
||||
pendingCount.value = 0
|
||||
}
|
||||
}
|
||||
// 미확인 내역 즉시 확정(분류 미지정으로 수락)
|
||||
async function confirmRow(e) {
|
||||
try {
|
||||
await accountApi.confirmEntry(e.id, {})
|
||||
await load()
|
||||
await loadPendingCount()
|
||||
} catch (err) {
|
||||
alert(err.response?.data?.message || '확인 처리에 실패했습니다.')
|
||||
}
|
||||
}
|
||||
|
||||
// 검색·필터
|
||||
const filterOpen = ref(false)
|
||||
@@ -331,6 +351,7 @@ function todayStr() {
|
||||
|
||||
function openCreate() {
|
||||
editId.value = null
|
||||
editingPending.value = false
|
||||
Object.assign(form, { entryDate: todayStr(), type: 'EXPENSE', category: '', amount: null, memo: '', walletKind: '', walletId: '', toWalletKind: '', toWalletId: '', principal: null, interest: null, installmentMonths: '' })
|
||||
selectedTagIds.value = []
|
||||
cancelAddCategory()
|
||||
@@ -339,6 +360,7 @@ function openCreate() {
|
||||
}
|
||||
function openEdit(e) {
|
||||
editId.value = e.id
|
||||
editingPending.value = !!e.pending
|
||||
Object.assign(form, {
|
||||
entryDate: e.entryDate,
|
||||
type: e.type,
|
||||
@@ -428,10 +450,18 @@ async function submit() {
|
||||
tagIds: selectedTagIds.value,
|
||||
}
|
||||
try {
|
||||
if (editId.value) await accountApi.update(editId.value, payload)
|
||||
else await accountApi.create(payload)
|
||||
if (editId.value) {
|
||||
await accountApi.update(editId.value, payload)
|
||||
// 미확인(카드 알림) 건이면 수정 저장 후 확정 처리
|
||||
if (editingPending.value) {
|
||||
await accountApi.confirmEntry(editId.value, {})
|
||||
}
|
||||
} else {
|
||||
await accountApi.create(payload)
|
||||
}
|
||||
formOpen.value = false
|
||||
await load()
|
||||
await loadPendingCount()
|
||||
} catch (e) {
|
||||
formError.value = e.response?.data?.message || '저장에 실패했습니다.'
|
||||
} finally {
|
||||
@@ -460,13 +490,14 @@ onMounted(async () => {
|
||||
loadTagOptions()
|
||||
loadWallets()
|
||||
loadCategories()
|
||||
loadPendingCount()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="account">
|
||||
<header class="account-head">
|
||||
<h1>가계부</h1>
|
||||
<h1>가계부<span v-if="pendingCount" class="pending-count">확인 필요 {{ pendingCount }}건</span></h1>
|
||||
<IconBtn icon="plus" title="내역 추가" variant="primary" @click="openCreate" />
|
||||
</header>
|
||||
|
||||
@@ -535,17 +566,19 @@ onMounted(async () => {
|
||||
</span>
|
||||
</div>
|
||||
<ul class="day-items">
|
||||
<li v-for="e in g.items" :key="e.id" class="entry-item">
|
||||
<li v-for="e in g.items" :key="e.id" class="entry-item" :class="{ 'is-pending': e.pending }">
|
||||
<div class="ei-line1">
|
||||
<span class="ei-cat">
|
||||
<span class="type-dot" :class="dotClass(e.type)"></span>
|
||||
<template v-if="e.type === 'TRANSFER'">이체</template>
|
||||
<template v-else>{{ e.category || (e.type === 'INCOME' ? '수입' : '지출') }}</template>
|
||||
</span>
|
||||
<span v-if="e.pending" class="ei-pending">확인필요</span>
|
||||
<span v-if="e.type === 'TRANSFER'" class="ei-wallet">{{ e.walletName }} → {{ e.toWalletName }}</span>
|
||||
<span v-else-if="e.walletName" class="ei-wallet">{{ e.walletName }}</span>
|
||||
<span class="ei-amount" :class="amountClass(e.type)">{{ amountSign(e.type) }}{{ won(e.amount) }}</span>
|
||||
<span class="ei-act">
|
||||
<button v-if="e.pending" type="button" class="confirm-btn" title="확인(확정)" @click="confirmRow(e)">확인</button>
|
||||
<IconBtn icon="edit" title="수정" size="sm" @click="openEdit(e)" />
|
||||
<IconBtn icon="trash" title="삭제" variant="danger" size="sm" @click="remove(e)" />
|
||||
</span>
|
||||
@@ -876,6 +909,39 @@ button.primary {
|
||||
padding: 0.5rem 0.1rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
.entry-item.is-pending {
|
||||
background: rgba(230, 126, 34, 0.07);
|
||||
}
|
||||
.ei-pending {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
color: #e67e22;
|
||||
border: 1px solid #e67e22;
|
||||
border-radius: 3px;
|
||||
padding: 0.02rem 0.3rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.confirm-btn {
|
||||
padding: 0.18rem 0.5rem;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
border: 1px solid hsla(160, 100%, 37%, 1);
|
||||
border-radius: 4px;
|
||||
background: hsla(160, 100%, 37%, 1);
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pending-count {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
vertical-align: middle;
|
||||
color: #e67e22;
|
||||
border: 1px solid #e67e22;
|
||||
border-radius: 999px;
|
||||
padding: 0.1rem 0.5rem;
|
||||
}
|
||||
.ei-line1 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user