Merge branch 'board-comment'
This commit is contained in:
commit
b2eafbb9aa
@ -2,116 +2,121 @@
|
||||
* Main
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
(function () {
|
||||
// Initialize menu
|
||||
//-----------------
|
||||
let menu, animate;
|
||||
|
||||
<<<<<<< HEAD
|
||||
var menu, animate;
|
||||
(function () {
|
||||
// Initialize menu
|
||||
//-----------------
|
||||
=======
|
||||
let layoutMenuEl = document.querySelectorAll('#layout-menu');
|
||||
layoutMenuEl.forEach(function (element) {
|
||||
menu = new Menu(element, {
|
||||
orientation: 'vertical',
|
||||
closeChildren: false,
|
||||
});
|
||||
// Change parameter to true if you want scroll animation
|
||||
window.Helpers.scrollToActive((animate = false));
|
||||
window.Helpers.mainMenu = menu;
|
||||
});
|
||||
>>>>>>> board-comment
|
||||
|
||||
let layoutMenuEl = document.querySelectorAll('#layout-menu')
|
||||
layoutMenuEl.forEach(function (element) {
|
||||
menu = new Menu(element, {
|
||||
orientation: 'vertical',
|
||||
closeChildren: false,
|
||||
})
|
||||
// Change parameter to true if you want scroll animation
|
||||
window.Helpers.scrollToActive((animate = false))
|
||||
window.Helpers.mainMenu = menu
|
||||
})
|
||||
// Initialize menu togglers and bind click on each
|
||||
let menuToggler = document.querySelectorAll('.layout-menu-toggle');
|
||||
menuToggler.forEach(item => {
|
||||
item.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
window.Helpers.toggleCollapsed();
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize menu togglers and bind click on each
|
||||
let menuToggler = document.querySelectorAll('.layout-menu-toggle')
|
||||
menuToggler.forEach((item) => {
|
||||
item.addEventListener('click', (event) => {
|
||||
event.preventDefault()
|
||||
window.Helpers.toggleCollapsed()
|
||||
})
|
||||
})
|
||||
// Display menu toggle (layout-menu-toggle) on hover with delay
|
||||
let delay = function (elem, callback) {
|
||||
let timeout = null;
|
||||
elem.onmouseenter = function () {
|
||||
// Set timeout to be a timer which will invoke callback after 300ms (not for small screen)
|
||||
if (!Helpers.isSmallScreen()) {
|
||||
timeout = setTimeout(callback, 300);
|
||||
} else {
|
||||
timeout = setTimeout(callback, 0);
|
||||
}
|
||||
};
|
||||
|
||||
// Display menu toggle (layout-menu-toggle) on hover with delay
|
||||
let delay = function (elem, callback) {
|
||||
let timeout = null
|
||||
elem.onmouseenter = function () {
|
||||
// Set timeout to be a timer which will invoke callback after 300ms (not for small screen)
|
||||
if (!Helpers.isSmallScreen()) {
|
||||
timeout = setTimeout(callback, 300)
|
||||
} else {
|
||||
timeout = setTimeout(callback, 0)
|
||||
}
|
||||
elem.onmouseleave = function () {
|
||||
// Clear any timers set to timeout
|
||||
document.querySelector('.layout-menu-toggle').classList.remove('d-block');
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
};
|
||||
if (document.getElementById('layout-menu')) {
|
||||
delay(document.getElementById('layout-menu'), function () {
|
||||
// not for small screen
|
||||
if (!Helpers.isSmallScreen()) {
|
||||
document.querySelector('.layout-menu-toggle').classList.add('d-block');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
elem.onmouseleave = function () {
|
||||
// Clear any timers set to timeout
|
||||
document.querySelector('.layout-menu-toggle').classList.remove('d-block')
|
||||
clearTimeout(timeout)
|
||||
// Display in main menu when menu scrolls
|
||||
let menuInnerContainer = document.getElementsByClassName('menu-inner'),
|
||||
menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0];
|
||||
if (menuInnerContainer.length > 0 && menuInnerShadow) {
|
||||
menuInnerContainer[0].addEventListener('ps-scroll-y', function () {
|
||||
if (this.querySelector('.ps__thumb-y').offsetTop) {
|
||||
menuInnerShadow.style.display = 'block';
|
||||
} else {
|
||||
menuInnerShadow.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (document.getElementById('layout-menu')) {
|
||||
delay(document.getElementById('layout-menu'), function () {
|
||||
// not for small screen
|
||||
if (!Helpers.isSmallScreen()) {
|
||||
document.querySelector('.layout-menu-toggle').classList.add('d-block')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Display in main menu when menu scrolls
|
||||
let menuInnerContainer = document.getElementsByClassName('menu-inner'),
|
||||
menuInnerShadow = document.getElementsByClassName('menu-inner-shadow')[0]
|
||||
if (menuInnerContainer.length > 0 && menuInnerShadow) {
|
||||
menuInnerContainer[0].addEventListener('ps-scroll-y', function () {
|
||||
if (this.querySelector('.ps__thumb-y').offsetTop) {
|
||||
menuInnerShadow.style.display = 'block'
|
||||
} else {
|
||||
menuInnerShadow.style.display = 'none'
|
||||
}
|
||||
})
|
||||
}
|
||||
// Init helpers & misc
|
||||
// --------------------
|
||||
|
||||
// Init helpers & misc
|
||||
// --------------------
|
||||
// Init BS Tooltip
|
||||
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
});
|
||||
|
||||
// Init BS Tooltip
|
||||
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
||||
tooltipTriggerList.map(function (tooltipTriggerEl) {
|
||||
return new bootstrap.Tooltip(tooltipTriggerEl)
|
||||
})
|
||||
// Accordion active class
|
||||
const accordionActiveFunction = function (e) {
|
||||
if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') {
|
||||
e.target.closest('.accordion-item').classList.add('active');
|
||||
} else {
|
||||
e.target.closest('.accordion-item').classList.remove('active');
|
||||
}
|
||||
};
|
||||
|
||||
// Accordion active class
|
||||
const accordionActiveFunction = function (e) {
|
||||
if (e.type == 'show.bs.collapse' || e.type == 'show.bs.collapse') {
|
||||
e.target.closest('.accordion-item').classList.add('active')
|
||||
} else {
|
||||
e.target.closest('.accordion-item').classList.remove('active')
|
||||
const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion'));
|
||||
const accordionList = accordionTriggerList.map(function (accordionTriggerEl) {
|
||||
accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction);
|
||||
accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction);
|
||||
});
|
||||
|
||||
// Auto update layout based on screen size
|
||||
window.Helpers.setAutoUpdate(true);
|
||||
|
||||
// Toggle Password Visibility
|
||||
window.Helpers.initPasswordToggle();
|
||||
|
||||
// Speech To Text
|
||||
window.Helpers.initSpeechToText();
|
||||
|
||||
// Manage menu expanded/collapsed with templateCustomizer & local storage
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// If current layout is horizontal OR current window screen is small (overlay menu) than return from here
|
||||
if (window.Helpers.isSmallScreen()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const accordionTriggerList = [].slice.call(document.querySelectorAll('.accordion'))
|
||||
const accordionList = accordionTriggerList.map(function (accordionTriggerEl) {
|
||||
accordionTriggerEl.addEventListener('show.bs.collapse', accordionActiveFunction)
|
||||
accordionTriggerEl.addEventListener('hide.bs.collapse', accordionActiveFunction)
|
||||
})
|
||||
// If current layout is vertical and current window screen is > small
|
||||
|
||||
// Auto update layout based on screen size
|
||||
window.Helpers.setAutoUpdate(true)
|
||||
|
||||
// Toggle Password Visibility
|
||||
window.Helpers.initPasswordToggle()
|
||||
|
||||
// Speech To Text
|
||||
window.Helpers.initSpeechToText()
|
||||
|
||||
// Manage menu expanded/collapsed with templateCustomizer & local storage
|
||||
//------------------------------------------------------------------
|
||||
|
||||
// If current layout is horizontal OR current window screen is small (overlay menu) than return from here
|
||||
if (window.Helpers.isSmallScreen()) {
|
||||
return
|
||||
}
|
||||
|
||||
// If current layout is vertical and current window screen is > small
|
||||
|
||||
// Auto update menu collapsed/expanded based on the themeConfig
|
||||
window.Helpers.setCollapsed(true, false)
|
||||
})()
|
||||
// Auto update menu collapsed/expanded based on the themeConfig
|
||||
window.Helpers.setCollapsed(true, false);
|
||||
})();
|
||||
|
||||
@ -15,35 +15,69 @@ import Quill from 'quill';
|
||||
$common.변수
|
||||
*/
|
||||
const common = {
|
||||
// JSON 문자열로 Delta 타입을 변환
|
||||
contentToHtml(content) {
|
||||
try {
|
||||
if (content.startsWith('{') || content.startsWith('[')) {
|
||||
// Delta 형식으로 변환
|
||||
const delta = JSON.parse(content);
|
||||
const quill = new Quill(document.createElement('div'));
|
||||
quill.setContents(delta);
|
||||
return quill.root.innerHTML; // HTML 반환
|
||||
}
|
||||
return content; // 이미 HTML일 경우 그대로 반환
|
||||
} catch (error) {
|
||||
console.error('콘텐츠 변환 오류:', error);
|
||||
return content; // 오류 발생 시 원본 반환
|
||||
}
|
||||
},
|
||||
// Delta 타입을 JSON 문자열로 변환
|
||||
deltaAsJson(content) {
|
||||
if (content && content.ops) {
|
||||
return JSON.stringify(content.ops); // Delta 객체에서 ops 속성만 JSON 문자열로 변환
|
||||
}
|
||||
console.error('잘못된 Delta 객체:', content);
|
||||
return null; // Delta 객체가 아니거나 ops가 없을 경우 null 반환
|
||||
}
|
||||
// JSON 문자열로 Delta 타입을 변환
|
||||
contentToHtml(content) {
|
||||
try {
|
||||
if (content.startsWith('{') || content.startsWith('[')) {
|
||||
// Delta 형식으로 변환
|
||||
const delta = JSON.parse(content);
|
||||
const quill = new Quill(document.createElement('div'));
|
||||
quill.setContents(delta);
|
||||
return quill.root.innerHTML; // HTML 반환
|
||||
}
|
||||
return content; // 이미 HTML일 경우 그대로 반환
|
||||
} catch (error) {
|
||||
console.error('콘텐츠 변환 오류:', error);
|
||||
return content; // 오류 발생 시 원본 반환
|
||||
}
|
||||
},
|
||||
// Delta 타입을 JSON 문자열로 변환
|
||||
deltaAsJson(content) {
|
||||
if (content && content.ops) {
|
||||
return JSON.stringify(content.ops); // Delta 객체에서 ops 속성만 JSON 문자열로 변환
|
||||
}
|
||||
console.error('잘못된 Delta 객체:', content);
|
||||
return null; // Delta 객체가 아니거나 ops가 없을 경우 null 반환
|
||||
},
|
||||
|
||||
}
|
||||
/**
|
||||
* Date 타입 문자열 포멧팅
|
||||
*
|
||||
* @param {string} dateStr
|
||||
* @return
|
||||
* 1. Date type 인 경우 예시 '25-02-24 12:02'
|
||||
* 2. Date type 이 아닌 경우 입력값 리턴
|
||||
*
|
||||
*/
|
||||
dateFormatter(dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
const dateCheck = date.getTime();
|
||||
|
||||
if (isNaN(dateCheck)) {
|
||||
return dateStr;
|
||||
} else {
|
||||
const { year, month, day, hours, minutes } = this.formatDateTime(date);
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
}
|
||||
},
|
||||
|
||||
formatDateTime(date) {
|
||||
const zeroFormat = num => (num < 10 ? `0${num}` : num);
|
||||
|
||||
return {
|
||||
year: date.getFullYear(),
|
||||
month: zeroFormat(date.getMonth() + 1),
|
||||
day: zeroFormat(date.getDate()),
|
||||
hours: zeroFormat(date.getHours()),
|
||||
minutes: zeroFormat(date.getMinutes()),
|
||||
seconds: zeroFormat(date.getSeconds()),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
install(app) {
|
||||
app.config.globalProperties.$common = common;
|
||||
}
|
||||
install(app) {
|
||||
app.config.globalProperties.$common = common;
|
||||
app.provide('common', common);
|
||||
},
|
||||
};
|
||||
|
||||
@ -2,18 +2,20 @@
|
||||
<div>
|
||||
<BoardProfile
|
||||
:unknown="unknown"
|
||||
:isCommentAuthor="isCommentAuthor"
|
||||
:boardId="comment.boardId"
|
||||
:profileName="comment.author"
|
||||
:date="comment.createdAt"
|
||||
:comment="comment"
|
||||
:showDetail="false"
|
||||
:author="true"
|
||||
:isLike="!isLike"
|
||||
:isCommentPassword="comment.isCommentPassword"
|
||||
:isCommentProfile="true"
|
||||
@editClick="$emit('editClick', comment)"
|
||||
@deleteClick="$emit('deleteClick', comment)"
|
||||
@updateReaction="handleUpdateReaction"
|
||||
/>
|
||||
/>
|
||||
<!-- :author="true" -->
|
||||
<!-- 댓글 비밀번호 입력창 (익명일 경우) -->
|
||||
<div v-if="isCommentPassword && unknown" class="mt-3 w-25 ms-auto">
|
||||
<div class="input-group">
|
||||
@ -27,7 +29,9 @@
|
||||
</div>
|
||||
<span v-if="passwordCommentAlert" class="invalid-feedback d-block text-start">{{ passwordCommentAlert }}</span>
|
||||
</div>
|
||||
|
||||
<p>authorId:{{ comment.authorId }}</p>
|
||||
<p>코멘트 비교: {{comment.isCommentAuthor}}</p>
|
||||
|
||||
|
||||
<div class="mt-6">
|
||||
<template v-if="comment.isEditTextarea">
|
||||
@ -44,7 +48,7 @@
|
||||
|
||||
<PlusButton v-if="isPlusButton" @click="toggleComment" class="mt-6"/>
|
||||
<BoardCommentArea v-if="isComment" @submitComment="submitComment"/>
|
||||
|
||||
|
||||
<!-- 대댓글 -->
|
||||
<ul v-if="comment.children && comment.children.length" class="list-unstyled">
|
||||
<li
|
||||
@ -52,12 +56,12 @@
|
||||
:key="child.commentId"
|
||||
class="mt-8 pt-6 ps-10 border-top"
|
||||
>
|
||||
<BoardComment
|
||||
<BoardComment
|
||||
:comment="child"
|
||||
:unknown="unknown"
|
||||
:isPlusButton="false"
|
||||
:isPlusButton="false"
|
||||
:isLike="true"
|
||||
@submitComment="submitComment"
|
||||
@submitComment="submitComment"
|
||||
@updateReaction="handleUpdateReaction"
|
||||
/>
|
||||
</li>
|
||||
@ -77,9 +81,13 @@ const props = defineProps({
|
||||
required: true,
|
||||
},
|
||||
unknown: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isCommentAuthor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isPlusButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@ -133,7 +141,7 @@ const handleUpdateReaction = (reactionData) => {
|
||||
// 비밀번호 확인
|
||||
const logPasswordAndEmit = () => {
|
||||
emit('submitPassword', props.comment, password.value);
|
||||
password.value = "";
|
||||
password.value = "";
|
||||
};
|
||||
|
||||
watch(() => props.comment.isEditTextarea, (newVal) => {
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
class="form-control flex-grow-1"
|
||||
v-model="password"
|
||||
/>
|
||||
<!-- <span v-if="passwordAlert" class="invalid-feedback d-block text-start">{{ passwordAlert }}</span> -->
|
||||
<span v-if="passwordAlert" class="invalid-feedback d-block text-start ms-2">{{ passwordAlert }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -70,6 +70,10 @@ const props = defineProps({
|
||||
parentId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
passwordAlert: {
|
||||
type: String,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
<BoardComment
|
||||
:unknown="unknown"
|
||||
:comment="comment"
|
||||
:isCommentAuthor="comment.isCommentAuthor"
|
||||
:isCommentPassword="comment.isCommentPassword"
|
||||
:isEditTextarea="comment.isEditTextarea"
|
||||
:passwordCommentAlert="passwordCommentAlert"
|
||||
@ -38,6 +39,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
isCommentAuthor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isCommentPassword: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
@ -52,7 +57,7 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'submitPassword', 'clearPassword']);
|
||||
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'clearPassword']);
|
||||
|
||||
const submitComment = (replyData) => {
|
||||
emit('submitComment', replyData);
|
||||
|
||||
@ -22,7 +22,8 @@
|
||||
<!-- 버튼 영역 -->
|
||||
<div class="ms-auto text-end">
|
||||
<!-- 수정, 삭제 버튼 -->
|
||||
<template v-if="author || showDetail">
|
||||
<!-- <template v-if="isAuthor || showDetail"> -->
|
||||
<template v-if="isCommentProfile ? isCommentAuthor : isAuthor">
|
||||
<EditButton @click.stop="editClick" />
|
||||
<DeleteButton @click.stop="deleteClick" />
|
||||
</template>
|
||||
@ -49,7 +50,7 @@ import BoardRecommendBtn from '../button/BoardRecommendBtn.vue';
|
||||
const props = defineProps({
|
||||
comment: {
|
||||
type: Object,
|
||||
required: true,
|
||||
required: false,
|
||||
},
|
||||
boardId: {
|
||||
type: Number,
|
||||
@ -61,7 +62,11 @@ const props = defineProps({
|
||||
},
|
||||
profileName: {
|
||||
type: String,
|
||||
<<<<<<< HEAD
|
||||
default: '익명',
|
||||
=======
|
||||
default: '',
|
||||
>>>>>>> board-comment
|
||||
},
|
||||
unknown: {
|
||||
type: Boolean,
|
||||
@ -72,10 +77,12 @@ const props = defineProps({
|
||||
default: true,
|
||||
},
|
||||
// 게시글의 작성자 여부를 확인 : 현재 로그인한 사용자가 이 게시글의 작성자인지 여부
|
||||
author: {
|
||||
isAuthor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
isCommentAuthor: Boolean, // 댓글 작성자인지 여부
|
||||
isCommentProfile: Boolean, // 현재 컴포넌트가 댓글용인지 여부
|
||||
date: {
|
||||
type: String,
|
||||
required: true,
|
||||
@ -98,6 +105,7 @@ const emit = defineEmits(['updateReaction', 'editClick', 'deleteClick']);
|
||||
|
||||
// 수정
|
||||
const editClick = () => {
|
||||
console.log('클릭 확인')
|
||||
emit('editClick', props.unknown);
|
||||
};
|
||||
|
||||
|
||||
@ -24,14 +24,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import TheTop from './TheTop.vue';
|
||||
import TheFooter from './TheFooter.vue';
|
||||
import TheMenu from './TheMenu.vue';
|
||||
import { nextTick } from 'vue';
|
||||
import { wait } from '@/common/utils';
|
||||
import TheTop from './TheTop.vue';
|
||||
import TheFooter from './TheFooter.vue';
|
||||
import TheMenu from './TheMenu.vue';
|
||||
import { nextTick } from 'vue';
|
||||
import { wait } from '@/common/utils';
|
||||
|
||||
window.isDarkStyle = window.Helpers.isDarkStyle();
|
||||
window.isDarkStyle = window.Helpers.isDarkStyle();
|
||||
|
||||
<<<<<<< HEAD
|
||||
const loadScript = src => {
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
@ -44,6 +45,25 @@ nextTick(async () => {
|
||||
loadScript('/vendor/js/menu.js');
|
||||
// loadScript('/js/main.js');
|
||||
});
|
||||
=======
|
||||
const loadScript = src => {
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
script.type = 'text/javascript';
|
||||
script.async = true;
|
||||
document.body.appendChild(script);
|
||||
// script.onload = () => {
|
||||
// console.log(`${src} loaded successfully.`);
|
||||
// };
|
||||
script.onerror = () => {
|
||||
console.error(`Failed to load script: ${src}`);
|
||||
};
|
||||
};
|
||||
nextTick(async () => {
|
||||
await wait(200);
|
||||
loadScript('/vendor/js/menu.js');
|
||||
loadScript('/js/main.js');
|
||||
});
|
||||
>>>>>>> board-comment
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
||||
<style></style>
|
||||
|
||||
@ -10,14 +10,14 @@
|
||||
:boardId="currentBoardId"
|
||||
:profileName="profileName"
|
||||
:unknown="unknown"
|
||||
:author="isAuthor"
|
||||
:views="views"
|
||||
:commentNum="commentNum"
|
||||
:date="formattedBoardDate"
|
||||
:isLike="false"
|
||||
:isAuthor="isAuthor"
|
||||
@editClick="editClick"
|
||||
@deleteClick="deleteClick"
|
||||
/>
|
||||
/>
|
||||
|
||||
<!-- 비밀번호 입력창 (익명일 경우) -->
|
||||
<div v-if="isPassword && unknown" class="mt-3 w-25 ms-auto">
|
||||
@ -74,8 +74,14 @@
|
||||
:likeClicked="likeClicked"
|
||||
:dislikeClicked="dislikeClicked"
|
||||
@updateReaction="handleUpdateReaction"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
<p>현재 로그인한 사용자 ID: {{ currentUserId }}</p>
|
||||
<p>게시글 작성자: {{ authorId }}</p>
|
||||
<p>isAuthor 값: {{ isAuthor }}</p>
|
||||
<!-- <p>use이미지:{{userStore.user.img}}</p> -->
|
||||
<!-- <img :src="`http://localhost:10325/upload/img/profile/${userStore.user.profile}`" alt="Profile Image" class="w-px-40 h-auto rounded-circle"/> -->
|
||||
|
||||
|
||||
<!-- 첨부파일 목록 -->
|
||||
<!-- <ul v-if="attachments.length" class="attachments mt-4 list-unstyled">
|
||||
@ -88,6 +94,7 @@
|
||||
<BoardCommentArea
|
||||
:profileName="profileName"
|
||||
:unknown="unknown"
|
||||
:passwordAlert="passwordAlert"
|
||||
@submitComment="handleCommentSubmit"
|
||||
/>
|
||||
<!-- <BoardCommentArea :profileName="profileName" :unknown="unknown" /> -->
|
||||
@ -97,7 +104,7 @@
|
||||
<div class="card-footer">
|
||||
<BoardCommentList
|
||||
:unknown="unknown"
|
||||
:comments="comments"
|
||||
:comments="commentsWithAuthStatus"
|
||||
:isCommentPassword="isCommentPassword"
|
||||
:isEditTextarea="isEditTextarea"
|
||||
:passwordCommentAlert="passwordCommentAlert"
|
||||
@ -130,6 +137,7 @@ import BoardRecommendBtn from '@c/button/BoardRecommendBtn.vue';
|
||||
import Pagination from '@c/pagination/Pagination.vue';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
||||
import axios from '@api';
|
||||
|
||||
// 게시물 데이터 상태
|
||||
@ -148,12 +156,28 @@ const comments = ref([]);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const userStore = useUserInfoStore();
|
||||
const currentBoardId = ref(Number(route.params.id));
|
||||
<<<<<<< HEAD
|
||||
const unknown = computed(() => profileName.value === '익명');
|
||||
const currentUserId = ref('김자바'); // 현재 로그인한 사용자 id
|
||||
const authorId = ref(null); // 작성자 id
|
||||
=======
|
||||
// const unknown = computed(() => profileName.value === '익명');
|
||||
const currentUserId = computed(() => userStore.user.id); // 현재 로그인한 사용자 id
|
||||
const authorId = ref(''); // 작성자 id
|
||||
>>>>>>> board-comment
|
||||
|
||||
const isAuthor = computed(() => currentUserId.value === authorId.value);
|
||||
// const isCommentAuthor =
|
||||
const commentsWithAuthStatus = computed(() => {
|
||||
const updatedComments = comments.value.map(comment => ({
|
||||
...comment,
|
||||
isCommentAuthor: comment.authorId === currentUserId.value
|
||||
}));
|
||||
// console.log("✅ commentsWithAuthStatus 업데이트됨:", updatedComments);
|
||||
return updatedComments;
|
||||
});
|
||||
|
||||
|
||||
const password = ref('');
|
||||
@ -187,9 +211,12 @@ const fetchBoardDetails = async () => {
|
||||
const response = await axios.get(`board/${currentBoardId.value}`);
|
||||
const data = response.data.data;
|
||||
|
||||
console.log(data)
|
||||
|
||||
// API 응답 데이터 반영
|
||||
// const boardDetail = data.boardDetail || {};
|
||||
|
||||
<<<<<<< HEAD
|
||||
profileName.value = data.author || '익명';
|
||||
|
||||
// 익명확인하고 싶을때
|
||||
@ -198,6 +225,15 @@ const fetchBoardDetails = async () => {
|
||||
// 게시글의 작성자 여부를 확인 : 현재 로그인한 사용자가 이 게시글의 작성자인지 여부
|
||||
authorId.value = data.author;
|
||||
|
||||
=======
|
||||
profileName.value = data.author;
|
||||
|
||||
// 익명확인하고 싶을때
|
||||
// profileName.value = '익명';
|
||||
|
||||
|
||||
authorId.value = data.authorId; //게시글 작성자 id
|
||||
>>>>>>> board-comment
|
||||
boardTitle.value = data.title || '제목 없음';
|
||||
boardContent.value = data.content || '';
|
||||
date.value = data.date || '';
|
||||
@ -262,7 +298,7 @@ const handleCommentReaction = async ({ boardId, commentId, isLike, isDislike })
|
||||
}
|
||||
};
|
||||
|
||||
// 댓글 목록 조회 (대댓글 포함)
|
||||
// 댓글 목록 조회
|
||||
const fetchComments = async (page = 1) => {
|
||||
try {
|
||||
// 댓글
|
||||
@ -273,11 +309,20 @@ const fetchComments = async (page = 1) => {
|
||||
}
|
||||
});
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
console.log(response.data.data)
|
||||
|
||||
>>>>>>> board-comment
|
||||
const commentsList = response.data.data.list.map(comment => ({
|
||||
commentId: comment.LOCCMTSEQ, // 댓글 ID
|
||||
boardId: comment.LOCBRDSEQ,
|
||||
parentId: comment.LOCCMTPNT, // 부모 ID
|
||||
author: comment.author || '익명',
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
authorId: comment.authorId,
|
||||
>>>>>>> board-comment
|
||||
content: comment.LOCCMTRPY,
|
||||
likeCount: comment.likeCount || 0,
|
||||
dislikeCount: comment.dislikeCount || 0,
|
||||
@ -286,8 +331,6 @@ const fetchComments = async (page = 1) => {
|
||||
createdAtRaw: new Date(comment.LOCCMTRDT), // 정렬용
|
||||
createdAt: formattedDate(comment.LOCCMTRDT), // 표시용
|
||||
children: [], // 대댓글을 담을 배열
|
||||
// isCommentPassword: false, // 개별 댓글 비밀번호 입력 상태
|
||||
// isEditTextarea: false // 개별 댓글 수정 상태
|
||||
}));
|
||||
|
||||
for (const comment of commentsList) {
|
||||
@ -297,7 +340,7 @@ const fetchComments = async (page = 1) => {
|
||||
params: { LOCCMTPNT: comment.commentId }
|
||||
});
|
||||
|
||||
// console.log(`대댓글 데이터 (${comment.commentId}의 대댓글):`, replyResponse.data);
|
||||
console.log(`대댓글 데이터 (${comment.commentId}의 대댓글):`, replyResponse.data);
|
||||
|
||||
if (replyResponse.data.data) {
|
||||
comment.children = replyResponse.data.data.map(reply => ({
|
||||
@ -343,21 +386,22 @@ const fetchComments = async (page = 1) => {
|
||||
console.log('댓글 목록 불러오기 오류:', error);
|
||||
}
|
||||
};
|
||||
const isSubmitting = ref(false);
|
||||
// const isSubmitting = ref(false);
|
||||
|
||||
// 댓글 작성
|
||||
const handleCommentSubmit = async ({ comment, password }) => {
|
||||
// if (unknown.value && !password) {
|
||||
// passwordAlert.value = "익명 사용자는 비밀번호를 입력해야 합니다."; // UI에 메시지 표시
|
||||
// return;
|
||||
// }
|
||||
console.log('댓글')
|
||||
//비밀번호 입력 안했을시
|
||||
// if (!password) {
|
||||
// passwordAlert.value = "비밀번호를 입력해야 합니다."; // UI에서 경고 표시
|
||||
// passwordAlert.value = "비밀번호를 입력해야 합니다.";
|
||||
// return;
|
||||
// }
|
||||
|
||||
// console.log('비밀번호 눌렀음')
|
||||
|
||||
// 중복 실행 방지
|
||||
if (isSubmitting.value) return;
|
||||
isSubmitting.value = true;
|
||||
// if (isSubmitting.value) return;
|
||||
// isSubmitting.value = true;
|
||||
|
||||
try {
|
||||
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
|
||||
@ -368,7 +412,7 @@ const handleCommentSubmit = async ({ comment, password }) => {
|
||||
});
|
||||
|
||||
if (response.status === 200) {
|
||||
// console.log('댓글 작성 성공:', response.data.message);
|
||||
console.log('댓글 작성 성공:', response.data.message);
|
||||
await fetchComments();
|
||||
} else {
|
||||
console.log('댓글 작성 실패:', response.data.message);
|
||||
@ -439,19 +483,28 @@ const deleteClick = (unknown) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 댓글 수정 버튼 클릭(대댓글 포함)
|
||||
// 댓글 수정 버튼 클릭
|
||||
const editComment = (comment) => {
|
||||
if (comment.isEditTextarea) {
|
||||
// 이미 수정창이 열려 있으면 닫기
|
||||
comment.isEditTextarea = false;
|
||||
const targetComment = comments.value.find(c => c.commentId === comment.commentId);
|
||||
|
||||
if (!targetComment) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 수정 text창 열림, 닫힘 유무
|
||||
if (targetComment.isEditTextarea) {
|
||||
targetComment.isEditTextarea = false;
|
||||
} else {
|
||||
targetComment.isEditTextarea = true;
|
||||
}
|
||||
|
||||
// 익명일때
|
||||
if (unknown.value) {
|
||||
toggleCommentPassword(comment, "edit");
|
||||
} else {
|
||||
comment.isEditTextarea = true;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
// comments.value.forEach(c => {
|
||||
// c.isEditTextarea = false;
|
||||
@ -463,10 +516,13 @@ const editComment = (comment) => {
|
||||
// } else {
|
||||
// comment.isEditTextarea = true;
|
||||
// }
|
||||
=======
|
||||
>>>>>>> board-comment
|
||||
}
|
||||
|
||||
// 댓글 삭제 버튼 클릭(대댓글 포함)
|
||||
const deleteComment = (comment) => {
|
||||
// 댓글 삭제 버튼 클릭
|
||||
const deleteComment = async (comment) => {
|
||||
// 익명 사용자인 경우
|
||||
if (unknown.value) {
|
||||
if (comment.isEditTextarea) {
|
||||
// 현재 수정 중이라면 수정 모드를 끄고, 삭제 비밀번호 입력창을 띄움
|
||||
@ -477,8 +533,8 @@ const deleteComment = (comment) => {
|
||||
toggleCommentPassword(comment, "delete");
|
||||
}
|
||||
} else {
|
||||
// 로그인 사용자 바로 삭제
|
||||
comments.value = comments.value.filter(c => c.commentId !== comment.commentId);
|
||||
// 로그인 사용자인 경우 (바로 삭제)
|
||||
deleteReplyComment(comment)
|
||||
}
|
||||
};
|
||||
|
||||
@ -531,7 +587,7 @@ const submitPassword = async () => {
|
||||
}
|
||||
lastClickedButton.value = null;
|
||||
} else {
|
||||
passwordAlert.value = "비밀번호가 일치하지 않습니다.????";
|
||||
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
|
||||
}
|
||||
} catch (error) {
|
||||
// console.log("📌 전체 오류:", error);
|
||||
@ -552,6 +608,7 @@ const submitPassword = async () => {
|
||||
|
||||
// 댓글 삭제 (비밀번호 확인 후)
|
||||
const submitCommentPassword = async (comment, password) => {
|
||||
|
||||
if (!password) {
|
||||
passwordCommentAlert.value = "비밀번호를 입력해주세요.";
|
||||
return;
|
||||
@ -564,12 +621,19 @@ const submitCommentPassword = async (comment, password) => {
|
||||
});
|
||||
|
||||
if (response.data.code === 200 && response.data.data === true) {
|
||||
passwordCommentAlert.value = "";
|
||||
comment.isCommentPassword = false;
|
||||
|
||||
if (lastCommentClickedButton.value === "edit") {
|
||||
comment.isEditTextarea = true;
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
passwordCommentAlert.value = "";
|
||||
|
||||
>>>>>>> board-comment
|
||||
// handleSubmitEdit(comment, comment.content);
|
||||
} else if (lastCommentClickedButton.value === "delete") {
|
||||
passwordCommentAlert.value = "";
|
||||
|
||||
deleteReplyComment(comment)
|
||||
}
|
||||
@ -631,7 +695,7 @@ const deleteReplyComment = async (comment) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 댓글 수정 확인 (대댓글 포함)
|
||||
// 댓글 수정 확인
|
||||
const handleSubmitEdit = async (comment, editedContent) => {
|
||||
try {
|
||||
const response = await axios.put(`board/comment/${comment.commentId}`, {
|
||||
@ -640,8 +704,19 @@ const handleSubmitEdit = async (comment, editedContent) => {
|
||||
});
|
||||
|
||||
// 수정 성공 시 업데이트
|
||||
<<<<<<< HEAD
|
||||
comment.content = editedContent;
|
||||
comment.isEditTextarea = false;
|
||||
=======
|
||||
// comment.content = editedContent;
|
||||
// comment.isEditTextarea = false;
|
||||
if (response.status === 200) {
|
||||
// 댓글 목록 새로고침
|
||||
await fetchComments();
|
||||
} else {
|
||||
console.log("❌ 댓글 수정 실패:", response.data);
|
||||
}
|
||||
>>>>>>> board-comment
|
||||
} catch (error) {
|
||||
console.error("댓글 수정 중 오류 발생:", error);
|
||||
}
|
||||
@ -650,7 +725,16 @@ const handleSubmitEdit = async (comment, editedContent) => {
|
||||
// 댓글 수정 취소 (대댓글 포함)
|
||||
const handleCancelEdit = (comment) => {
|
||||
console.log("BoardView.vue - 댓글 수정 취소:", comment);
|
||||
comment.isEditTextarea = false;
|
||||
|
||||
// 원본 comments 배열에서 동일한 comment 찾기
|
||||
const targetComment = comments.value.find(c => c.commentId === comment.commentId);
|
||||
|
||||
if (targetComment) {
|
||||
console.log("✅ 원본 데이터 찾음, 수정 취소 처리 가능");
|
||||
targetComment.isEditTextarea = false;
|
||||
} else {
|
||||
console.error("❌ 원본 데이터 찾을 수 없음, 수정 취소 실패");
|
||||
}
|
||||
};
|
||||
|
||||
// 페이지 변경
|
||||
|
||||
Loading…
Reference in New Issue
Block a user