diff --git a/src/api/authApi.js b/src/api/authApi.js index 706ec66..84e2e85 100644 --- a/src/api/authApi.js +++ b/src/api/authApi.js @@ -43,6 +43,10 @@ export const authApi = { points() { return http.get('/auth/points') }, + // 포인트 적립/차감 내역 (최신순) + pointHistory() { + return http.get('/auth/point-history') + }, // 현재 사용자 전체 프로필(아바타·포인트 포함) — 재로그인 없이 최신값 동기화 profile() { return http.get('/auth/profile') diff --git a/src/views/settings/AccountInfoView.vue b/src/views/settings/AccountInfoView.vue index ec0bb8f..c5540b1 100644 --- a/src/views/settings/AccountInfoView.vue +++ b/src/views/settings/AccountInfoView.vue @@ -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() { 업그레이드 → -
+
+ + {{ points.toLocaleString('ko-KR') }} P + 내역 › + + + + +
+ +
+
+ @@ -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;