Compare commits
7 Commits
main
...
board-view
| Author | SHA1 | Date | |
|---|---|---|---|
| b1dec17cc0 | |||
| 2e013a54be | |||
| fbf13fda33 | |||
| 5bd56faf11 | |||
| 922fc09c2d | |||
| c68043f12b | |||
| 0f78a0f8ac |
@ -1,10 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<BoardProfile :profileName="comment.author" :showDetail="false" :author="true" />
|
<BoardProfile :profileName="comment.author" :showDetail="false" :author="true" :isChild="isChild" />
|
||||||
<div class="my-6">
|
<div class="mt-6">
|
||||||
<p>{{ comment.content }}</p>
|
<p class="m-0">{{ comment.content }}</p>
|
||||||
</div>
|
</div>
|
||||||
<PlusButton v-if="isPlusButton" @click="toggleComment"/>
|
<PlusButton v-if="isPlusButton" @click="toggleComment" class="mt-6"/>
|
||||||
<BoardComentArea v-if="isComment" @submit="submitComment"/>
|
<BoardComentArea v-if="isComment" @submit="submitComment"/>
|
||||||
|
|
||||||
<!-- 대댓글 -->
|
<!-- 대댓글 -->
|
||||||
@ -12,9 +12,9 @@
|
|||||||
<li
|
<li
|
||||||
v-for="child in comment.children"
|
v-for="child in comment.children"
|
||||||
:key="child.id"
|
:key="child.id"
|
||||||
class="pt-6 ps-10"
|
class="pt-8 ps-10"
|
||||||
>
|
>
|
||||||
<BoardComment :comment="child" :isPlusButton="false" @submitComment="addChildComment" />
|
<BoardComment :comment="child" :isPlusButton="false" :isChild="true" @submitComment="addChildComment" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<!-- <ul class="list-unstyled twoDepth">
|
<!-- <ul class="list-unstyled twoDepth">
|
||||||
@ -46,7 +46,11 @@ const props = defineProps({
|
|||||||
isPlusButton: {
|
isPlusButton: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
}
|
},
|
||||||
|
isChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// emits 정의
|
// emits 정의
|
||||||
|
|||||||
@ -3,18 +3,15 @@
|
|||||||
<li
|
<li
|
||||||
v-for="comment in comments"
|
v-for="comment in comments"
|
||||||
:key="comment.id"
|
:key="comment.id"
|
||||||
class="mt-6"
|
class="mt-8"
|
||||||
>
|
>
|
||||||
<BoardComment :comment="comment" @submitComment="addComment" />
|
<BoardComment :comment="comment" @submitComment="addComment" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<Pagination/>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import Pagination from '../pagination/Pagination.vue';
|
|
||||||
import BoardComment from './BoardComment.vue'
|
import BoardComment from './BoardComment.vue'
|
||||||
|
|
||||||
const comments = ref([
|
const comments = ref([
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
<div class="d-flex justify-content-between align-items-center flex-wrap mb-6 gap-2">
|
<div class="d-flex justify-content-between align-items-center flex-wrap mb-6 gap-2">
|
||||||
<!-- 제목 섹션 -->
|
<!-- 제목 섹션 -->
|
||||||
<div class="me-1">
|
<div class="me-1">
|
||||||
<h5 class="mb-0">{{ boardTitle }}</h5>
|
<h5 class="mb-0">{{ boardTitle }}adada</h5>
|
||||||
</div>
|
</div>
|
||||||
<!-- 첨부파일 섹션 -->
|
<!-- 첨부파일 섹션 -->
|
||||||
<div v-if="dropdownItems.length > 0" class="btn-group">
|
<div v-if="dropdownItems.length > 0" class="btn-group">
|
||||||
|
|||||||
@ -10,13 +10,13 @@
|
|||||||
<span>2024.12.10 10:46</span>
|
<span>2024.12.10 10:46</span>
|
||||||
<template v-if="showDetail">
|
<template v-if="showDetail">
|
||||||
<span>
|
<span>
|
||||||
<i class="fa-regular fa-eye"></i> 1
|
<i class="fa-regular fa-eye"></i> {{ views }}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<i class="fa-regular fa-thumbs-up"></i> 1
|
<i class="fa-regular fa-thumbs-up"></i> {{ likes }}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<i class="fa-regular fa-thumbs-down"></i> 1
|
<i class="fa-regular fa-thumbs-down"></i> {{ dislikes }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
<i class='bx bx-trash'></i>
|
<i class='bx bx-trash'></i>
|
||||||
</button> -->
|
</button> -->
|
||||||
</template>
|
</template>
|
||||||
<BoardRecommendBtn :likeClicked="true" :dislikeClicked="false" :isRecommend="false" />
|
<BoardRecommendBtn v-if="!isChild" :isRecommend="false" />
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -50,13 +50,12 @@ import axios from '@api';
|
|||||||
import DeleteButton from '../button/DeleteBtn.vue';
|
import DeleteButton from '../button/DeleteBtn.vue';
|
||||||
import EditButton from '../button/EditBtn.vue';
|
import EditButton from '../button/EditBtn.vue';
|
||||||
import BoardRecommendBtn from '../button/BoardRecommendBtn.vue';
|
import BoardRecommendBtn from '../button/BoardRecommendBtn.vue';
|
||||||
import { onMounted } from 'vue';
|
|
||||||
|
|
||||||
// Vue Router 인스턴스
|
// Vue Router 인스턴스
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
// Props 정의
|
// Props 정의
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
profileName: {
|
profileName: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '익명',
|
default: '익명',
|
||||||
@ -73,6 +72,22 @@ defineProps({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
views: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
likes: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
dislikes: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
isChild: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const boardId = 100; //수정필요!!
|
const boardId = 100; //수정필요!!
|
||||||
@ -89,7 +104,6 @@ const handleDelete = async () => {
|
|||||||
alert('게시물이 성공적으로 삭제되었습니다.');
|
alert('게시물이 성공적으로 삭제되었습니다.');
|
||||||
router.push({ name: 'BoardList' });
|
router.push({ name: 'BoardList' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('게시물 삭제 중 오류 발생:', error);
|
|
||||||
alert('게시물 삭제에 실패했습니다.');
|
alert('게시물 삭제에 실패했습니다.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
<template v-if="isRecommend">
|
<template v-if="isRecommend">
|
||||||
<button class="btn btn-label-primary btn-icon" :class="likeClicked ? 'clicked' : '', bigBtn ? 'big' : '' ">
|
<button class="btn btn-label-primary btn-icon" :class="{'clicked': likeClicked, 'big': bigBtn}" @click="handleLike">
|
||||||
<i class="fa-regular fa-thumbs-up"></i> <span class="num">1</span>
|
<i class="fa-regular fa-thumbs-up"></i> <span class="num">{{ likeCount }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-label-danger btn-icon" :class="dislikeClicked ? 'clicked' : '', bigBtn ? 'big' : '' ">
|
<button class="btn btn-label-danger btn-icon" :class="{'clicked': dislikeClicked, 'big': bigBtn}" @click="handleDislike">
|
||||||
<i class="fa-regular fa-thumbs-down"></i> <span class="num">1</span>
|
<i class="fa-regular fa-thumbs-down"></i> <span class="num">{{ dislikeCount }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
defineProps({
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
likeClicked : {
|
likeClicked : {
|
||||||
type : Boolean,
|
type : Boolean,
|
||||||
default : false,
|
default : false,
|
||||||
@ -24,8 +26,65 @@ defineProps({
|
|||||||
isRecommend: {
|
isRecommend: {
|
||||||
type:Boolean,
|
type:Boolean,
|
||||||
default:true,
|
default:true,
|
||||||
}
|
},
|
||||||
|
boardId: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
commentId: {
|
||||||
|
type: Number,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
likeCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
dislikeCount: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['updateReaction']);
|
||||||
|
|
||||||
|
const likeClicked = ref(props.likeClicked);
|
||||||
|
const dislikeClicked = ref(props.dislikeClicked);
|
||||||
|
const likeCount = ref(props.likeCount);
|
||||||
|
const dislikeCount = ref(props.dislikeCount);
|
||||||
|
|
||||||
|
// 부모에서 likeCount 또는 dislikeCount가 변경되면 로컬 상태를 업데이트
|
||||||
|
watch(() => props.likeCount, (newVal) => {
|
||||||
|
likeCount.value = newVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => props.dislikeCount, (newVal) => {
|
||||||
|
dislikeCount.value = newVal;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLike = () => {
|
||||||
|
likeClicked.value = !likeClicked.value;
|
||||||
|
likeCount.value += likeClicked.value ? 1 : -1;
|
||||||
|
emit('updateReaction', { type: 'like', boardId: props.boardId, commentId: props.commentId });
|
||||||
|
if(likeClicked.value === true){
|
||||||
|
if(dislikeClicked.value === true) {
|
||||||
|
dislikeClicked.value = false;
|
||||||
|
dislikeCount.value += -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDislike = () => {
|
||||||
|
dislikeClicked.value = !dislikeClicked.value;
|
||||||
|
dislikeCount.value += dislikeClicked.value ? 1 : -1;
|
||||||
|
emit('updateReaction', { type: 'dislike', boardId: props.boardId, commentId: props.commentId });
|
||||||
|
if(dislikeClicked.value === true){
|
||||||
|
if(likeClicked.value === true) {
|
||||||
|
likeClicked.value = false;
|
||||||
|
likeCount.value += -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -5,28 +5,62 @@
|
|||||||
<div class="card">
|
<div class="card">
|
||||||
<!-- 프로필 헤더 -->
|
<!-- 프로필 헤더 -->
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<BoardProfile :boardId="currentBoardId" :profileName="profileName" />
|
<BoardProfile
|
||||||
<hr/>
|
:boardId="currentBoardId"
|
||||||
|
:profileName="profileName"
|
||||||
|
:views="views"
|
||||||
|
:likes="likes"
|
||||||
|
:dislikes="dislikes"
|
||||||
|
class="pb-6 border-bottom"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- 게시글 내용 -->
|
<!-- 게시글 내용 -->
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="mb-4">{{ boardTitle }}</h5>
|
|
||||||
<!-- HTML 콘텐츠 렌더링 -->
|
|
||||||
<div class="board-content text-body" style="line-height: 1.6;" v-html="$common.contentToHtml(boardContent)">
|
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center flex-wrap mb-6 gap-2">
|
||||||
|
<!-- 제목 섹션 -->
|
||||||
|
<div class="me-1">
|
||||||
|
<h5 class="mb-4">{{ boardTitle }}</h5>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 첨부파일 섹션 -->
|
||||||
|
<div v-if="attachments" class="btn-group">
|
||||||
|
<button type="button" class="btn btn-label-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
<i class="fa-solid fa-download me-2"></i>
|
||||||
|
첨부파일
|
||||||
|
<!-- (<span class="attachment-num">{{ dropdownItems.length }}</span>) -->
|
||||||
|
</button>
|
||||||
|
<!-- <ul class="dropdown-menu">
|
||||||
|
<li v-for="(item, index) in dropdownItems" :key="index">
|
||||||
|
<a class="dropdown-item" href="javascript:void(0);">
|
||||||
|
{{ item.label }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul> -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- HTML 콘텐츠 렌더링 -->
|
||||||
|
<div class="board-content text-body" style="line-height: 1.6;" v-html="$common.contentToHtml(boardContent)"></div>
|
||||||
|
|
||||||
<!-- 좋아요 버튼 -->
|
<!-- 좋아요 버튼 -->
|
||||||
<div class="row justify-content-center my-10">
|
<div class="row justify-content-center my-10">
|
||||||
<BoardRecommendBtn :bigBtn="true"/>
|
<BoardRecommendBtn
|
||||||
|
:bigBtn="true"
|
||||||
|
:boardId="currentBoardId"
|
||||||
|
:commentId="null"
|
||||||
|
:likeCount="currentLikeCount"
|
||||||
|
:dislikeCount="currentDislikeCount"
|
||||||
|
@updateReaction="handleUpdateReaction"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 첨부파일 목록 -->
|
<!-- 첨부파일 목록 -->
|
||||||
<ul v-if="attachments.length" class="attachments mt-4 list-unstyled">
|
<!-- <ul v-if="attachments.length" class="attachments mt-4 list-unstyled">
|
||||||
<li v-for="(attachment, index) in attachments" :key="index" class="mb-2">
|
<li v-for="(attachment, index) in attachments" :key="index" class="mb-2">
|
||||||
<a :href="attachment.url" target="_blank" class="text-decoration-none">{{ attachment.name }}</a>
|
<a :href="attachment.url" target="_blank" class="text-decoration-none">{{ attachment.name }}</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul> -->
|
||||||
|
|
||||||
<!-- 댓글 영역 -->
|
<!-- 댓글 영역 -->
|
||||||
<BoardComentArea :comments="comments" />
|
<BoardComentArea :comments="comments" />
|
||||||
@ -38,6 +72,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-footer">
|
<div class="card-footer">
|
||||||
<BoardCommentList/>
|
<BoardCommentList/>
|
||||||
|
|
||||||
|
<Pagination/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -47,21 +83,24 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import BoardComentArea from '@c/board/BoardComentArea.vue';
|
import BoardComentArea from '@c/board/BoardComentArea.vue';
|
||||||
import BoardCommentList from '@/components/board/BoardCommentList.vue';
|
|
||||||
import BoardProfile from '@c/board/BoardProfile.vue';
|
import BoardProfile from '@c/board/BoardProfile.vue';
|
||||||
|
import BoardCommentList from '@/components/board/BoardCommentList.vue';
|
||||||
import BoardRecommendBtn from '@/components/button/BoardRecommendBtn.vue';
|
import BoardRecommendBtn from '@/components/button/BoardRecommendBtn.vue';
|
||||||
|
import Pagination from '@/components/pagination/Pagination.vue';
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import axios from '@api';
|
import axios from '@api';
|
||||||
import Quill from 'quill';
|
|
||||||
|
|
||||||
// 게시물 데이터 상태
|
// 게시물 데이터 상태
|
||||||
const profileName = ref('익명 사용자');
|
const profileName = ref('익명 사용자');
|
||||||
const boardTitle = ref('제목 없음');
|
const boardTitle = ref('제목 없음');
|
||||||
const boardContent = ref('');
|
const boardContent = ref('');
|
||||||
const convertedContent = ref('내용 없음');
|
|
||||||
const comments = ref([]);
|
const comments = ref([]);
|
||||||
const attachments = ref([]);
|
const attachments = ref([]);
|
||||||
|
const views = ref(0);
|
||||||
|
const likes = ref(0);
|
||||||
|
const dislikes = ref(0);
|
||||||
|
const attachment = ref(false);
|
||||||
|
|
||||||
// 라우트에서 ID 가져오기
|
// 라우트에서 ID 가져오기
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@ -81,19 +120,36 @@ const fetchBoardDetails = async () => {
|
|||||||
|
|
||||||
// API 응답 데이터 반영
|
// API 응답 데이터 반영
|
||||||
const boardDetail = data.boardDetail || {};
|
const boardDetail = data.boardDetail || {};
|
||||||
profileName.value = boardDetail.author || '익명 사용자';
|
|
||||||
boardTitle.value = boardDetail.title || '제목 없음';
|
|
||||||
boardContent.value = boardDetail.content || '';
|
|
||||||
|
|
||||||
|
profileName.value = data.author || '익명 사용자';
|
||||||
|
boardTitle.value = data.title || '제목 없음';
|
||||||
|
boardContent.value = data.content || '';
|
||||||
|
views.value = data.cnt || 0;
|
||||||
|
likes.value = data.likeCount || 0;
|
||||||
|
dislikes.value = data.dislikeCount || 0;
|
||||||
|
attachment.value = data.hasAttachment || null;
|
||||||
|
|
||||||
attachments.value = data.attachments || [];
|
attachments.value = data.attachments || [];
|
||||||
comments.value = data.comments || [];
|
comments.value = data.comments || [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('게시물 가져오기 오류:', error);
|
|
||||||
alert('게시물 데이터를 불러오는 중 오류가 발생했습니다.');
|
alert('게시물 데이터를 불러오는 중 오류가 발생했습니다.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const currentLikeCount = ref(10);
|
||||||
|
const currentDislikeCount = ref(2);
|
||||||
|
|
||||||
|
// 좋아요, 싫어요
|
||||||
|
const handleUpdateReaction = async ({ type, boardId, commentId }) => {
|
||||||
|
try {
|
||||||
|
const cmtId = commentId !== null ? commentId : 0;
|
||||||
|
|
||||||
|
const response = await axios.post(`/board/${boardId}/${cmtId}/reaction`, { type });
|
||||||
|
} catch (error) {
|
||||||
|
alert('반응을 업데이트하는 중 오류 발생');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 컴포넌트 마운트 시 데이터 로드
|
// 컴포넌트 마운트 시 데이터 로드
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchBoardDetails();
|
fetchBoardDetails();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user