Merge branch 'main' into login
This commit is contained in:
commit
6a7ed19d54
@ -3,22 +3,33 @@
|
|||||||
|
|
||||||
/* 휴가 */
|
/* 휴가 */
|
||||||
|
|
||||||
.fc-daygrid-day-events {
|
|
||||||
max-height: 100px !important;
|
|
||||||
overflow-y: auto !important;
|
|
||||||
}
|
|
||||||
/* 이벤트 선 없게 */
|
/* 이벤트 선 없게 */
|
||||||
.fc-event {
|
.fc-event {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
/* 오전전반차 그래프 */
|
/* 오전 반차 그래프 (왼쪽 절반) */
|
||||||
.fc-daygrid-event.half-day-am {
|
.fc-daygrid-event.half-day-am {
|
||||||
width: calc(50% - 4px) !important;
|
width: 50% !important;
|
||||||
|
height: 8px !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
font-size: 0px !important;
|
||||||
}
|
}
|
||||||
/* 오후반차 그래프프 */
|
/* 오후 반차 그래프 (오른쪽 절반) */
|
||||||
.fc-daygrid-event.half-day-pm {
|
.fc-daygrid-event.half-day-pm {
|
||||||
width: calc(50% - 4px) !important;
|
width: 50% !important;
|
||||||
margin-left: auto !important
|
height: 8px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
font-size: 0px !important;
|
||||||
|
}
|
||||||
|
/* 연차 그래프 (풀풀) */
|
||||||
|
.fc-daygrid-event.full-day {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 8px !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
border-radius: 2px !important;
|
||||||
|
font-size: 0px !important;
|
||||||
}
|
}
|
||||||
/* 공휴일,일요일 색상 */
|
/* 공휴일,일요일 색상 */
|
||||||
.fc-day-sun .fc-daygrid-day-number,
|
.fc-day-sun .fc-daygrid-day-number,
|
||||||
@ -84,7 +95,6 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
|
|
||||||
|
|
||||||
/* 본인 모달 */
|
/* 본인 모달 */
|
||||||
|
|
||||||
/* 닫기 버튼 */
|
/* 닫기 버튼 */
|
||||||
.close-btn {
|
.close-btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -109,7 +119,6 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
|
|
||||||
|
|
||||||
/* 선물하기 모달 */
|
/* 선물하기 모달 */
|
||||||
|
|
||||||
/* 연차 개수 버튼 */
|
/* 연차 개수 버튼 */
|
||||||
.count-btn {
|
.count-btn {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@ -127,7 +136,6 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
background: #cccccc;
|
background: #cccccc;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 버튼 컨테이너 (우측 정렬) */
|
/* 버튼 컨테이너 (우측 정렬) */
|
||||||
.custom-button-container {
|
.custom-button-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -141,18 +149,17 @@ opacity: 0.6; /* 흐려 보이게 */
|
|||||||
padding: 10px; /* 크기 조정 */
|
padding: 10px; /* 크기 조정 */
|
||||||
cursor: pointer; /* 클릭 가능하도록 변경 */
|
cursor: pointer; /* 클릭 가능하도록 변경 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 아이콘 색상 변경 (기본) */
|
/* 아이콘 색상 변경 (기본) */
|
||||||
.custom-button i {
|
.custom-button i {
|
||||||
color: #282538; /* 기본 아이콘 색상 */
|
color: #282538; /* 기본 아이콘 색상 */
|
||||||
font-size: 25px; /* 아이콘 크기 */
|
font-size: 25px; /* 아이콘 크기 */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 버튼 호버 효과 */
|
/* 버튼 호버 효과 */
|
||||||
.custom-button:hover i {
|
.custom-button:hover i {
|
||||||
color: #ff0800; /* 호버 시 아이콘 색상 변경 */
|
color: #ff0800; /* 호버 시 아이콘 색상 변경 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.grayscaleImg {
|
.grayscaleImg {
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,35 +11,29 @@
|
|||||||
:isLike="!isLike"
|
:isLike="!isLike"
|
||||||
:isCommentPassword="isCommentPassword"
|
:isCommentPassword="isCommentPassword"
|
||||||
:isCommentProfile="true"
|
:isCommentProfile="true"
|
||||||
@editClick="aaaa"
|
@editClick="handleEditClick"
|
||||||
@deleteClick="$emit('deleteClick', comment)"
|
@deleteClick="$emit('deleteClick', comment)"
|
||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
/>
|
/>
|
||||||
<!-- <P>Commentpassssss: {{isCommentPassword}}</P> -->
|
|
||||||
<!-- :author="true" -->
|
|
||||||
<!-- 댓글 비밀번호 입력창 (익명일 경우) -->
|
<!-- 댓글 비밀번호 입력창 (익명일 경우) -->
|
||||||
<div v-if="isCommentPassword === comment.commentId && unknown" class="mt-3 w-25 ms-auto">
|
<div v-if="currentPasswordCommentId === comment.commentId && unknown" class="mt-3 w-25 ms-auto">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
v-model="password"
|
:value="password"
|
||||||
placeholder="비밀번호 입력"
|
placeholder="비밀번호 입력"
|
||||||
|
@input="$emit('update:password', $event.target.value.trim())"
|
||||||
/>
|
/>
|
||||||
<button class="btn btn-primary" @click="logPasswordAndEmit">확인</button>
|
<button class="btn btn-primary" @click="logPasswordAndEmit">확인</button>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="passwordCommentAlert" class="invalid-feedback d-block text-start">{{ passwordCommentAlert }}</span>
|
<span v-if="passwordCommentAlert" class="invalid-feedback d-block text-start">{{ passwordCommentAlert }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- <p>authorId:{{ comment.authorId }}</p>
|
|
||||||
<p>코멘트 비교: {{comment.isCommentAuthor}}</p> -->
|
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<template v-if="comment.isEditTextarea">
|
<template v-if="comment.isEditTextarea">
|
||||||
<textarea v-model="localEditedContent" class="form-control"></textarea>
|
<textarea v-model="localEditedContent" class="form-control"></textarea>
|
||||||
<div class="mt-2 d-flex justify-content-end">
|
<div class="mt-2 d-flex justify-content-end">
|
||||||
<!-- <button class="btn btn-secondary me-2" @click="$emit('cancelEdit', comment)">취소</button> -->
|
|
||||||
<!-- <button class="btn btn-primary" @click="submitEdit">수정</button> -->
|
|
||||||
<SaveBtn class="btn btn-primary" @click="submitEdit"></SaveBtn>
|
<SaveBtn class="btn btn-primary" @click="submitEdit"></SaveBtn>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -58,10 +52,6 @@
|
|||||||
:key="child.commentId"
|
:key="child.commentId"
|
||||||
class="mt-8 pt-6 ps-10 border-top"
|
class="mt-8 pt-6 ps-10 border-top"
|
||||||
>
|
>
|
||||||
<!-- <p>대댓글 데이터(JSON): {{ JSON.stringify(child, null, 2) }}</p> -->
|
|
||||||
<!-- <p>comment child: {{ comment.children }}</p> -->
|
|
||||||
<!-- :unknown="child.author === '익명'" -->
|
|
||||||
<p>child.isCommentPassword: {{ child.isCommentPassword }}</p>
|
|
||||||
<BoardComment
|
<BoardComment
|
||||||
:comment="child"
|
:comment="child"
|
||||||
:unknown="child.author === '익명'"
|
:unknown="child.author === '익명'"
|
||||||
@ -70,12 +60,17 @@
|
|||||||
:isCommentProfile="true"
|
:isCommentProfile="true"
|
||||||
:isCommentAuthor="child.isCommentAuthor"
|
:isCommentAuthor="child.isCommentAuthor"
|
||||||
:isCommentPassword="isCommentPassword"
|
:isCommentPassword="isCommentPassword"
|
||||||
@editClick="$emit('editClick', $event)"
|
:currentPasswordCommentId="currentPasswordCommentId"
|
||||||
|
:passwordCommentAlert="passwordCommentAlert"
|
||||||
|
:password="password"
|
||||||
|
@editClick="handleReplyEditClick"
|
||||||
@deleteClick="$emit('deleteClick', child)"
|
@deleteClick="$emit('deleteClick', child)"
|
||||||
@submitEdit="(comment, editedContent) => $emit('submitEdit', comment, editedContent)"
|
@submitEdit="(comment, editedContent) => $emit('submitEdit', comment, editedContent)"
|
||||||
@cancelEdit="$emit('cancelEdit', child)"
|
@cancelEdit="$emit('cancelEdit', child)"
|
||||||
@submitComment="submitComment"
|
@submitComment="submitComment"
|
||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
|
@submitPassword="$emit('submitPassword', child, password)"
|
||||||
|
@update:password="$emit('update:password', $event)"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -120,14 +115,19 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
passwordCommentAlert: {
|
passwordCommentAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: false
|
default: ''
|
||||||
}
|
},
|
||||||
|
currentPasswordCommentId: {
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
password:{
|
||||||
|
type: String
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// emits 정의
|
// emits 정의
|
||||||
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'submitEdit', 'cancelEdit']);
|
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'submitEdit', 'cancelEdit', 'update:password']);
|
||||||
|
|
||||||
const password = ref('');
|
|
||||||
const localEditedContent = ref(props.comment.content);
|
const localEditedContent = ref(props.comment.content);
|
||||||
|
|
||||||
// 댓글 입력 창 토글
|
// 댓글 입력 창 토글
|
||||||
@ -154,8 +154,8 @@ const handleUpdateReaction = (reactionData) => {
|
|||||||
|
|
||||||
// 비밀번호 확인
|
// 비밀번호 확인
|
||||||
const logPasswordAndEmit = () => {
|
const logPasswordAndEmit = () => {
|
||||||
emit('submitPassword', props.comment, password.value);
|
console.log('비밀번호 확인',props.password)
|
||||||
password.value = "";
|
emit('submitPassword', props.comment, props.password);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.comment.isEditTextarea, (newVal) => {
|
watch(() => props.comment.isEditTextarea, (newVal) => {
|
||||||
@ -169,8 +169,12 @@ const submitEdit = () => {
|
|||||||
emit('submitEdit', props.comment, localEditedContent.value);
|
emit('submitEdit', props.comment, localEditedContent.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const aaaa = () => {
|
const handleEditClick = () => {
|
||||||
emit('editClick', props.comment);
|
emit('editClick', props.comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleReplyEditClick = (comment) => {
|
||||||
|
emit('editClick', comment);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -51,10 +51,7 @@
|
|||||||
|
|
||||||
<!-- 답변 쓰기 버튼 -->
|
<!-- 답변 쓰기 버튼 -->
|
||||||
<div class="ms-auto mt-3 mt-md-0">
|
<div class="ms-auto mt-3 mt-md-0">
|
||||||
<button class="btn btn-primary" @click="handleCommentSubmit">
|
<SaveBtn class="btn btn-primary" @click="handleCommentSubmit"></SaveBtn>
|
||||||
<!-- <i class="icon-base bx bx-check"></i> -->
|
|
||||||
확인
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -63,6 +60,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, defineEmits, defineProps, computed, watch } from 'vue';
|
import { ref, defineEmits, defineProps, computed, watch } from 'vue';
|
||||||
|
import SaveBtn from '../button/SaveBtn.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
unknown: {
|
unknown: {
|
||||||
@ -75,11 +73,11 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
passwordAlert: {
|
passwordAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: false
|
default: ''
|
||||||
},
|
},
|
||||||
commentAlert: {
|
commentAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: false
|
default: ''
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -11,15 +11,17 @@
|
|||||||
:isCommentAuthor="comment.isCommentAuthor"
|
:isCommentAuthor="comment.isCommentAuthor"
|
||||||
:isEditTextarea="comment.isEditTextarea"
|
:isEditTextarea="comment.isEditTextarea"
|
||||||
:isCommentPassword="isCommentPassword"
|
:isCommentPassword="isCommentPassword"
|
||||||
:passwordCommentAlert="passwordCommentAlert"
|
:passwordCommentAlert="passwordCommentAlert || ''"
|
||||||
|
:currentPasswordCommentId="currentPasswordCommentId"
|
||||||
|
:password="password"
|
||||||
@editClick="handleEditClick"
|
@editClick="handleEditClick"
|
||||||
@deleteClick="handleDeleteClick"
|
@deleteClick="handleDeleteClick"
|
||||||
@submitPassword="submitPassword"
|
@submitPassword="submitPassword"
|
||||||
@submitComment="submitComment"
|
@submitComment="submitComment"
|
||||||
@commentDeleted="handleCommentDeleted"
|
|
||||||
@submitEdit="handleSubmitEdit"
|
@submitEdit="handleSubmitEdit"
|
||||||
@cancelEdit="handleCancelEdit"
|
@cancelEdit="handleCancelEdit"
|
||||||
@updateReaction="(reactionData) => handleUpdateReaction(reactionData, comment.commentId, comment.boardId)"
|
@updateReaction="(reactionData) => handleUpdateReaction(reactionData, comment.commentId, comment.boardId)"
|
||||||
|
@update:password="updatePassword"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@ -53,11 +55,17 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
passwordCommentAlert: {
|
passwordCommentAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: false
|
default: ''
|
||||||
}
|
},
|
||||||
|
currentPasswordCommentId: {
|
||||||
|
type: Number
|
||||||
|
},
|
||||||
|
password:{
|
||||||
|
type: String
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'clearPassword']);
|
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'clearPassword','submitEdit', 'update:password']);
|
||||||
|
|
||||||
const submitComment = (replyData) => {
|
const submitComment = (replyData) => {
|
||||||
emit('submitComment', replyData);
|
emit('submitComment', replyData);
|
||||||
@ -78,7 +86,11 @@ const submitPassword = (comment, password) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleEditClick = (comment) => {
|
const handleEditClick = (comment) => {
|
||||||
emit('editClick', comment);
|
if (comment.parentId) {
|
||||||
|
emit('editClick', comment); // 대댓글
|
||||||
|
} else {
|
||||||
|
emit('editClick', comment); // 댓글
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitEdit = (comment, editedContent) => {
|
const handleSubmitEdit = (comment, editedContent) => {
|
||||||
@ -100,4 +112,8 @@ const handleCancelEdit = (comment) => {
|
|||||||
emit('cancelEdit', comment); // 댓글 수정 취소
|
emit('cancelEdit', comment); // 댓글 수정 취소
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updatePassword = (newPassword) => {
|
||||||
|
emit('update:password', newPassword);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
<div v-if="!unknown" class="avatar me-2">
|
<div v-if="!unknown" class="avatar me-2">
|
||||||
<img src="/img/avatars/2.png" alt="Avatar" class="rounded-circle" />
|
<img src="/img/avatars/2.png" alt="Avatar" class="rounded-circle" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="me-2">
|
<div class="me-2">
|
||||||
<h6 class="mb-0">{{ profileName }}</h6>
|
<h6 class="mb-0">{{ profileName }}</h6>
|
||||||
<div class="profile-detail">
|
<div class="profile-detail">
|
||||||
@ -81,7 +82,7 @@ const props = defineProps({
|
|||||||
isCommentProfile: Boolean, // 현재 컴포넌트가 댓글용인지 여부
|
isCommentProfile: Boolean, // 현재 컴포넌트가 댓글용인지 여부
|
||||||
date: {
|
date: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: '',
|
||||||
},
|
},
|
||||||
views: {
|
views: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@ -117,6 +118,10 @@ const handleUpdateReaction = (reactionData) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getProfileImage = (profilePath) => {
|
||||||
|
return profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { ref, computed } from 'vue';
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
comment: {
|
comment: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
likeClicked : {
|
likeClicked : {
|
||||||
type : Boolean,
|
type : Boolean,
|
||||||
@ -36,7 +36,7 @@ const props = defineProps({
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
commentId: {
|
commentId: {
|
||||||
type: Number,
|
type: [Number, null],
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
likeCount: {
|
likeCount: {
|
||||||
|
|||||||
@ -28,10 +28,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineEmits, ref, defineProps } from "vue";
|
import { defineEmits, ref, defineProps, watch } from "vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
isDisabled: Boolean
|
isDisabled: Boolean,
|
||||||
|
selectedDate: String // 날짜 선택 값을 props로 받음
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(["toggleHalfDay", "addVacationRequests", "resetHalfDay"]);
|
const emit = defineEmits(["toggleHalfDay", "addVacationRequests", "resetHalfDay"]);
|
||||||
@ -39,15 +40,16 @@ const halfDayType = ref(null);
|
|||||||
|
|
||||||
const toggleHalfDay = (type) => {
|
const toggleHalfDay = (type) => {
|
||||||
halfDayType.value = type;
|
halfDayType.value = type;
|
||||||
|
|
||||||
emit("toggleHalfDay", halfDayType.value);
|
emit("toggleHalfDay", halfDayType.value);
|
||||||
|
|
||||||
// ✅ 버튼 클릭 후 1초 후 자동 비활성화
|
|
||||||
setTimeout(() => {
|
|
||||||
halfDayType.value = null;
|
|
||||||
}, 1000);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// `selectedDate`가 변경되면 반차 선택 초기화
|
||||||
|
watch(() => props.selectedDate, (newDate) => {
|
||||||
|
if (newDate) {
|
||||||
|
resetHalfDay();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 날짜 선택 후 반차 버튼 상태 초기화
|
// 날짜 선택 후 반차 버튼 상태 초기화
|
||||||
const resetHalfDay = () => {
|
const resetHalfDay = () => {
|
||||||
halfDayType.value = null;
|
halfDayType.value = null;
|
||||||
@ -89,20 +91,20 @@ defineExpose({ resetHalfDay });
|
|||||||
|
|
||||||
/* 선택된 (눌린) 버튼 */
|
/* 선택된 (눌린) 버튼 */
|
||||||
.btn.active {
|
.btn.active {
|
||||||
border: 3px solid #fff; /* 흰색 테두리 강조 */
|
border: 3px solid #fff;
|
||||||
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3);
|
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
transform: scale(1.1);
|
transform: scale(1.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* AM 버튼 (선택된 상태) */
|
/* AM 버튼 (선택된 상태) */
|
||||||
.btn-warning.active {
|
.btn-warning.active {
|
||||||
background-color: #ffca2c !important; /* 진한 노란색 */
|
background-color: #ffca2c !important;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PM 버튼 (선택된 상태) */
|
/* PM 버튼 (선택된 상태) */
|
||||||
.btn-info.active {
|
.btn-info.active {
|
||||||
background-color: #0b5ed7 !important; /* 진한 파란색 */
|
background-color: #0b5ed7 !important;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mb-4 row">
|
<div class="mb-4 row">
|
||||||
<label :for="name" class="col-md-2 col-form-label">{{ title }}</label>
|
<label :for="inputId" class="col-md-2 col-form-label">{{ title }}</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<input
|
<input
|
||||||
class="form-control"
|
class="form-control"
|
||||||
type="file"
|
type="file"
|
||||||
:id="name"
|
:id="inputId"
|
||||||
|
ref="fileInput"
|
||||||
@change="changeHandler"
|
@change="changeHandler"
|
||||||
multiple
|
multiple
|
||||||
/>
|
/>
|
||||||
@ -21,7 +22,7 @@ import { ref ,computed} from 'vue';
|
|||||||
import { fileMsg } from '@/common/msgEnum';
|
import { fileMsg } from '@/common/msgEnum';
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const prop = defineProps({
|
const props = defineProps({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '라벨',
|
default: '라벨',
|
||||||
@ -29,7 +30,7 @@ const prop = defineProps({
|
|||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'nameplz',
|
default: 'fileInput',
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
isAlert: {
|
isAlert: {
|
||||||
@ -38,12 +39,13 @@ const prop = defineProps({
|
|||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const inputId = computed(() => props.name || 'defaultFileInput');
|
||||||
|
|
||||||
const emits = defineEmits(['update:data', 'update:isValid']);
|
const emits = defineEmits(['update:data', 'update:isValid']);
|
||||||
|
|
||||||
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
const MAX_TOTAL_SIZE = 5 * 1024 * 1024; // 5MB
|
||||||
const MAX_FILE_COUNT = 5; // 최대 파일 개수
|
const MAX_FILE_COUNT = 5; // 최대 파일 개수
|
||||||
const ALLOWED_FILE_TYPES = ['image/jpeg', 'image/png', 'application/pdf']; // 허용된 파일 유형
|
const ALLOWED_FILE_TYPES = []; // 모든 파일을 허용
|
||||||
|
|
||||||
const showError = ref(false);
|
const showError = ref(false);
|
||||||
const fileMsgKey = ref(''); // 에러 메시지 키
|
const fileMsgKey = ref(''); // 에러 메시지 키
|
||||||
@ -51,9 +53,12 @@ const fileMsgKey = ref(''); // 에러 메시지 키
|
|||||||
const changeHandler = (event) => {
|
const changeHandler = (event) => {
|
||||||
const files = Array.from(event.target.files);
|
const files = Array.from(event.target.files);
|
||||||
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
|
const totalSize = files.reduce((sum, file) => sum + file.size, 0);
|
||||||
const invalidFiles = files.filter(file => !ALLOWED_FILE_TYPES.includes(file.type));
|
|
||||||
|
|
||||||
// 파일 검증 로직
|
// ALLOWED_FILE_TYPES가 비어있으면 모든 파일 허용
|
||||||
|
const invalidFiles = ALLOWED_FILE_TYPES.length > 0
|
||||||
|
? files.filter(file => !ALLOWED_FILE_TYPES.includes(file.type))
|
||||||
|
: [];
|
||||||
|
|
||||||
if (totalSize > MAX_TOTAL_SIZE) {
|
if (totalSize > MAX_TOTAL_SIZE) {
|
||||||
showError.value = true;
|
showError.value = true;
|
||||||
fileMsgKey.value = 'FileMaxSizeMsg';
|
fileMsgKey.value = 'FileMaxSizeMsg';
|
||||||
|
|||||||
@ -195,10 +195,7 @@ const fetchGeneralPosts = async (page = 1) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (data?.data) {
|
if (data?.data) {
|
||||||
// console.log(data)
|
const totalPosts = data.data.total;
|
||||||
const totalPosts = data.data.total; // 전체 게시물 개수 받아오기
|
|
||||||
|
|
||||||
// console.log('📌 API 응답 데이터:', data.data);
|
|
||||||
|
|
||||||
generalList.value = data.data.list.map((post, index) => ({
|
generalList.value = data.data.list.map((post, index) => ({
|
||||||
realId: post.id,
|
realId: post.id,
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
class="form-control"
|
class="form-control"
|
||||||
v-model="password"
|
v-model="password"
|
||||||
placeholder="비밀번호 입력"
|
placeholder="비밀번호 입력"
|
||||||
|
@input="password = password.replace(/\s/g, '')"
|
||||||
/>
|
/>
|
||||||
<button class="btn btn-primary" @click="submitPassword">확인</button>
|
<button class="btn btn-primary" @click="submitPassword">확인</button>
|
||||||
</div>
|
</div>
|
||||||
@ -76,12 +77,6 @@
|
|||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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">
|
<!-- <ul v-if="attachments.length" class="attachments mt-4 list-unstyled">
|
||||||
@ -98,7 +93,6 @@
|
|||||||
:passwordAlert="passwordAlert"
|
:passwordAlert="passwordAlert"
|
||||||
@submitComment="handleCommentSubmit"
|
@submitComment="handleCommentSubmit"
|
||||||
/>
|
/>
|
||||||
<!-- <BoardCommentArea :profileName="profileName" :unknown="unknown" /> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 댓글 목록 -->
|
<!-- 댓글 목록 -->
|
||||||
@ -106,9 +100,11 @@
|
|||||||
<BoardCommentList
|
<BoardCommentList
|
||||||
:unknown="unknown"
|
:unknown="unknown"
|
||||||
:comments="commentsWithAuthStatus"
|
:comments="commentsWithAuthStatus"
|
||||||
:isCommentPassword="Boolean(isCommentPassword)"
|
:isCommentPassword="isCommentPassword"
|
||||||
:isEditTextarea="isEditTextarea"
|
:isEditTextarea="isEditTextarea"
|
||||||
:passwordCommentAlert="passwordCommentAlert"
|
:passwordCommentAlert="passwordCommentAlert"
|
||||||
|
:currentPasswordCommentId="currentPasswordCommentId"
|
||||||
|
:password="password"
|
||||||
@editClick="editComment"
|
@editClick="editComment"
|
||||||
@deleteClick="deleteComment"
|
@deleteClick="deleteComment"
|
||||||
@updateReaction="handleCommentReaction"
|
@updateReaction="handleCommentReaction"
|
||||||
@ -117,6 +113,7 @@
|
|||||||
@commentDeleted="handleCommentDeleted"
|
@commentDeleted="handleCommentDeleted"
|
||||||
@cancelEdit="handleCancelEdit"
|
@cancelEdit="handleCancelEdit"
|
||||||
@submitEdit="handleSubmitEdit"
|
@submitEdit="handleSubmitEdit"
|
||||||
|
@update:password="updatePassword"
|
||||||
/>
|
/>
|
||||||
<Pagination
|
<Pagination
|
||||||
v-if="pagination.pages"
|
v-if="pagination.pages"
|
||||||
@ -173,7 +170,6 @@ const commentsWithAuthStatus = computed(() => {
|
|||||||
isCommentAuthor: reply.authorId === currentUserId.value,
|
isCommentAuthor: reply.authorId === currentUserId.value,
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
// console.log("✅ commentsWithAuthStatus 업데이트됨:", updatedComments);
|
|
||||||
return updatedComments;
|
return updatedComments;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -183,10 +179,15 @@ const passwordAlert = ref("");
|
|||||||
const passwordCommentAlert = ref("");
|
const passwordCommentAlert = ref("");
|
||||||
const isPassword = ref(false);
|
const isPassword = ref(false);
|
||||||
const isCommentPassword = ref(false);
|
const isCommentPassword = ref(false);
|
||||||
|
const currentPasswordCommentId = ref(null);
|
||||||
const lastClickedButton = ref("");
|
const lastClickedButton = ref("");
|
||||||
const lastCommentClickedButton = ref("");
|
const lastCommentClickedButton = ref("");
|
||||||
const isEditTextarea = ref(false);
|
const isEditTextarea = ref(false);
|
||||||
const commentAlert = ref('')
|
const commentAlert = ref('');
|
||||||
|
|
||||||
|
const updatePassword = (newPassword) => {
|
||||||
|
password.value = newPassword;
|
||||||
|
};
|
||||||
|
|
||||||
const pagination = ref({
|
const pagination = ref({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
@ -210,20 +211,12 @@ const fetchBoardDetails = async () => {
|
|||||||
const response = await axios.get(`board/${currentBoardId.value}`);
|
const response = await axios.get(`board/${currentBoardId.value}`);
|
||||||
const data = response.data.data;
|
const data = response.data.data;
|
||||||
|
|
||||||
// console.log(data)
|
|
||||||
|
|
||||||
// API 응답 데이터 반영
|
// API 응답 데이터 반영
|
||||||
// const boardDetail = data.boardDetail || {};
|
// const boardDetail = data.boardDetail || {};
|
||||||
|
|
||||||
profileName.value = data.author || '익명';
|
profileName.value = data.author || '익명';
|
||||||
|
|
||||||
// 익명확인하고 싶을때
|
|
||||||
// profileName.value = '익명';
|
|
||||||
|
|
||||||
// console.log("📌 게시글 작성자:", profileName.value); // 작성자 이름 출력
|
|
||||||
// console.log("🔍 익명 여부 (unknown.value):", unknown.value); // 익명 여부 확인
|
|
||||||
|
|
||||||
|
|
||||||
authorId.value = data.authorId; //게시글 작성자 id
|
authorId.value = data.authorId; //게시글 작성자 id
|
||||||
boardTitle.value = data.title || '제목 없음';
|
boardTitle.value = data.title || '제목 없음';
|
||||||
boardContent.value = data.content || '';
|
boardContent.value = data.content || '';
|
||||||
@ -245,7 +238,6 @@ const handleUpdateReaction = async ({ boardId, commentId, isLike, isDislike }) =
|
|||||||
await axios.post(`/board/${boardId}/${commentId}/reaction`, {
|
await axios.post(`/board/${boardId}/${commentId}/reaction`, {
|
||||||
LOCBRDSEQ: boardId, // 게시글 id
|
LOCBRDSEQ: boardId, // 게시글 id
|
||||||
LOCCMTSEQ: commentId, //댓글 id
|
LOCCMTSEQ: commentId, //댓글 id
|
||||||
// MEMBERSEQ: 1, // 멤버아이디 지금은 1 나중에 수정해야함
|
|
||||||
LOCGOBGOD: isLike ? 'T' : 'F',
|
LOCGOBGOD: isLike ? 'T' : 'F',
|
||||||
LOCGOBBAD: isDislike ? 'T' : 'F'
|
LOCGOBBAD: isDislike ? 'T' : 'F'
|
||||||
});
|
});
|
||||||
@ -258,13 +250,9 @@ const handleUpdateReaction = async ({ boardId, commentId, isLike, isDislike }) =
|
|||||||
|
|
||||||
likeClicked.value = isLike;
|
likeClicked.value = isLike;
|
||||||
dislikeClicked.value = isDislike;
|
dislikeClicked.value = isDislike;
|
||||||
// console.log(updatedData)
|
|
||||||
|
|
||||||
// console.log("갱신된 데이터:", updatedData);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('오류가 발생했습니다.');
|
alert('오류가 발생했습니다.');
|
||||||
// console.log('반응을 업데이트하는 중 오류가 발생했습니다.');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -281,13 +269,10 @@ const handleCommentReaction = async ({ boardId, commentId, isLike, isDislike })
|
|||||||
LOCGOBBAD: isDislike ? 'T' : 'F'
|
LOCGOBBAD: isDislike ? 'T' : 'F'
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log("댓글 좋아요 API 응답 데이터:", response.data);
|
|
||||||
|
|
||||||
await fetchComments();
|
await fetchComments();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('오류가 발생했습니다.');
|
alert('오류가 발생했습니다.');
|
||||||
// console.log('댓글 반응을 업데이트하는 중 오류 발생');
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -302,8 +287,6 @@ const fetchComments = async (page = 1) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log(response.data.data)
|
|
||||||
|
|
||||||
const commentsList = response.data.data.list.map(comment => ({
|
const commentsList = response.data.data.list.map(comment => ({
|
||||||
commentId: comment.LOCCMTSEQ, // 댓글 ID
|
commentId: comment.LOCCMTSEQ, // 댓글 ID
|
||||||
boardId: comment.LOCBRDSEQ,
|
boardId: comment.LOCBRDSEQ,
|
||||||
@ -320,6 +303,8 @@ const fetchComments = async (page = 1) => {
|
|||||||
children: [], // 대댓글을 담을 배열
|
children: [], // 대댓글을 담을 배열
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
commentsList.sort((a, b) => b.createdAtRaw - a.createdAtRaw);
|
||||||
|
|
||||||
for (const comment of commentsList) {
|
for (const comment of commentsList) {
|
||||||
if (!comment.commentId) continue;
|
if (!comment.commentId) continue;
|
||||||
|
|
||||||
@ -327,8 +312,6 @@ const fetchComments = async (page = 1) => {
|
|||||||
params: { LOCCMTPNT: comment.commentId }
|
params: { LOCCMTPNT: comment.commentId }
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log(`대댓글 데이터 (${comment.commentId}의 대댓글):`, replyResponse.data);
|
|
||||||
|
|
||||||
if (replyResponse.data.data) {
|
if (replyResponse.data.data) {
|
||||||
comment.children = replyResponse.data.data.map(reply => ({
|
comment.children = replyResponse.data.data.map(reply => ({
|
||||||
author: reply.author || '익명',
|
author: reply.author || '익명',
|
||||||
@ -368,23 +351,19 @@ const fetchComments = async (page = 1) => {
|
|||||||
navigateLastPage: response.data.data.navigateLastPage // 페이지네이션에서 마지막 페이지 번호
|
navigateLastPage: response.data.data.navigateLastPage // 페이지네이션에서 마지막 페이지 번호
|
||||||
};
|
};
|
||||||
|
|
||||||
// console.log("📌 댓글 목록:", comments.value);
|
|
||||||
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert('오류가 발생했습니다.');
|
alert('오류가 발생했습니다.');
|
||||||
// alert('댓글 목록 불러오기 오류:', error);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 댓글 작성
|
// 댓글 작성
|
||||||
const handleCommentSubmit = async (data, isCheck) => {
|
const handleCommentSubmit = async (data) => {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
console.error("handleCommentSubmit: data가 undefined입니다.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { comment, password } = data;
|
const { comment, password, isCheck } = data;
|
||||||
|
|
||||||
if (!comment || comment.trim() === "") {
|
if (!comment || comment.trim() === "") {
|
||||||
commentAlert.value = '댓글을 입력해주세요.';
|
commentAlert.value = '댓글을 입력해주세요.';
|
||||||
@ -404,25 +383,22 @@ const handleCommentSubmit = async (data, isCheck) => {
|
|||||||
LOCCMTRPY: comment,
|
LOCCMTRPY: comment,
|
||||||
LOCCMTPWD: isCheck ? password : '',
|
LOCCMTPWD: isCheck ? password : '',
|
||||||
LOCCMTPNT: 1,
|
LOCCMTPNT: 1,
|
||||||
LOCBRDTYP: unknown.value ? "300102" : null
|
LOCBRDTYP: isCheck ? "300102" : null
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
// console.log('댓글 작성 성공:', response.data.message);
|
|
||||||
passwordAlert.value = '';
|
passwordAlert.value = '';
|
||||||
commentAlert.value = '';
|
commentAlert.value = '';
|
||||||
await fetchComments();
|
await fetchComments();
|
||||||
} else {
|
} else {
|
||||||
// console.error('댓글 작성 실패:', response.data.message);
|
|
||||||
alert("댓글 작성을 실패했습니다.")
|
alert("댓글 작성을 실패했습니다.")
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('댓글 작성 중 오류 발생:', error);
|
|
||||||
alert("오류가 발생했습니다.")
|
alert("오류가 발생했습니다.")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 대댓글 추가 (부모 `BoardCommentList`로부터 이벤트 받아서 처리)
|
// 대댓글 추가
|
||||||
const handleCommentReply = async (reply) => {
|
const handleCommentReply = async (reply) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
|
const response = await axios.post(`board/${currentBoardId.value}/comment`, {
|
||||||
@ -434,22 +410,17 @@ const handleCommentReply = async (reply) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
if (response.data.code === 200) { // 서버 응답 코드도 확인
|
if (response.data.code === 200) {
|
||||||
// console.log('대댓글 작성 성공:', response.data);
|
await fetchComments();
|
||||||
await fetchComments(); // 댓글 목록 새로고침
|
|
||||||
} else {
|
} else {
|
||||||
// console.log('대댓글 작성 실패 - 서버 응답:', response.data);
|
|
||||||
alert('대댓글 작성을 실패했습니다.');
|
alert('대댓글 작성을 실패했습니다.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('대댓글 작성 중 오류 발생:', error);
|
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
// console.error('서버 응답 에러:', error.response.data);
|
|
||||||
alert("오류가 발생했습니다.");
|
alert("오류가 발생했습니다.");
|
||||||
}
|
}
|
||||||
alert("오류가 발생했습니다.");
|
alert("오류가 발생했습니다.");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -486,45 +457,41 @@ const findCommentById = (commentId, commentsList) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 댓글 수정 버튼 클릭
|
// 댓글 수정(대댓글 포함)
|
||||||
const editComment = (comment) => {
|
const editComment = (comment) => {
|
||||||
|
password.value = '';
|
||||||
|
passwordCommentAlert.value = '';
|
||||||
|
currentPasswordCommentId.value = null;
|
||||||
|
|
||||||
// 부모 또는 대댓글을 찾아서 가져오기
|
|
||||||
const targetComment = findCommentById(comment.commentId, comments.value);
|
const targetComment = findCommentById(comment.commentId, comments.value);
|
||||||
|
|
||||||
if (!targetComment) {
|
if (!targetComment) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 댓글 작성자가 현재 로그인한 사용자와 동일한지 확인
|
|
||||||
const isMyComment = comment.authorId === currentUserId.value;
|
const isMyComment = comment.authorId === currentUserId.value;
|
||||||
const isAnonymous = comment.author === "익명";
|
const isAnonymous = comment.author === "익명";
|
||||||
|
|
||||||
if (isMyComment) {
|
if (isMyComment) {
|
||||||
// 본인 댓글이면 바로 수정 모드 활성화
|
targetComment.isEditTextarea = !targetComment.isEditTextarea;
|
||||||
targetComment.isEditTextarea = true;
|
lastCommentClickedButton.value = "edit";
|
||||||
} else if (isAnonymous) {
|
} else if (isAnonymous) {
|
||||||
// 익명 댓글이면 비밀번호 입력창 띄우기
|
if (targetComment.isEditTextarea) return;
|
||||||
toggleCommentPassword(comment, "edit");
|
toggleCommentPassword(comment, "edit");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// console.log("다른 사용자 댓글 - 수정 불가");
|
|
||||||
alert("수정이 불가능합니다");
|
alert("수정이 불가능합니다");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 댓글 삭제 버튼 클릭
|
// 댓글 삭제 버튼 클릭
|
||||||
const deleteComment = async (comment) => {
|
const deleteComment = async (comment) => {
|
||||||
// 익명 사용자인 경우
|
|
||||||
const isMyComment = comment.authorId === currentUserId.value;
|
const isMyComment = comment.authorId === currentUserId.value;
|
||||||
|
|
||||||
if (unknown.value && !isMyComment) {
|
if (unknown.value && !isMyComment) {
|
||||||
if (comment.isEditTextarea) {
|
if (comment.isEditTextarea) {
|
||||||
// 현재 수정 중이라면 수정 모드를 끄고, 삭제 비밀번호 입력창을 띄움
|
|
||||||
comment.isEditTextarea = false;
|
comment.isEditTextarea = false;
|
||||||
comment.isCommentPassword = true;
|
comment.isCommentPassword = true;
|
||||||
} else {
|
} else {
|
||||||
// 수정 중이 아니면 기존의 삭제 비밀번호 입력창을 띄우는 로직 실행
|
|
||||||
toggleCommentPassword(comment, "delete");
|
toggleCommentPassword(comment, "delete");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -532,19 +499,21 @@ const deleteComment = async (comment) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 익명 비밀번호 창 토글
|
// 익명 댓글 비밀번호 창 토글
|
||||||
const toggleCommentPassword = (comment, button) => {
|
const toggleCommentPassword = (comment, button) => {
|
||||||
if (lastCommentClickedButton.value === button && isCommentPassword.value === comment.commentId) {
|
if (lastCommentClickedButton.value === button && currentPasswordCommentId.value === comment.commentId) {
|
||||||
isCommentPassword.value = false; // 비밀번호 창 닫기
|
currentPasswordCommentId.value = null; // 비밀번호 창 닫기
|
||||||
|
password.value = '';
|
||||||
|
passwordCommentAlert.value = '';
|
||||||
} else {
|
} else {
|
||||||
isCommentPassword.value = comment.commentId; // 비밀번호 창 열기
|
currentPasswordCommentId.value = comment.commentId; // 비밀번호 창 열기
|
||||||
|
password.value = '';
|
||||||
|
passwordCommentAlert.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
lastCommentClickedButton.value = button;
|
lastCommentClickedButton.value = button;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const togglePassword = (button) => {
|
const togglePassword = (button) => {
|
||||||
if (lastClickedButton.value === button) {
|
if (lastClickedButton.value === button) {
|
||||||
isPassword.value = !isPassword.value;
|
isPassword.value = !isPassword.value;
|
||||||
@ -556,16 +525,15 @@ const togglePassword = (button) => {
|
|||||||
|
|
||||||
// 게시글 비밀번호 제출
|
// 게시글 비밀번호 제출
|
||||||
const submitPassword = async () => {
|
const submitPassword = async () => {
|
||||||
if (!password.value) {
|
if (!password.value.trim()) {
|
||||||
passwordAlert.value = "비밀번호를 입력해주세요.";
|
passwordAlert.value = "비밀번호를 입력해주세요.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// console.log("📌 요청 시작: submitPassword 실행됨");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.post(`board/${currentBoardId.value}/password`, {
|
const response = await axios.post(`board/${currentBoardId.value}/password`, {
|
||||||
LOCBRDPWD: password.value,
|
LOCBRDPWD: password.value,
|
||||||
LOCBRDSEQ: 288, // 나중에 현재 게시글 ID 사용해야함
|
LOCBRDSEQ: 288,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data.code === 200 && response.data.data === true) {
|
if (response.data.code === 200 && response.data.data === true) {
|
||||||
@ -582,8 +550,6 @@ const submitPassword = async () => {
|
|||||||
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
|
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log("📌 전체 오류:", error);
|
|
||||||
|
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
if (error.response.status === 401) {
|
if (error.response.status === 401) {
|
||||||
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
|
passwordAlert.value = "비밀번호가 일치하지 않습니다.";
|
||||||
@ -598,34 +564,38 @@ const submitPassword = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 댓글 삭제 (비밀번호 확인 후)
|
// 댓글 (비밀번호 확인 후)
|
||||||
const submitCommentPassword = async (comment, password) => {
|
const submitCommentPassword = async (comment, password) => {
|
||||||
|
|
||||||
// console.log("비밀번호 :", password);
|
|
||||||
// console.log("댓글 ID:", comment.commentId);
|
|
||||||
|
|
||||||
if (!password) {
|
if (!password) {
|
||||||
passwordCommentAlert.value = "비밀번호를 입력해주세요.";
|
passwordCommentAlert.value = "비밀번호를 입력해주세요.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const targetComment = findCommentById(comment.commentId, comments.value);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// console.log('서버로 비밀번호 확인 요청')
|
|
||||||
const response = await axios.post(`board/comment/${comment.commentId}/password`, {
|
const response = await axios.post(`board/comment/${comment.commentId}/password`, {
|
||||||
LOCCMTPWD: password,
|
LOCCMTPWD: password,
|
||||||
LOCCMTSEQ: comment.commentId,
|
LOCCMTSEQ: comment.commentId,
|
||||||
});
|
});
|
||||||
// console.log("✅ 서버 응답 데이터:", response.data);
|
|
||||||
|
|
||||||
if (response.data.code === 200 && response.data.data === true) {
|
if (response.data.code === 200 && response.data.data === true) {
|
||||||
passwordCommentAlert.value = "";
|
passwordCommentAlert.value = "";
|
||||||
comment.isCommentPassword = false;
|
comment.isCommentPassword = false;
|
||||||
|
|
||||||
|
// 수정
|
||||||
if (lastCommentClickedButton.value === "edit") {
|
if (lastCommentClickedButton.value === "edit") {
|
||||||
comment.isEditTextarea = true;
|
|
||||||
passwordCommentAlert.value = "";
|
|
||||||
|
|
||||||
// handleSubmitEdit(comment, comment.content);
|
if (targetComment) {
|
||||||
|
//여기서 input 창 닫혀야함
|
||||||
|
targetComment.isEditTextarea = true;
|
||||||
|
passwordCommentAlert.value = "";
|
||||||
|
currentPasswordCommentId.value = null;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
alert("수정 취소를 실패했습니다.");
|
||||||
|
}
|
||||||
|
//삭제
|
||||||
} else if (lastCommentClickedButton.value === "delete") {
|
} else if (lastCommentClickedButton.value === "delete") {
|
||||||
passwordCommentAlert.value = "";
|
passwordCommentAlert.value = "";
|
||||||
|
|
||||||
@ -633,14 +603,12 @@ const submitCommentPassword = async (comment, password) => {
|
|||||||
}
|
}
|
||||||
lastCommentClickedButton.value = null;
|
lastCommentClickedButton.value = null;
|
||||||
} else {
|
} else {
|
||||||
// console.log("❌ 비밀번호가 틀림");
|
|
||||||
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다.";
|
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다.";
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log("🚨 서버 요청 중 오류 발생:", error.response?.data || error);
|
if (error.response?.status === 401) {
|
||||||
// if (error.response?.status === 401) {
|
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다";
|
||||||
// console.log("❌ 401 오류: 인증 실패 (비밀번호 불일치)");
|
}
|
||||||
// }
|
|
||||||
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다";
|
passwordCommentAlert.value = "비밀번호가 일치하지 않습니다";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -672,24 +640,18 @@ const deletePost = async () => {
|
|||||||
// 댓글 삭제 (대댓글 포함)
|
// 댓글 삭제 (대댓글 포함)
|
||||||
const deleteReplyComment = async (comment) => {
|
const deleteReplyComment = async (comment) => {
|
||||||
if (!confirm("정말 이 댓글을 삭제하시겠습니까?")) return;
|
if (!confirm("정말 이 댓글을 삭제하시겠습니까?")) return;
|
||||||
// console.log("댓글 ID:", comment);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.delete(`board/comment/${comment.commentId}`, {
|
const response = await axios.delete(`board/comment/${comment.commentId}`, {
|
||||||
data: { LOCCMTSEQ: comment.commentId }
|
data: { LOCCMTSEQ: comment.commentId }
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log("서버 응답:", response.data);
|
|
||||||
|
|
||||||
if (response.data.code === 200) {
|
if (response.data.code === 200) {
|
||||||
// console.log("댓글 삭제 성공!");
|
|
||||||
await fetchComments();
|
await fetchComments();
|
||||||
} else {
|
} else {
|
||||||
// console.log("댓글 삭제 실패:", response.data.message);
|
|
||||||
alert("댓글 삭제에 실패했습니다.");
|
alert("댓글 삭제에 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.log("댓글 삭제 중 오류 발생:", error);
|
|
||||||
alert("댓글 삭제 중 오류가 발생했습니다.");
|
alert("댓글 삭제 중 오류가 발생했습니다.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -702,9 +664,6 @@ const handleSubmitEdit = async (comment, editedContent) => {
|
|||||||
LOCCMTRPY: editedContent
|
LOCCMTRPY: editedContent
|
||||||
});
|
});
|
||||||
|
|
||||||
// 수정 성공 시 업데이트
|
|
||||||
// comment.content = editedContent;
|
|
||||||
// comment.isEditTextarea = false; f
|
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
|
|
||||||
const targetComment = findCommentById(comment.commentId, comments.value);
|
const targetComment = findCommentById(comment.commentId, comments.value);
|
||||||
@ -712,15 +671,12 @@ const handleSubmitEdit = async (comment, editedContent) => {
|
|||||||
targetComment.content = editedContent; // 댓글 내용 업데이트
|
targetComment.content = editedContent; // 댓글 내용 업데이트
|
||||||
targetComment.isEditTextarea = false; // 수정 모드 닫기
|
targetComment.isEditTextarea = false; // 수정 모드 닫기
|
||||||
} else {
|
} else {
|
||||||
// console.warn("❌ 수정할 댓글을 찾을 수 없음");
|
|
||||||
alert("수정할 댓글을 찾을 수 없습니다.");
|
alert("수정할 댓글을 찾을 수 없습니다.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// console.log("❌ 댓글 수정 실패:", response.data);
|
|
||||||
alert("댓글 수정 실패했습니다.");
|
alert("댓글 수정 실패했습니다.");
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error("댓글 수정 중 오류 발생:", error);
|
|
||||||
alert("댓글 수정 중 오류 발생했습니다.");
|
alert("댓글 수정 중 오류 발생했습니다.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -730,10 +686,8 @@ const handleCancelEdit = (comment) => {
|
|||||||
const targetComment = findCommentById(comment.commentId, comments.value);
|
const targetComment = findCommentById(comment.commentId, comments.value);
|
||||||
|
|
||||||
if (targetComment) {
|
if (targetComment) {
|
||||||
// console.log("✅ 원본 데이터 찾음, 수정 취소 처리 가능:", targetComment);
|
|
||||||
targetComment.isEditTextarea = false;
|
targetComment.isEditTextarea = false;
|
||||||
} else {
|
} else {
|
||||||
// console.error("❌ 원본 데이터 찾을 수 없음, 수정 취소 실패");
|
|
||||||
alert("수정 취소를 실패했습니다.");
|
alert("수정 취소를 실패했습니다.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -756,7 +710,7 @@ const handleCommentDeleted = (deletedCommentId) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2대댓글 삭제
|
// 대댓글 삭제
|
||||||
for (let parent of comments.value) {
|
for (let parent of comments.value) {
|
||||||
const childIndex = parent.children.findIndex(child => child.commentId === deletedCommentId);
|
const childIndex = parent.children.findIndex(child => child.commentId === deletedCommentId);
|
||||||
if (childIndex !== -1) {
|
if (childIndex !== -1) {
|
||||||
@ -764,11 +718,8 @@ const handleCommentDeleted = (deletedCommentId) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// console.error("❌ 삭제할 댓글을 찾을 수 없음:", deletedCommentId);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 날짜
|
// 날짜
|
||||||
const formattedDate = (dateString) => {
|
const formattedDate = (dateString) => {
|
||||||
if (!dateString) return "날짜 없음";
|
if (!dateString) return "날짜 없음";
|
||||||
|
|||||||
@ -7,9 +7,11 @@
|
|||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<div class="sidebar-actions text-center my-3">
|
<div class="sidebar-actions text-center my-3">
|
||||||
<HalfDayButtons
|
<HalfDayButtons
|
||||||
|
ref="halfDayButtonsRef"
|
||||||
@toggleHalfDay="toggleHalfDay"
|
@toggleHalfDay="toggleHalfDay"
|
||||||
@addVacationRequests="saveVacationChanges"
|
@addVacationRequests="saveVacationChanges"
|
||||||
:isDisabled="!hasChanges"
|
:isDisabled="!hasChanges"
|
||||||
|
:selectedDate="selectedDate"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ProfileList
|
<ProfileList
|
||||||
@ -59,7 +61,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref, onMounted, nextTick, computed, watch } from "vue";
|
import { reactive, ref, onMounted, nextTick, computed, watch, onBeforeUnmount } from "vue";
|
||||||
import axios from "@api";
|
import axios from "@api";
|
||||||
import FullCalendar from "@fullcalendar/vue3";
|
import FullCalendar from "@fullcalendar/vue3";
|
||||||
import dayGridPlugin from "@fullcalendar/daygrid";
|
import dayGridPlugin from "@fullcalendar/daygrid";
|
||||||
@ -80,6 +82,9 @@
|
|||||||
import { useUserInfoStore } from "@s/useUserInfoStore";
|
import { useUserInfoStore } from "@s/useUserInfoStore";
|
||||||
import { fetchHolidays } from "@c/calendar/holiday.js";
|
import { fetchHolidays } from "@c/calendar/holiday.js";
|
||||||
import { useToastStore } from '@s/toastStore';
|
import { useToastStore } from '@s/toastStore';
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const toastStore = useToastStore();
|
const toastStore = useToastStore();
|
||||||
const userStore = useUserInfoStore();
|
const userStore = useUserInfoStore();
|
||||||
@ -90,7 +95,7 @@
|
|||||||
const receivedVacations = ref([]);
|
const receivedVacations = ref([]);
|
||||||
const isModalOpen = ref(false);
|
const isModalOpen = ref(false);
|
||||||
const remainingVacationData = ref({});
|
const remainingVacationData = ref({});
|
||||||
|
const selectedDate = ref(null);
|
||||||
const lastRemainingYear = ref(new Date().getFullYear());
|
const lastRemainingYear = ref(new Date().getFullYear());
|
||||||
const lastRemainingMonth = ref(String(new Date().getMonth() + 1).padStart(2, "0"));
|
const lastRemainingMonth = ref(String(new Date().getMonth() + 1).padStart(2, "0"));
|
||||||
const isGrantModalOpen = ref(false);
|
const isGrantModalOpen = ref(false);
|
||||||
@ -106,6 +111,39 @@
|
|||||||
const fetchedEvents = ref([]);
|
const fetchedEvents = ref([]);
|
||||||
const halfDayButtonsRef = ref(null);
|
const halfDayButtonsRef = ref(null);
|
||||||
|
|
||||||
|
// 페이지 이동 시 변경 사항 확인
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
if (hasChanges.value) {
|
||||||
|
const answer = window.confirm("저장하지 않은 변경 사항이 있습니다. 이동하시겠습니까?");
|
||||||
|
if (!answer) {
|
||||||
|
return next(false); // 이동 취소
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener("beforeunload", preventUnsavedChanges);
|
||||||
|
});
|
||||||
|
|
||||||
|
function preventUnsavedChanges(event) {
|
||||||
|
if (hasChanges.value) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.returnValue = ""; // 대부분의 브라우저에서 경고 메시지 표시됨
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// `selectedDates` 변경 시 반차 버튼 초기화
|
||||||
|
watch(
|
||||||
|
() => Array.from(selectedDates.value.keys()), // 선택된 날짜 리스트 감시
|
||||||
|
(newKeys) => {
|
||||||
|
if (halfDayButtonsRef.value) {
|
||||||
|
halfDayButtonsRef.value.resetHalfDay();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
// 데이트피커 인풋 ref
|
// 데이트피커 인풋 ref
|
||||||
const calendarDatepicker = ref(null);
|
const calendarDatepicker = ref(null);
|
||||||
let fpInstance = null;
|
let fpInstance = null;
|
||||||
@ -192,6 +230,7 @@ function handleDateClick(info) {
|
|||||||
await fetchRemainingVacation();
|
await fetchRemainingVacation();
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
await fetchVacationHistory(currentYear);
|
await fetchVacationHistory(currentYear);
|
||||||
|
window.addEventListener("beforeunload", preventUnsavedChanges);
|
||||||
|
|
||||||
// Flatpickr 초기화 (달 선택 모드)
|
// Flatpickr 초기화 (달 선택 모드)
|
||||||
fpInstance = flatpickr(calendarDatepicker.value, {
|
fpInstance = flatpickr(calendarDatepicker.value, {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user