메인보드

This commit is contained in:
dyhj625 2025-04-01 10:52:24 +09:00
parent 0e901ccaa0
commit 675eb93587

View File

@ -1,267 +1,235 @@
<template> <template>
<div class="row" style="gap: 20px;"> <div class="row" style="gap: 20px;">
<!-- 공지사항 카드 --> <div class="col-md-6 col-lg-4 col-xl-4 order-0 mb-6">
<div class="col-md-6 col-lg-4 col-xl-4 order-0 mb-6"> <div class="card text-center h-100">
<div class="card text-center h-100"> <div class="modal-content">
<div class="card-header d-flex justify-content-between align-items-center"> <!-- 모달 본문 -->
<h3 class="mb-0">공지사항</h3> <div class="modal-bod mt-3">
<router-link :to="{ name: 'BoardList', query: { type: 'notices' } }" class="btn btn-sm btn-primary"> <!-- 버튼 영역 -->
더보기 <div class="btn-group mb-3" role="group">
</router-link> <button
</div> type="button"
<div class="card-body p-0"> class="btn"
<table class="table mb-0"> :class="selectedBoard === 'notices' ? 'btn-primary' : 'btn-outline-primary'"
<thead> @click="changeBoard('notices')"
>
공지사항
</button>
<button
type="button"
class="btn"
:class="selectedBoard === 'general' ? 'btn-primary' : 'btn-outline-primary'"
@click="changeBoard('general')"
>
자유게시판
</button>
<button
type="button"
class="btn"
:class="selectedBoard === 'anonymous' ? 'btn-primary' : 'btn-outline-primary'"
@click="changeBoard('anonymous')"
>
익명게시판
</button>
</div>
<!-- 게시글 미리보기 테이블 -->
<table class="table">
<thead>
<tr> <tr>
<th class="text-center" style="width: 15%;">번호</th> <!-- 익명게시판은 'nickname', 나머지는 'writer' -->
<th class="text-center" style="width: 55%;">제목</th> <th class="text-center" style="width: 20%;">
<th class="text-center" style="width: 15%;">작성일</th> {{ selectedBoard === 'anonymous' ? 'nickname' : 'writer' }}
<th class="text-center" style="width: 15%;">조회수</th> </th>
<!-- 제목 헤더는 왼쪽 정렬 -->
<th class="text-start" style="width: 65%;">title</th>
<th class="text-center" style="width: 15%;">views</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr <tr
v-for="(post, index) in noticeList" v-for="post in currentList"
:key="post.id" :key="post.id"
@click="goDetail(post.id, 'notices')" style="cursor: pointer;"
style="cursor: pointer;" @click="goDetail(post.id, selectedBoard)"
> >
<td class="text-center">{{ index + 1 }}</td> <td class="text-center">
<td> {{ selectedBoard === 'anonymous' ? post.nickname : post.author }}
{{ post.title }} </td>
<span v-if="post.commentCount" class="text-danger ml-1">[{{ post.commentCount }}]</span> <td class="text-start">
<span v-if="isNewPost(post.rawDate)" class="badge bg-warning text-dark ml-1">N</span> <div>
</td> {{ truncateTitle(post.title) }}
<td class="text-center">{{ post.date }}</td> <span v-if="post.commentCount" class="text-danger ml-1">
<td class="text-center">{{ post.views }}</td> [{{ post.commentCount }}]
</span>
<i v-if="post.img" class="bi bi-image mx-1"></i>
<i
v-if="post.hasAttachment.length > 0"
class="bi bi-paperclip ml-1"
></i>
</div>
<div class="text-muted small">
{{ post.date }}
</div>
</td>
<td class="text-center">{{ post.views }}</td>
</tr> </tr>
<tr v-if="noticeList.length === 0"> <tr v-if="currentList.length === 0">
<td colspan="4" class="text-center text-muted">게시물이 없습니다.</td> <td colspan="3" class="text-center text-muted">게시물이 없습니다.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div>
<div class="card-footer d-flex justify-content-end">
<router-link :to="{ name: 'BoardList', query: { type: 'notices' } }" class="btn btn-sm btn-primary">
더보기
</router-link>
</div>
</div> </div>
</div> <!-- 모달 푸터: 더보기 버튼 오른쪽 정렬 -->
<div class="modal-foote d-flex">
<!-- 자유게시판 카드 --> <router-link
<div class="col-md-6 col-lg-4 col-xl-4 order-0 mb-6"> :to="{ name: 'BoardList', query: { type: selectedBoard } }"
<div class="card text-center h-100"> class="btn btn-primary ms-auto"
<div class="card-header d-flex justify-content-between align-items-center"> >
<h3 class="mb-0">자유게시판</h3> more
<router-link :to="{ name: 'BoardList', query: { type: 'general' } }" class="btn btn-sm btn-primary">
더보기
</router-link> </router-link>
</div>
<div class="card-body p-0">
<table class="table mb-0">
<thead>
<tr>
<th class="text-center" style="width: 15%;">번호</th>
<th class="text-center" style="width: 55%;">제목</th>
<th class="text-center" style="width: 15%;">작성일</th>
<th class="text-center" style="width: 15%;">조회수</th>
</tr>
</thead>
<tbody>
<tr
v-for="(post, index) in freeList"
:key="post.id"
@click="goDetail(post.id, 'general')"
style="cursor: pointer;"
>
<td class="text-center">{{ index + 1 }}</td>
<td>
{{ post.title }}
<span v-if="post.commentCount" class="text-danger ml-1">[{{ post.commentCount }}]</span>
<span v-if="isNewPost(post.rawDate)" class="badge bg-warning text-dark ml-1">N</span>
</td>
<td class="text-center">{{ post.date }}</td>
<td class="text-center">{{ post.views }}</td>
</tr>
<tr v-if="freeList.length === 0">
<td colspan="4" class="text-center text-muted">게시물이 없습니다.</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer d-flex justify-content-end">
<router-link :to="{ name: 'BoardList', query: { type: 'general' } }" class="btn btn-sm btn-primary">
더보기
</router-link>
</div>
</div> </div>
</div>
<!-- 익명게시판 카드 -->
<div class="col-md-6 col-lg-4 col-xl-4 order-0 mb-6">
<div class="card text-center h-100">
<div class="card-header d-flex justify-content-between align-items-center">
<h3 class="mb-0">익명게시판</h3>
<router-link :to="{ name: 'BoardList', query: { type: 'anonymous' } }" class="btn btn-sm btn-primary">
더보기
</router-link>
</div>
<div class="card-body p-0">
<table class="table mb-0">
<thead>
<tr>
<th class="text-center" style="width: 15%;">번호</th>
<th class="text-center" style="width: 55%;">제목</th>
<th class="text-center" style="width: 15%;">작성일</th>
<th class="text-center" style="width: 15%;">조회수</th>
</tr>
</thead>
<tbody>
<tr
v-for="(post, index) in anonymousList"
:key="post.id"
@click="goDetail(post.id, 'anonymous')"
style="cursor: pointer;"
>
<td class="text-center">{{ index + 1 }}</td>
<td>
{{ post.title }}
<span v-if="post.commentCount" class="text-danger ml-1">[{{ post.commentCount }}]</span>
<span v-if="isNewPost(post.rawDate)" class="badge bg-warning text-dark ml-1">N</span>
</td>
<td class="text-center">{{ post.date }}</td>
<td class="text-center">{{ post.views }}</td>
</tr>
<tr v-if="anonymousList.length === 0">
<td colspan="4" class="text-center text-muted">게시물이 없습니다.</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer d-flex justify-content-end">
<router-link :to="{ name: 'BoardList', query: { type: 'anonymous' } }" class="btn btn-sm btn-primary">
더보기
</router-link>
</div>
</div> </div>
</div>
</div> </div>
</template> </div>
</div>
</template>
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import axios from '@api'; import axios from '@api';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import isToday from 'dayjs/plugin/isToday'; import isToday from 'dayjs/plugin/isToday';
import isYesterday from 'dayjs/plugin/isYesterday'; import isYesterday from 'dayjs/plugin/isYesterday';
import 'bootstrap-icons/font/bootstrap-icons.css';
dayjs.extend(isToday); dayjs.extend(isToday);
dayjs.extend(isYesterday); dayjs.extend(isYesterday);
const router = useRouter(); const router = useRouter();
// // true
const noticeList = ref([]); const isModalOpen = ref(true);
const freeList = ref([]);
const anonymousList = ref([]);
// : HH:mm, YYYY-MM-DD // : 'notices', 'general', 'anonymous'
const formatDate = dateString => { const selectedBoard = ref('notices');
const date = dayjs(dateString);
return date.isToday() ? date.format('HH:mm') : date.format('YYYY-MM-DD');
};
// : //
const isNewPost = dateString => { const noticeList = ref([]);
const date = dayjs(dateString); const freeList = ref([]);
return date.isToday() || date.isYesterday(); const anonymousList = ref([]);
};
// ( 5) // computed
const fetchNoticePosts = async () => { const currentList = computed(() => {
try { if (selectedBoard.value === 'notices') return noticeList.value;
const { data } = await axios.get('board/notices', { params: { size: 5 } }); if (selectedBoard.value === 'general') return freeList.value;
if (data?.data) { if (selectedBoard.value === 'anonymous') return anonymousList.value;
noticeList.value = data.data.map(post => ({ return [];
id: post.id, });
title: post.title,
date: formatDate(post.date), // : HH:mm, YYYY-MM-DD
rawDate: post.date, const formatDate = dateString => {
views: post.cnt || 0, const date = dayjs(dateString);
commentCount: post.commentCount, return date.isToday() ? date.format('HH:mm') : date.format('YYYY-MM-DD');
hasAttachment: post.hasAttachment, };
img: post.firstImageUrl || null,
})); // 14 ...
} const truncateTitle = title => {
} catch (error) { return title.length > 10 ? title.slice(0, 10) + '...' : title;
console.error(error); };
// ( 5)
const fetchNoticePosts = async () => {
try {
const { data } = await axios.get('board/notices', { params: { size: 5 } });
if (data?.data) {
noticeList.value = data.data.map(post => ({
id: post.id,
title: post.title,
date: formatDate(post.date),
rawDate: post.date,
views: post.cnt || 0,
commentCount: post.commentCount,
img: post.firstImageUrl,
author: post.author || '관리자',
nickname: post.nickname || '관리자',
hasAttachment: post.hasAttachment, //
}));
} }
}; } catch (error) {
console.error(error);
}
};
// board/general ( 10 5) // board/general ( 10 5)
const fetchGeneralPosts = async () => { const fetchGeneralPosts = async () => {
try { try {
const { data } = await axios.get('board/general', { params: { size: 10 } }); const { data } = await axios.get('board/general', { params: { size: 10 } });
if (data?.data && data.data.list) { console.log(data.data.list)
const freePosts = []; if (data?.data && data.data.list) {
const anonymousPosts = []; const freePosts = [];
data.data.list.forEach(post => { const anonymousPosts = [];
// , data.data.list.forEach(post => {
if (post.nickname) { if (post.nickname) {
anonymousPosts.push({ //
id: post.id, anonymousPosts.push({
title: post.title, id: post.id,
date: formatDate(post.date), title: post.title,
rawDate: post.date, date: formatDate(post.date),
views: post.cnt || 0, img: post.firstImageUrl,
commentCount: post.commentCount, rawDate: post.date,
hasAttachment: post.hasAttachment, views: post.cnt || 0,
img: post.firstImageUrl || null, commentCount: post.commentCount,
}); nickname: post.nickname,
} else { hasAttachment: post.hasAttachment, //
freePosts.push({
id: post.id,
title: post.title,
date: formatDate(post.date),
rawDate: post.date,
views: post.cnt || 0,
commentCount: post.commentCount,
hasAttachment: post.hasAttachment,
img: post.firstImageUrl || null,
author: post.author || '익명',
});
}
}); });
freeList.value = freePosts.slice(0, 5); } else {
anonymousList.value = anonymousPosts.slice(0, 5); //
} freePosts.push({
} catch (error) { id: post.id,
console.error(error); title: post.title,
date: formatDate(post.date),
rawDate: post.date,
views: post.cnt || 0,
img: post.firstImageUrl,
commentCount: post.commentCount,
author: post.author || '익명',
hasAttachment: post.hasAttachment, //
});
}
});
freeList.value = freePosts.slice(0, 5);
anonymousList.value = anonymousPosts.slice(0, 5);
} }
}; } catch (error) {
console.error(error);
}
};
// ( ) //
const goDetail = (id, boardType) => { const changeBoard = type => {
router.push({ name: 'BoardDetail', params: { id, type: boardType } }); selectedBoard.value = type;
}; };
onMounted(() => { // ( )
fetchNoticePosts(); const goDetail = (id, boardType) => {
fetchGeneralPosts(); router.push({ name: 'BoardDetail', params: { id, type: boardType } });
}); };
</script>
<style scoped> //
.table { fetchNoticePosts();
margin-bottom: 0; fetchGeneralPosts();
} </script>
.badge {
font-size: 0.75rem; <style scoped>
padding: 0.25em 0.5em; .table > :not(caption) > * > * {
} padding: 0 !important;
.ml-1 { }
margin-left: 0.25rem; .badge {
} font-size: 0.75rem;
</style> padding: 0.25em 0.5em;
}
.ml-1 {
margin-left: 0.25rem;
}
</style>