Merge branch 'main' of http://192.168.0.251:3000/localnet/localhost-front
This commit is contained in:
commit
ad0a5653f4
@ -96,7 +96,7 @@ import { computed } from "vue";
|
|||||||
import { useUserInfoStore } from '@s/useUserInfoStore';
|
import { useUserInfoStore } from '@s/useUserInfoStore';
|
||||||
|
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
const allowedUserId = 26; // 특정 ID (변경필요!!)
|
const allowedUserId = 1; // 특정 ID (변경필요!!)
|
||||||
|
|
||||||
const userId = computed(() => userStore.user?.id ?? null);
|
const userId = computed(() => userStore.user?.id ?? null);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -111,7 +111,7 @@ const router = createRouter({
|
|||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
await authStore.checkAuthStatus(); // 로그인 상태 확인
|
await authStore.checkAuthStatus(); // 로그인 상태 확인
|
||||||
const allowedUserId = 26; // 특정 ID (변경필요!!)
|
const allowedUserId = 1; // 특정 ID (변경필요!!)
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
const userId = userStore.user?.id ?? null;
|
const userId = userStore.user?.id ?? null;
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
return next({ name: 'Login', query: { redirect: to.fullPath } });
|
return next({ name: 'Login', query: { redirect: to.fullPath } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorization 페이지는 ID가 26이 아니면 접근 차단
|
// Authorization 페이지는 ID가 1이 아니면 접근 차단
|
||||||
if (to.path === "/authorization" && userId !== allowedUserId) {
|
if (to.path === "/authorization" && userId !== allowedUserId) {
|
||||||
return next("/");
|
return next("/");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,18 +6,19 @@
|
|||||||
<div class="mb-3 w-100">
|
<div class="mb-3 w-100">
|
||||||
<search-bar @update:data="search" @keyup.enter="searchOnEnter" class="flex-grow-1" />
|
<search-bar @update:data="search" @keyup.enter="searchOnEnter" class="flex-grow-1" />
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center" style="gap: 15px;">
|
<div class="d-flex align-items-center" style="gap: 15px">
|
||||||
<!-- 리스트 갯수 선택 -->
|
<!-- 리스트 갯수 선택 -->
|
||||||
<select class="form-select w-auto" v-model="selectedSize" @change="handleSizeChange" style="margin-left: 0;">
|
<select class="form-select w-auto" v-model="selectedSize" @change="handleSizeChange" style="margin-left: 0">
|
||||||
<option value="10">10개씩</option>
|
<option value="10">10개씩</option>
|
||||||
<option value="20">20개씩</option>
|
<option value="20">20개씩</option>
|
||||||
<option value="30">30개씩</option>
|
<option value="30">30개씩</option>
|
||||||
<option value="50">50개씩</option>
|
<option value="50">50개씩</option>
|
||||||
|
<option value="100">100개씩</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<!-- 셀렉트 박스 -->
|
<!-- 셀렉트 박스 -->
|
||||||
<select class="form-select w-auto" v-model="selectedOrder" @change="handleSortChange">
|
<select class="form-select w-auto" v-model="selectedOrder" @change="handleSortChange">
|
||||||
<option value="date">최신날짜</option>
|
<option value="date">날짜</option>
|
||||||
<option value="views">조회수</option>
|
<option value="views">조회수</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@ -38,26 +39,33 @@
|
|||||||
<table class="datatables-users table border-top dataTable dtr-column">
|
<table class="datatables-users table border-top dataTable dtr-column">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th style="width: 11%;" class="text-center fw-bold">번호</th>
|
<th style="width: 11%" class="text-center fw-bold">번호</th>
|
||||||
<th style="width: 45%;" class="text-center fw-bold">제목</th>
|
<th style="width: 45%" class="text-center fw-bold">제목</th>
|
||||||
<th style="width: 10%;" class="text-center fw-bold">작성자</th>
|
<th style="width: 10%" class="text-center fw-bold">작성자</th>
|
||||||
<th style="width: 15%;" class="text-center fw-bold">작성일</th>
|
<th style="width: 15%" class="text-center fw-bold">작성일</th>
|
||||||
<th style="width: 9%;" class="text-center fw-bold">조회수</th>
|
<th style="width: 9%" class="text-center fw-bold">조회수</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- 공지사항 -->
|
<!-- 공지사항 -->
|
||||||
<template v-if="pagination.currentPage === 1 && !showNotices">
|
<template v-if="pagination.currentPage === 1 && !showNotices">
|
||||||
<tr v-for="(notice, index) in noticeList"
|
<tr
|
||||||
|
v-for="(notice, index) in noticeList"
|
||||||
:key="'notice-' + index"
|
:key="'notice-' + index"
|
||||||
class="bg-label-gray fw-bold"
|
class="bg-label-gray fw-bold"
|
||||||
@click="goDetail(notice.id)">
|
@click="goDetail(notice.id)"
|
||||||
|
>
|
||||||
<td class="text-center">공지</td>
|
<td class="text-center">공지</td>
|
||||||
<td class="cursor-pointer">
|
<td class="cursor-pointer">
|
||||||
📌 {{ notice.title }}
|
📌 {{ notice.title }}
|
||||||
<span v-if="notice.commentCount" class="text-danger fw-bold me-1">[ {{ notice.commentCount }} ]</span>
|
<span v-if="notice.commentCount" class="text-danger fw-bold me-1"
|
||||||
|
>[ {{ notice.commentCount }} ]</span
|
||||||
|
>
|
||||||
<i v-if="notice.img" class="bi bi-image me-1 align-middle"></i>
|
<i v-if="notice.img" class="bi bi-image me-1 align-middle"></i>
|
||||||
<i v-if="Array.isArray(notice.hasAttachment) && notice.hasAttachment.length > 0" class="bi bi-paperclip"></i>
|
<i
|
||||||
|
v-if="Array.isArray(notice.hasAttachment) && notice.hasAttachment.length > 0"
|
||||||
|
class="bi bi-paperclip"
|
||||||
|
></i>
|
||||||
<span v-if="isNewPost(notice.rawDate)" class="badge bg-danger text-white ms-2 fs-tiny">N</span>
|
<span v-if="isNewPost(notice.rawDate)" class="badge bg-danger text-white ms-2 fs-tiny">N</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ notice.author }}</td>
|
<td class="text-center">{{ notice.author }}</td>
|
||||||
@ -66,16 +74,21 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
<!-- 일반 게시물 -->
|
<!-- 일반 게시물 -->
|
||||||
<tr v-for="(post, index) in generalList"
|
<tr
|
||||||
|
v-for="(post, index) in generalList"
|
||||||
:key="'post-' + index"
|
:key="'post-' + index"
|
||||||
class="invert-bg-white"
|
class="invert-bg-white"
|
||||||
@click="goDetail(post.realId)">
|
@click="goDetail(post.realId)"
|
||||||
|
>
|
||||||
<td class="text-center">{{ post.id }}</td>
|
<td class="text-center">{{ post.id }}</td>
|
||||||
<td class="cursor-pointer">
|
<td class="cursor-pointer">
|
||||||
{{ post.title }}
|
{{ post.title }}
|
||||||
<span v-if="post.commentCount" class="comment-count">[ {{ post.commentCount }} ]</span>
|
<span v-if="post.commentCount" class="comment-count">[ {{ post.commentCount }} ]</span>
|
||||||
<i v-if="post.img" class="bi bi-image me-1"></i>
|
<i v-if="post.img" class="bi bi-image me-1"></i>
|
||||||
<i v-if="Array.isArray(post.hasAttachment) && post.hasAttachment.length > 0" class="bi bi-paperclip"></i>
|
<i
|
||||||
|
v-if="Array.isArray(post.hasAttachment) && post.hasAttachment.length > 0"
|
||||||
|
class="bi bi-paperclip"
|
||||||
|
></i>
|
||||||
<span v-if="isNewPost(post.rawDate)" class="badge bg-danger text-white ms-2 fs-tiny">N</span>
|
<span v-if="isNewPost(post.rawDate)" class="badge bg-danger text-white ms-2 fs-tiny">N</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center">{{ post.author }}</td>
|
<td class="text-center">{{ post.author }}</td>
|
||||||
@ -86,9 +99,7 @@
|
|||||||
</table>
|
</table>
|
||||||
<!-- 게시물이 없을 때 -->
|
<!-- 게시물이 없을 때 -->
|
||||||
<div v-if="generalList.length === 0">
|
<div v-if="generalList.length === 0">
|
||||||
<p class="text-center pt-10 mt-2 mb-0 text-muted">
|
<p class="text-center pt-10 mt-2 mb-0 text-muted">게시물이 없습니다.</p>
|
||||||
게시물이 없습니다.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -96,11 +107,7 @@
|
|||||||
<!-- 페이지네이션 -->
|
<!-- 페이지네이션 -->
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
<Pagination
|
<Pagination v-if="pagination.pages" v-bind="pagination" @update:currentPage="handlePageChange" />
|
||||||
v-if="pagination.pages"
|
|
||||||
v-bind="pagination"
|
|
||||||
@update:currentPage="handlePageChange"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -109,172 +116,169 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import Pagination from '@c/pagination/Pagination.vue';
|
import Pagination from '@c/pagination/Pagination.vue';
|
||||||
import SearchBar from '@c/search/SearchBar.vue';
|
import SearchBar from '@c/search/SearchBar.vue';
|
||||||
import router from '@/router';
|
import router from '@/router';
|
||||||
import WriteButton from '@c/button/WriteBtn.vue';
|
import WriteButton from '@c/button/WriteBtn.vue';
|
||||||
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';
|
import 'bootstrap-icons/font/bootstrap-icons.css';
|
||||||
|
|
||||||
|
dayjs.extend(isToday);
|
||||||
|
dayjs.extend(isYesterday);
|
||||||
|
|
||||||
dayjs.extend(isToday);
|
// 데이터 초기화
|
||||||
dayjs.extend(isYesterday);
|
const generalList = ref([]);
|
||||||
|
const noticeList = ref([]);
|
||||||
|
const searchText = ref('');
|
||||||
|
const selectedOrder = ref('date');
|
||||||
|
const selectedSize = ref(10);
|
||||||
|
const showNotices = ref(false);
|
||||||
|
|
||||||
// 데이터 초기화
|
const pagination = ref({
|
||||||
const generalList = ref([]);
|
currentPage: 1,
|
||||||
const noticeList = ref([]);
|
pages: 1,
|
||||||
const searchText = ref('');
|
prePage: 0,
|
||||||
const selectedOrder = ref('date');
|
nextPage: 1,
|
||||||
const selectedSize = ref(10);
|
isFirstPage: true,
|
||||||
const showNotices = ref(false);
|
isLastPage: false,
|
||||||
|
hasPreviousPage: false,
|
||||||
|
hasNextPage: false,
|
||||||
|
navigatePages: 10,
|
||||||
|
navigatepageNums: [1],
|
||||||
|
navigateFirstPage: 1,
|
||||||
|
navigateLastPage: 1,
|
||||||
|
});
|
||||||
|
|
||||||
const pagination = ref({
|
// 상세 페이지 이동
|
||||||
currentPage: 1,
|
const goDetail = id => {
|
||||||
pages: 1,
|
router.push({ name: 'BoardDetail', params: { id: id } });
|
||||||
prePage: 0,
|
};
|
||||||
nextPage: 1,
|
|
||||||
isFirstPage: true,
|
|
||||||
isLastPage: false,
|
|
||||||
hasPreviousPage: false,
|
|
||||||
hasNextPage: false,
|
|
||||||
navigatePages: 10,
|
|
||||||
navigatepageNums: [1],
|
|
||||||
navigateFirstPage: 1,
|
|
||||||
navigateLastPage: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
// 상세 페이지 이동
|
// 날짜 포맷 변환 함수 (오늘이면 HH:mm, 아니면 YYYY-MM-DD)
|
||||||
const goDetail = (id) => {
|
const formatDate = dateString => {
|
||||||
router.push({ name: 'BoardDetail', params: { id: id } });
|
const date = dayjs(dateString);
|
||||||
};
|
return date.isToday() ? date.format('HH:mm') : date.format('YYYY-MM-DD');
|
||||||
|
};
|
||||||
|
|
||||||
// 날짜 포맷 변환 함수 (오늘이면 HH:mm, 아니면 YYYY-MM-DD)
|
// 새로 올라온 게시물 여부 판단 (오늘 또는 어제 작성된 경우)
|
||||||
const formatDate = (dateString) => {
|
const isNewPost = dateString => {
|
||||||
const date = dayjs(dateString);
|
const date = dayjs(dateString);
|
||||||
return date.isToday() ? date.format('HH:mm') : date.format('YYYY-MM-DD');
|
return date.isToday() || date.isYesterday();
|
||||||
};
|
};
|
||||||
|
|
||||||
// 새로 올라온 게시물 여부 판단 (오늘 또는 어제 작성된 경우)
|
// 검색 처리
|
||||||
const isNewPost = (dateString) => {
|
const search = e => {
|
||||||
const date = dayjs(dateString);
|
searchText.value = e.trim();
|
||||||
return date.isToday() || date.isYesterday();
|
fetchGeneralPosts(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 검색 처리
|
// 정렬 변경 핸들러
|
||||||
const search = (e) => {
|
const handleSortChange = () => {
|
||||||
searchText.value = e.trim();
|
fetchGeneralPosts(1);
|
||||||
fetchGeneralPosts(1);
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// 정렬 변경 핸들러
|
// 리스트 개수 변경 핸들러
|
||||||
const handleSortChange = () => {
|
const handleSizeChange = () => {
|
||||||
fetchGeneralPosts(1);
|
fetchGeneralPosts(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 리스트 개수 변경 핸들러
|
// 일반 게시물 데이터 로드
|
||||||
const handleSizeChange = () => {
|
const fetchGeneralPosts = async (page = 1) => {
|
||||||
fetchGeneralPosts(1);
|
try {
|
||||||
};
|
const { data } = await axios.get('board/general', {
|
||||||
|
params: {
|
||||||
|
page,
|
||||||
|
size: selectedSize.value,
|
||||||
|
orderBy: selectedOrder.value,
|
||||||
|
searchKeyword: searchText.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 일반 게시물 데이터 로드
|
if (data?.data) {
|
||||||
const fetchGeneralPosts = async (page = 1) => {
|
const totalPosts = data.data.total;
|
||||||
try {
|
generalList.value = data.data.list.map((post, index) => ({
|
||||||
const { data } = await axios.get("board/general", {
|
realId: post.id,
|
||||||
params: {
|
id: totalPosts - (page - 1) * selectedSize.value - index,
|
||||||
page,
|
title: post.title,
|
||||||
size: selectedSize.value,
|
author: post.author || '익명',
|
||||||
orderBy: selectedOrder.value,
|
rawDate: post.date,
|
||||||
searchKeyword: searchText.value
|
date: formatDate(post.date), // 날짜 변환 적용
|
||||||
|
views: post.cnt || 0,
|
||||||
|
hasAttachment: post.hasAttachment,
|
||||||
|
img: post.firstImageUrl || null,
|
||||||
|
commentCount: post.commentCount,
|
||||||
|
}));
|
||||||
|
|
||||||
|
pagination.value = {
|
||||||
|
...pagination.value,
|
||||||
|
currentPage: data.data.pageNum,
|
||||||
|
pages: data.data.pages,
|
||||||
|
prePage: data.data.prePage,
|
||||||
|
nextPage: data.data.nextPage,
|
||||||
|
isFirstPage: data.data.isFirstPage,
|
||||||
|
isLastPage: data.data.isLastPage,
|
||||||
|
hasPreviousPage: data.data.hasPreviousPage,
|
||||||
|
hasNextPage: data.data.hasNextPage,
|
||||||
|
navigatePages: data.data.navigatePages,
|
||||||
|
navigatepageNums: data.data.navigatepageNums,
|
||||||
|
navigateFirstPage: data.data.navigateFirstPage,
|
||||||
|
navigateLastPage: data.data.navigateLastPage,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
});
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
if (data?.data) {
|
// 공지사항 데이터 로드
|
||||||
const totalPosts = data.data.total;
|
const fetchNoticePosts = async () => {
|
||||||
generalList.value = data.data.list.map((post, index) => ({
|
try {
|
||||||
realId: post.id,
|
const { data } = await axios.get('board/notices', {
|
||||||
id: totalPosts - ((page - 1) * selectedSize.value) - index,
|
params: { searchKeyword: searchText.value },
|
||||||
title: post.title,
|
});
|
||||||
author: post.author || '익명',
|
|
||||||
rawDate: post.date,
|
|
||||||
date: formatDate(post.date), // 날짜 변환 적용
|
|
||||||
views: post.cnt || 0,
|
|
||||||
hasAttachment: post.hasAttachment,
|
|
||||||
img: post.firstImageUrl || null,
|
|
||||||
commentCount : post.commentCount
|
|
||||||
}));
|
|
||||||
|
|
||||||
pagination.value = {
|
if (data?.data) {
|
||||||
...pagination.value,
|
noticeList.value = data.data.map(post => ({
|
||||||
currentPage: data.data.pageNum,
|
id: post.id,
|
||||||
pages: data.data.pages,
|
title: post.title,
|
||||||
prePage: data.data.prePage,
|
author: post.author || '관리자',
|
||||||
nextPage: data.data.nextPage,
|
date: formatDate(post.date),
|
||||||
isFirstPage: data.data.isFirstPage,
|
rawDate: post.date,
|
||||||
isLastPage: data.data.isLastPage,
|
views: post.cnt || 0,
|
||||||
hasPreviousPage: data.data.hasPreviousPage,
|
hasAttachment: post.hasAttachment,
|
||||||
hasNextPage: data.data.hasNextPage,
|
img: post.firstImageUrl || null,
|
||||||
navigatePages: data.data.navigatePages,
|
commentCount: post.commentCount,
|
||||||
navigatepageNums: data.data.navigatepageNums,
|
}));
|
||||||
navigateFirstPage: data.data.navigateFirstPage,
|
}
|
||||||
navigateLastPage: data.data.navigateLastPage
|
} catch (error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Enter 키를 눌렀을 때
|
||||||
|
const searchOnEnter = event => {
|
||||||
|
const searchTextValue = event.target.value.trim();
|
||||||
|
|
||||||
|
if (!searchTextValue || searchTextValue[0] === ' ') {
|
||||||
|
return; // 검색어가 비어있거나 첫 글자가 공백이면 실행 안 함
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 공지사항 데이터 로드
|
searchText.value = searchTextValue;
|
||||||
const fetchNoticePosts = async () => {
|
fetchGeneralPosts(1);
|
||||||
try {
|
};
|
||||||
const { data } = await axios.get("board/notices", {
|
|
||||||
params: { searchKeyword: searchText.value }
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data?.data) {
|
// 페이지 변경
|
||||||
noticeList.value = data.data.map(post => ({
|
const handlePageChange = page => {
|
||||||
id: post.id,
|
if (page !== pagination.value.currentPage) {
|
||||||
title: post.title,
|
fetchGeneralPosts(page);
|
||||||
author: post.author || '관리자',
|
|
||||||
date: formatDate(post.date),
|
|
||||||
rawDate: post.date,
|
|
||||||
views: post.cnt || 0,
|
|
||||||
hasAttachment: post.hasAttachment,
|
|
||||||
img: post.firstImageUrl || null,
|
|
||||||
commentCount : post.commentCount
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Enter 키를 눌렀을 때
|
// 데이터 로드
|
||||||
const searchOnEnter = (event) => {
|
onMounted(() => {
|
||||||
const searchTextValue = event.target.value.trim();
|
fetchNoticePosts();
|
||||||
|
fetchGeneralPosts();
|
||||||
if (!searchTextValue || searchTextValue[0] === ' ') {
|
});
|
||||||
return; // 검색어가 비어있거나 첫 글자가 공백이면 실행 안 함
|
|
||||||
}
|
|
||||||
|
|
||||||
searchText.value = searchTextValue;
|
|
||||||
fetchGeneralPosts(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 페이지 변경
|
|
||||||
const handlePageChange = (page) => {
|
|
||||||
if (page !== pagination.value.currentPage) {
|
|
||||||
fetchGeneralPosts(page);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 데이터 로드
|
|
||||||
onMounted(() => {
|
|
||||||
fetchNoticePosts();
|
|
||||||
fetchGeneralPosts();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user