@@ -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 : 1 px 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.5 rem ;
font - size : 0.82 rem ;
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 : 1 rem ;
}
. ph - modal {
width : 100 % ;
max - width : 380 px ;
max - height : 75 vh ;
display : flex ;
flex - direction : column ;
background : var ( -- color - background ) ;
border : 1 px solid var ( -- color - border ) ;
border - radius : 10 px ;
overflow : hidden ;
}
. ph - head {
display : flex ;
align - items : center ;
justify - content : space - between ;
padding : 0.85 rem 1 rem ;
border - bottom : 1 px solid var ( -- color - border ) ;
}
. ph - close {
border : 0 ;
background : transparent ;
color : var ( -- color - text ) ;
font - size : 1.5 rem ;
line - height : 1 ;
cursor : pointer ;
}
. ph - msg {
padding : 2 rem 1 rem ;
text - align : center ;
opacity : 0.7 ;
}
. ph - list {
list - style : none ;
margin : 0 ;
padding : 0.25 rem 0 ;
overflow - y : auto ;
}
. ph - list li {
display : flex ;
align - items : center ;
justify - content : space - between ;
gap : 0.75 rem ;
padding : 0.65 rem 1 rem ;
border - bottom : 1 px solid var ( -- color - border ) ;
}
. ph - list li : last - child {
border - bottom : 0 ;
}
. ph - main {
display : flex ;
flex - direction : column ;
gap : 0.15 rem ;
}
. ph - reason {
font - size : 0.92 rem ;
}
. ph - date {
font - size : 0.78 rem ;
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.5 rem ;
font - size : 0.82 rem ;