mirror of
https://github.com/headporter81/specialsource-homepage-frontend.git
synced 2026-10-07 15:21:10 +09:00
This commit is contained in:
@@ -26,6 +26,7 @@ body, html { margin: 0; padding: 0; width: 100%; height: 100%; background-color:
|
||||
.rail-item,
|
||||
.board-login-btn,
|
||||
.board-load-more,
|
||||
.mode-toggle-btn,
|
||||
.post-detail-close,
|
||||
.terminal,
|
||||
.terminal span {
|
||||
@@ -260,6 +261,19 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
|
||||
.contact-form { line-height: 2; color: var(--tui-dim);}
|
||||
|
||||
/* --- 게시판 (BOARD) --- */
|
||||
.board-page-head {
|
||||
display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;
|
||||
border-bottom: 1px solid var(--tui-border); padding-bottom: 10px; margin-bottom: 20px;
|
||||
}
|
||||
.board-page-head .page-title { border-bottom: none; padding-bottom: 0; margin-bottom: 0; }
|
||||
.board-mode-toggle { display: flex; border: 1px solid var(--tui-border); }
|
||||
.mode-toggle-btn {
|
||||
background: none; border: none; color: var(--tui-dim); font-size: 12px;
|
||||
padding: 6px 14px; cursor: pointer;
|
||||
}
|
||||
.mode-toggle-btn.active { color: #000; background: var(--tui-color); font-weight: bold; }
|
||||
.mode-toggle-btn:not(.active):hover { color: var(--tui-color); }
|
||||
|
||||
.board-layout { display: grid; grid-template-columns: 168px 1fr; gap: 18px; align-items: start; }
|
||||
.board-rail { display: flex; flex-direction: column; gap: 6px; }
|
||||
.rail-item {
|
||||
|
||||
+37
-4
@@ -112,7 +112,25 @@
|
||||
</div>
|
||||
|
||||
<div v-else-if="activeMenu === 'board'" class="sub-page board-page" key="board">
|
||||
<h2 class="page-title">> EXECUTING COMMUNITY_FEED.EXE</h2>
|
||||
<div class="board-page-head">
|
||||
<h2 class="page-title">> EXECUTING COMMUNITY_FEED.EXE</h2>
|
||||
<div class="board-mode-toggle">
|
||||
<button
|
||||
class="mode-toggle-btn"
|
||||
:class="{ active: boardViewMode === 'unread' }"
|
||||
@click="setBoardViewMode('unread')"
|
||||
>
|
||||
안 읽음
|
||||
</button>
|
||||
<button
|
||||
class="mode-toggle-btn"
|
||||
:class="{ active: boardViewMode === 'viewed' }"
|
||||
@click="setBoardViewMode('viewed')"
|
||||
>
|
||||
읽음
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="board-layout">
|
||||
<div class="board-rail">
|
||||
@@ -141,7 +159,9 @@
|
||||
</div>
|
||||
<div v-else-if="board.loading && board.posts.length === 0" class="board-status">SYNCING BOARD DATA...</div>
|
||||
<div v-else-if="board.error" class="board-status">FAILED TO LOAD BOARD DATA.</div>
|
||||
<div v-else-if="board.posts.length === 0" class="board-status">NO POSTS FOUND.</div>
|
||||
<div v-else-if="board.posts.length === 0" class="board-status">
|
||||
{{ boardViewMode === 'viewed' ? '아직 읽은 게시물이 없습니다.' : 'NO POSTS FOUND.' }}
|
||||
</div>
|
||||
<template v-else>
|
||||
<button
|
||||
v-for="post in board.posts"
|
||||
@@ -167,6 +187,7 @@
|
||||
<span v-if="post.createdAt">{{ post.createdAt }}</span>
|
||||
<span v-if="post.views !== null">조회 {{ post.views }}</span>
|
||||
<span v-if="post.likes !== null">추천 {{ post.likes }}</span>
|
||||
<span v-if="boardViewMode === 'viewed' && post.viewedAt">읽음 {{ post.viewedAt }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -449,6 +470,7 @@ const BOARD_PAGE_SIZE = 20;
|
||||
const board = ref({ loading: false, error: null, loaded: false, posts: [], page: 0, hasMore: false });
|
||||
const boardSources = ref([]);
|
||||
const activeBoardKey = ref('');
|
||||
const boardViewMode = ref('unread'); // 'unread' | 'viewed'
|
||||
const postDetail = ref({ show: false, post: null });
|
||||
|
||||
const boardAuthHeaders = () => ({ Authorization: `Bearer ${authToken.value}` });
|
||||
@@ -464,7 +486,8 @@ const mapScrapedPost = (row) => ({
|
||||
views: row.viewCount ?? null,
|
||||
likes: row.likeCount ?? null,
|
||||
content: row.content || '',
|
||||
images: Array.isArray(row.imageUrls) ? row.imageUrls : []
|
||||
images: Array.isArray(row.imageUrls) ? row.imageUrls : [],
|
||||
viewedAt: row.viewedAt || null
|
||||
});
|
||||
|
||||
const fetchBoardMeta = async () => {
|
||||
@@ -489,7 +512,8 @@ const fetchBoardPosts = async (reset) => {
|
||||
board.value.error = null;
|
||||
try {
|
||||
const apiBaseUrl = import.meta.env.VITE_API_URL || '';
|
||||
const res = await axios.get(`${apiBaseUrl}/api/scraped-posts`, {
|
||||
const endpoint = boardViewMode.value === 'viewed' ? '/api/scraped-posts/viewed' : '/api/scraped-posts';
|
||||
const res = await axios.get(`${apiBaseUrl}${endpoint}`, {
|
||||
params: {
|
||||
board: activeBoardKey.value || undefined,
|
||||
page: board.value.page,
|
||||
@@ -517,6 +541,12 @@ const selectBoardSource = (boardKey) => {
|
||||
fetchBoardPosts(true);
|
||||
};
|
||||
|
||||
const setBoardViewMode = (mode) => {
|
||||
if (boardViewMode.value === mode) return;
|
||||
boardViewMode.value = mode;
|
||||
fetchBoardPosts(true);
|
||||
};
|
||||
|
||||
const loadMoreBoardPosts = () => {
|
||||
board.value.page += 1;
|
||||
fetchBoardPosts(false);
|
||||
@@ -525,6 +555,9 @@ const loadMoreBoardPosts = () => {
|
||||
const openPostDetail = async (post) => {
|
||||
postDetail.value = { show: true, post };
|
||||
|
||||
// '읽음' 목록에서는 조회 처리를 다시 할 필요도, 목록에서 뺄 필요도 없다.
|
||||
if (boardViewMode.value === 'viewed') return;
|
||||
|
||||
try {
|
||||
const apiBaseUrl = import.meta.env.VITE_API_URL || '';
|
||||
await axios.post(`${apiBaseUrl}/api/scraped-posts/${post.id}/view`, null, {
|
||||
|
||||
Reference in New Issue
Block a user