- 포인트 행 클릭 → 적립/차감 내역 모달(사유·일시·증감) - authApi.pointHistory(), 사유 라벨(BOARD_WRITE=게시판 작성) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,33 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
// 포인트 적립내역 모달
|
||||
const POINT_REASONS = { BOARD_WRITE: '게시판 작성' }
|
||||
function reasonLabel(r) {
|
||||
return POINT_REASONS[r] || r
|
||||
}
|
||||
const historyOpen = ref(false)
|
||||
const history = ref([])
|
||||
const historyLoading = ref(false)
|
||||
async function openHistory() {
|
||||
historyOpen.value = true
|
||||
historyLoading.value = true
|
||||
try {
|
||||
history.value = await authApi.pointHistory()
|
||||
} catch {
|
||||
history.value = []
|
||||
} finally {
|
||||
historyLoading.value = false
|
||||
}
|
||||
}
|
||||
function fmtDateTime(s) {
|
||||
if (!s) return ''
|
||||
const d = new Date(s)
|
||||
if (Number.isNaN(d.getTime())) return s
|
||||
const p = (n) => String(n).padStart(2, '0')
|
||||
return `${d.getFullYear()}.${p(d.getMonth() + 1)}.${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`
|
||||
}
|
||||
|
||||
const u = computed(() => auth.user || {})
|
||||
const roleLabel = computed(() => (u.value.role === 'ADMIN' ? '관리자' : '일반회원'))
|
||||
const isPremium = computed(() => auth.isPremium)
|
||||
@@ -128,12 +155,38 @@ function goEdit() {
|
||||
<RouterLink v-if="!isPremium" to="/upgrade" class="up-link">업그레이드 →</RouterLink>
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<button type="button" class="row row-point" @click="openHistory">
|
||||
<span class="k">포인트</span>
|
||||
<span class="v"><span class="points">{{ points.toLocaleString('ko-KR') }} P</span></span>
|
||||
</div>
|
||||
<span class="v">
|
||||
<span class="points">{{ points.toLocaleString('ko-KR') }} P</span>
|
||||
<span class="point-more">내역 ›</span>
|
||||
</span>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<!-- 포인트 적립내역 모달 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="historyOpen" class="ph-backdrop" @click.self="historyOpen = false">
|
||||
<div class="ph-modal" role="dialog" aria-modal="true" aria-label="포인트 적립내역">
|
||||
<div class="ph-head">
|
||||
<strong>포인트 적립내역</strong>
|
||||
<button type="button" class="ph-close" aria-label="닫기" @click="historyOpen = false">×</button>
|
||||
</div>
|
||||
<p v-if="historyLoading" class="ph-msg">불러오는 중…</p>
|
||||
<p v-else-if="!history.length" class="ph-msg">적립 내역이 없습니다.</p>
|
||||
<ul v-else class="ph-list">
|
||||
<li v-for="(h, i) in history" :key="i">
|
||||
<div class="ph-main">
|
||||
<span class="ph-reason">{{ reasonLabel(h.reason) }}</span>
|
||||
<span class="ph-date">{{ fmtDateTime(h.createdAt) }}</span>
|
||||
</div>
|
||||
<span class="ph-amount" :class="{ minus: h.amount < 0 }">{{ h.amount > 0 ? '+' : '' }}{{ h.amount }} P</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
|
||||
<button type="button" class="primary-btn" @click="goEdit">이름 변경</button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -201,6 +254,107 @@ function goEdit() {
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
font-weight: 700;
|
||||
}
|
||||
.row-point {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
}
|
||||
.row-point:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.row-point:hover {
|
||||
background: var(--color-background-mute);
|
||||
}
|
||||
.point-more {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.82rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
opacity: 0.55;
|
||||
}
|
||||
/* 포인트 내역 모달 */
|
||||
.ph-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 2000;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
.ph-modal {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
max-height: 75vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--color-background);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ph-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.85rem 1rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
.ph-close {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
color: var(--color-text);
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ph-msg {
|
||||
padding: 2rem 1rem;
|
||||
text-align: center;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.ph-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.25rem 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ph-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 0.65rem 1rem;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
.ph-list li:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ph-main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
.ph-reason {
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
.ph-date {
|
||||
font-size: 0.78rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.ph-amount {
|
||||
font-weight: 700;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ph-amount.minus {
|
||||
color: #c0392b;
|
||||
}
|
||||
.up-link {
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.82rem;
|
||||
|
||||
Reference in New Issue
Block a user