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,
|
.rail-item,
|
||||||
.board-login-btn,
|
.board-login-btn,
|
||||||
.board-load-more,
|
.board-load-more,
|
||||||
|
.mode-toggle-btn,
|
||||||
.post-detail-close,
|
.post-detail-close,
|
||||||
.terminal,
|
.terminal,
|
||||||
.terminal span {
|
.terminal span {
|
||||||
@@ -260,6 +261,19 @@ iframe { width: 100%; height: 180px; border-radius: 4px; }
|
|||||||
.contact-form { line-height: 2; color: var(--tui-dim);}
|
.contact-form { line-height: 2; color: var(--tui-dim);}
|
||||||
|
|
||||||
/* --- 게시판 (BOARD) --- */
|
/* --- 게시판 (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-layout { display: grid; grid-template-columns: 168px 1fr; gap: 18px; align-items: start; }
|
||||||
.board-rail { display: flex; flex-direction: column; gap: 6px; }
|
.board-rail { display: flex; flex-direction: column; gap: 6px; }
|
||||||
.rail-item {
|
.rail-item {
|
||||||
|
|||||||
+36
-3
@@ -112,7 +112,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="activeMenu === 'board'" class="sub-page board-page" key="board">
|
<div v-else-if="activeMenu === 'board'" class="sub-page board-page" key="board">
|
||||||
|
<div class="board-page-head">
|
||||||
<h2 class="page-title">> EXECUTING COMMUNITY_FEED.EXE</h2>
|
<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-layout">
|
||||||
<div class="board-rail">
|
<div class="board-rail">
|
||||||
@@ -141,7 +159,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else-if="board.loading && board.posts.length === 0" class="board-status">SYNCING BOARD DATA...</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.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>
|
<template v-else>
|
||||||
<button
|
<button
|
||||||
v-for="post in board.posts"
|
v-for="post in board.posts"
|
||||||
@@ -167,6 +187,7 @@
|
|||||||
<span v-if="post.createdAt">{{ post.createdAt }}</span>
|
<span v-if="post.createdAt">{{ post.createdAt }}</span>
|
||||||
<span v-if="post.views !== null">조회 {{ post.views }}</span>
|
<span v-if="post.views !== null">조회 {{ post.views }}</span>
|
||||||
<span v-if="post.likes !== null">추천 {{ post.likes }}</span>
|
<span v-if="post.likes !== null">추천 {{ post.likes }}</span>
|
||||||
|
<span v-if="boardViewMode === 'viewed' && post.viewedAt">읽음 {{ post.viewedAt }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</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 board = ref({ loading: false, error: null, loaded: false, posts: [], page: 0, hasMore: false });
|
||||||
const boardSources = ref([]);
|
const boardSources = ref([]);
|
||||||
const activeBoardKey = ref('');
|
const activeBoardKey = ref('');
|
||||||
|
const boardViewMode = ref('unread'); // 'unread' | 'viewed'
|
||||||
const postDetail = ref({ show: false, post: null });
|
const postDetail = ref({ show: false, post: null });
|
||||||
|
|
||||||
const boardAuthHeaders = () => ({ Authorization: `Bearer ${authToken.value}` });
|
const boardAuthHeaders = () => ({ Authorization: `Bearer ${authToken.value}` });
|
||||||
@@ -464,7 +486,8 @@ const mapScrapedPost = (row) => ({
|
|||||||
views: row.viewCount ?? null,
|
views: row.viewCount ?? null,
|
||||||
likes: row.likeCount ?? null,
|
likes: row.likeCount ?? null,
|
||||||
content: row.content || '',
|
content: row.content || '',
|
||||||
images: Array.isArray(row.imageUrls) ? row.imageUrls : []
|
images: Array.isArray(row.imageUrls) ? row.imageUrls : [],
|
||||||
|
viewedAt: row.viewedAt || null
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchBoardMeta = async () => {
|
const fetchBoardMeta = async () => {
|
||||||
@@ -489,7 +512,8 @@ const fetchBoardPosts = async (reset) => {
|
|||||||
board.value.error = null;
|
board.value.error = null;
|
||||||
try {
|
try {
|
||||||
const apiBaseUrl = import.meta.env.VITE_API_URL || '';
|
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: {
|
params: {
|
||||||
board: activeBoardKey.value || undefined,
|
board: activeBoardKey.value || undefined,
|
||||||
page: board.value.page,
|
page: board.value.page,
|
||||||
@@ -517,6 +541,12 @@ const selectBoardSource = (boardKey) => {
|
|||||||
fetchBoardPosts(true);
|
fetchBoardPosts(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setBoardViewMode = (mode) => {
|
||||||
|
if (boardViewMode.value === mode) return;
|
||||||
|
boardViewMode.value = mode;
|
||||||
|
fetchBoardPosts(true);
|
||||||
|
};
|
||||||
|
|
||||||
const loadMoreBoardPosts = () => {
|
const loadMoreBoardPosts = () => {
|
||||||
board.value.page += 1;
|
board.value.page += 1;
|
||||||
fetchBoardPosts(false);
|
fetchBoardPosts(false);
|
||||||
@@ -525,6 +555,9 @@ const loadMoreBoardPosts = () => {
|
|||||||
const openPostDetail = async (post) => {
|
const openPostDetail = async (post) => {
|
||||||
postDetail.value = { show: true, post };
|
postDetail.value = { show: true, post };
|
||||||
|
|
||||||
|
// '읽음' 목록에서는 조회 처리를 다시 할 필요도, 목록에서 뺄 필요도 없다.
|
||||||
|
if (boardViewMode.value === 'viewed') return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const apiBaseUrl = import.meta.env.VITE_API_URL || '';
|
const apiBaseUrl = import.meta.env.VITE_API_URL || '';
|
||||||
await axios.post(`${apiBaseUrl}/api/scraped-posts/${post.id}/view`, null, {
|
await axios.post(`${apiBaseUrl}/api/scraped-posts/${post.id}/view`, null, {
|
||||||
|
|||||||
Reference in New Issue
Block a user