@@ -58,6 +58,13 @@ export const boardApi = {
|
|||||||
recommenders(id) {
|
recommenders(id) {
|
||||||
return http.get(`/board/posts/${id}/recommenders`)
|
return http.get(`/board/posts/${id}/recommenders`)
|
||||||
},
|
},
|
||||||
|
// 신고 (누적 5건 시 블라인드)
|
||||||
|
reportPost(id) {
|
||||||
|
return http.post(`/board/posts/${id}/report`)
|
||||||
|
},
|
||||||
|
reportComment(commentId) {
|
||||||
|
return http.post(`/board/comments/${commentId}/report`)
|
||||||
|
},
|
||||||
block(id, reason) {
|
block(id, reason) {
|
||||||
return http.post(`/board/posts/${id}/block`, { reason })
|
return http.post(`/board/posts/${id}/block`, { reason })
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -126,6 +126,28 @@ async function removeComment(c) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 신고 =====
|
||||||
|
async function reportPost() {
|
||||||
|
if (!(await dialog.confirm('이 게시글을 신고하시겠어요?\n신고가 누적되면 자동으로 블라인드 처리됩니다.', { title: '게시글 신고', danger: true }))) return
|
||||||
|
try {
|
||||||
|
await boardApi.reportPost(postId)
|
||||||
|
await dialog.alert('신고가 접수되었습니다.')
|
||||||
|
await load()
|
||||||
|
} catch (e) {
|
||||||
|
alert(e.response?.data?.message || '신고에 실패했습니다.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function reportComment(c) {
|
||||||
|
if (!(await dialog.confirm('이 댓글을 신고하시겠어요?\n신고가 누적되면 자동으로 블라인드 처리됩니다.', { title: '댓글 신고', danger: true }))) return
|
||||||
|
try {
|
||||||
|
await boardApi.reportComment(c.id)
|
||||||
|
await dialog.alert('신고가 접수되었습니다.')
|
||||||
|
await refreshComments()
|
||||||
|
} catch (e) {
|
||||||
|
alert(e.response?.data?.message || '신고에 실패했습니다.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 댓글 목록만 새로고침 (다른 사람이 단 댓글·추천 반영)
|
// 댓글 목록만 새로고침 (다른 사람이 단 댓글·추천 반영)
|
||||||
const refreshingComments = ref(false)
|
const refreshingComments = ref(false)
|
||||||
async function refreshComments() {
|
async function refreshComments() {
|
||||||
@@ -278,6 +300,11 @@ onMounted(load)
|
|||||||
<div class="actions">
|
<div class="actions">
|
||||||
<IconBtn icon="list" title="목록" @click="router.push({ path: `/board/${category}`, query: route.query })" />
|
<IconBtn icon="list" title="목록" @click="router.push({ path: `/board/${category}`, query: route.query })" />
|
||||||
<div class="right-actions">
|
<div class="right-actions">
|
||||||
|
<!-- 신고 (작성자·관리자 외) -->
|
||||||
|
<button
|
||||||
|
v-if="!isAdmin && !restricted && auth.user && post.authorId !== auth.user.id"
|
||||||
|
type="button" class="report-btn" @click="reportPost"
|
||||||
|
>🚩 신고</button>
|
||||||
<!-- 관리자 제한/해제 -->
|
<!-- 관리자 제한/해제 -->
|
||||||
<button v-if="isAdmin && !post.blocked" type="button" class="warn" @click="blockPost">열람 제한</button>
|
<button v-if="isAdmin && !post.blocked" type="button" class="warn" @click="blockPost">열람 제한</button>
|
||||||
<button v-if="isAdmin && post.blocked" type="button" class="warn" @click="unblockPost">제한 해제</button>
|
<button v-if="isAdmin && post.blocked" type="button" class="warn" @click="unblockPost">제한 해제</button>
|
||||||
@@ -305,13 +332,18 @@ onMounted(load)
|
|||||||
<strong>{{ c.authorName }}</strong>
|
<strong>{{ c.authorName }}</strong>
|
||||||
<span v-if="isAuthorComment(c)" class="author-badge">작성자</span>
|
<span v-if="isAuthorComment(c)" class="author-badge">작성자</span>
|
||||||
<span v-if="c.hot" class="hot-badge">🔥 인기</span>
|
<span v-if="c.hot" class="hot-badge">🔥 인기</span>
|
||||||
|
<span v-if="c.blocked && isAdmin" class="report-badge">🚩 신고 블라인드</span>
|
||||||
<span class="comment-date">{{ formatRelative(c.createdAt) }}</span>
|
<span class="comment-date">{{ formatRelative(c.createdAt) }}</span>
|
||||||
<span v-if="canModifyComment(c)" class="comment-del">
|
<span v-if="canModifyComment(c)" class="comment-del">
|
||||||
<IconBtn icon="trash" title="댓글 삭제" variant="danger" size="sm" @click="removeComment(c)" />
|
<IconBtn icon="trash" title="댓글 삭제" variant="danger" size="sm" @click="removeComment(c)" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 신고 누적 블라인드 (관리자만 본문 열람, 그 외엔 가림) -->
|
||||||
|
<div v-if="c.blocked && !isAdmin" class="blinded comment-blinded">
|
||||||
|
🚩 신고 누적으로 블라인드된 댓글입니다.
|
||||||
|
</div>
|
||||||
<!-- 비추천 많은 댓글 블라인드 (작성자·관리자는 정상) -->
|
<!-- 비추천 많은 댓글 블라인드 (작성자·관리자는 정상) -->
|
||||||
<div v-if="isBlinded(c) && !isMyComment(c) && !isAdmin && !revealedComments[c.id]" class="blinded comment-blinded">
|
<div v-else-if="isBlinded(c) && !isMyComment(c) && !isAdmin && !revealedComments[c.id]" class="blinded comment-blinded">
|
||||||
🙈 비추천이 많아 블라인드된 댓글입니다.
|
🙈 비추천이 많아 블라인드된 댓글입니다.
|
||||||
<button type="button" class="reveal-btn" @click="toggleReveal(c)">그래도 보기</button>
|
<button type="button" class="reveal-btn" @click="toggleReveal(c)">그래도 보기</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -330,6 +362,10 @@ onMounted(load)
|
|||||||
:title="isMyComment(c) ? '본인 댓글은 비추천할 수 없습니다' : ''"
|
:title="isMyComment(c) ? '본인 댓글은 비추천할 수 없습니다' : ''"
|
||||||
@click="voteComment(c, 'DOWN')"
|
@click="voteComment(c, 'DOWN')"
|
||||||
>👎 {{ c.downCount || 0 }}</button>
|
>👎 {{ c.downCount || 0 }}</button>
|
||||||
|
<button
|
||||||
|
v-if="!isAdmin && auth.user && c.authorId !== auth.user.id"
|
||||||
|
type="button" class="creport" @click="reportComment(c)"
|
||||||
|
>🚩 신고</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
@@ -605,6 +641,27 @@ button.notice-btn {
|
|||||||
font-size: 0.68rem;
|
font-size: 0.68rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
.report-badge {
|
||||||
|
padding: 0.05rem 0.35rem;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #c0392b;
|
||||||
|
color: #c0392b;
|
||||||
|
font-size: 0.68rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.report-btn {
|
||||||
|
padding: 0.45rem 0.9rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #c0392b;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.creport {
|
||||||
|
padding: 0.2rem 0.6rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
color: #c0392b;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
.comment-votes {
|
.comment-votes {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user