Merge branch 'main' of http://192.168.0.251:3000/localnet/localhost-front
This commit is contained in:
commit
16b95d546c
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
/* 휴가 */
|
/* 휴가 */
|
||||||
|
|
||||||
|
.fc-daygrid-event {
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
/* 이벤트 선 없게 */
|
/* 이벤트 선 없게 */
|
||||||
.fc-event {
|
.fc-event {
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
@updateReaction="handleUpdateReaction"
|
@updateReaction="handleUpdateReaction"
|
||||||
/>
|
/>
|
||||||
<!-- 댓글 비밀번호 입력창 (익명일 경우) -->
|
<!-- 댓글 비밀번호 입력창 (익명일 경우) -->
|
||||||
<div v-if="currentPasswordCommentId === comment.commentId && unknown" class="mt-3 w-25 ms-auto">
|
<div v-if="currentPasswordCommentId === comment.commentId && unknown && comment.author == '익명'" class="mt-3 w-25 ms-auto">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
@ -48,37 +48,9 @@
|
|||||||
<p class="m-0 text-muted">댓글이 삭제되었습니다.</p>
|
<p class="m-0 text-muted">댓글이 삭제되었습니다.</p>
|
||||||
</template> -->
|
</template> -->
|
||||||
<PlusButton v-if="isPlusButton" @click="toggleComment" class="mt-6" />
|
<PlusButton v-if="isPlusButton" @click="toggleComment" class="mt-6" />
|
||||||
<BoardCommentArea v-if="isComment" :unknown="unknown" @submitComment="submitComment"/>
|
<BoardCommentArea v-if="isComment" :unknown="unknown" @submitComment="submitComment" :commnetId="comment.commentId" />
|
||||||
|
|
||||||
<!-- 대댓글 -->
|
<slot name="reply"></slot>
|
||||||
<ul v-if="comment.children && comment.children.length" class="list-unstyled">
|
|
||||||
<li
|
|
||||||
v-for="child in comment.children"
|
|
||||||
:key="child.commentId"
|
|
||||||
class="mt-8 pt-6 ps-10 border-top"
|
|
||||||
>
|
|
||||||
<BoardComment
|
|
||||||
:comment="child"
|
|
||||||
:unknown="child.author === '익명'"
|
|
||||||
:isPlusButton="false"
|
|
||||||
:isLike="true"
|
|
||||||
:isCommentProfile="true"
|
|
||||||
:isCommentAuthor="child.isCommentAuthor"
|
|
||||||
:isCommentPassword="isCommentPassword"
|
|
||||||
:currentPasswordCommentId="currentPasswordCommentId"
|
|
||||||
:passwordCommentAlert="passwordCommentAlert"
|
|
||||||
:password="password"
|
|
||||||
@editClick="handleReplyEditClick"
|
|
||||||
@deleteClick="$emit('deleteClick', child)"
|
|
||||||
@submitEdit="(comment, editedContent) => $emit('submitEdit', comment, editedContent)"
|
|
||||||
@cancelEdit="$emit('cancelEdit', child)"
|
|
||||||
@submitComment="submitComment"
|
|
||||||
@updateReaction="handleUpdateReaction"
|
|
||||||
@submitPassword="$emit('submitPassword', child, password)"
|
|
||||||
@update:password="$emit('update:password', $event)"
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -112,11 +84,11 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
isEditTextarea: {
|
isEditTextarea: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
isDeleted: {
|
isDeleted: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
isCommentPassword: {
|
isCommentPassword: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -124,18 +96,27 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
passwordCommentAlert: {
|
passwordCommentAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: '',
|
||||||
},
|
},
|
||||||
currentPasswordCommentId: {
|
currentPasswordCommentId: {
|
||||||
type: Number
|
type: Number,
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
type: String
|
type: String,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// emits 정의
|
// emits 정의
|
||||||
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'submitEdit', 'cancelEdit', 'update:password']);
|
const emit = defineEmits([
|
||||||
|
'submitComment',
|
||||||
|
'updateReaction',
|
||||||
|
'editClick',
|
||||||
|
'deleteClick',
|
||||||
|
'submitPassword',
|
||||||
|
'submitEdit',
|
||||||
|
'cancelEdit',
|
||||||
|
'update:password',
|
||||||
|
]);
|
||||||
|
|
||||||
const localEditedContent = ref(props.comment.content);
|
const localEditedContent = ref(props.comment.content);
|
||||||
|
|
||||||
@ -146,19 +127,18 @@ const toggleComment = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 부모 컴포넌트에 대댓글 추가 요청
|
// 부모 컴포넌트에 대댓글 추가 요청
|
||||||
const submitComment = (newComment) => {
|
const submitComment = newComment => {
|
||||||
emit('submitComment', { parentId: props.comment.commentId, ...newComment, LOCBRDTYP: newComment.LOCBRDTYP });
|
emit('submitComment', { parentId: props.comment.commentId, ...newComment, LOCBRDTYP: newComment.LOCBRDTYP });
|
||||||
isComment.value = false;
|
isComment.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 좋아요, 싫어요
|
// 좋아요, 싫어요
|
||||||
const handleUpdateReaction = (reactionData) => {
|
const handleUpdateReaction = reactionData => {
|
||||||
emit('updateReaction', {
|
emit('updateReaction', {
|
||||||
boardId: props.comment.boardId,
|
boardId: props.comment.boardId,
|
||||||
commentId: props.comment.commentId || reactionData.commentId,
|
commentId: props.comment.commentId || reactionData.commentId,
|
||||||
...reactionData,
|
...reactionData,
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 비밀번호 확인
|
// 비밀번호 확인
|
||||||
@ -166,11 +146,14 @@ const logPasswordAndEmit = () => {
|
|||||||
emit('submitPassword', props.comment, props.password);
|
emit('submitPassword', props.comment, props.password);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(() => props.comment.isEditTextarea, (newVal) => {
|
watch(
|
||||||
|
() => props.comment.isEditTextarea,
|
||||||
|
newVal => {
|
||||||
if (newVal) {
|
if (newVal) {
|
||||||
localEditedContent.value = props.comment.content;
|
localEditedContent.value = props.comment.content;
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// watch(() => props.comment.isDeleted, () => {
|
// watch(() => props.comment.isDeleted, () => {
|
||||||
// console.log("BoardComment - isDeleted 상태 변경됨:", newVal);
|
// console.log("BoardComment - isDeleted 상태 변경됨:", newVal);
|
||||||
@ -188,10 +171,5 @@ const submitEdit = () => {
|
|||||||
|
|
||||||
const handleEditClick = () => {
|
const handleEditClick = () => {
|
||||||
emit('editClick', props.comment);
|
emit('editClick', props.comment);
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleReplyEditClick = (comment) => {
|
|
||||||
emit('editClick', comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
</div> -->
|
</div> -->
|
||||||
<!-- 텍스트박스 -->
|
<!-- 텍스트박스 -->
|
||||||
<div class="w-100">
|
<div class="w-100">
|
||||||
<textarea class="form-control" placeholder="댓글 달기" rows="3" v-model="comment"></textarea>
|
<textarea class="form-control" placeholder="댓글 달기" rows="3" :maxlength="maxLength" v-model="comment"></textarea>
|
||||||
<span v-if="commentAlert" class="invalid-feedback d-block text-start ms-2">{{ commentAlert }}</span>
|
<span v-if="commentAlert" class="invalid-feedback d-block text-start ms-2">{{ commentAlert }}</span>
|
||||||
<span v-else class="invalid-feedback d-block text-start ms-2">{{ textAlert }}</span>
|
<span v-else class="invalid-feedback d-block text-start ms-2">{{ textAlert }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -22,8 +22,8 @@
|
|||||||
<div class="d-flex flex-wrap align-items-center">
|
<div class="d-flex flex-wrap align-items-center">
|
||||||
<!-- 익명 체크박스 (익명게시판일 경우에만)-->
|
<!-- 익명 체크박스 (익명게시판일 경우에만)-->
|
||||||
<div v-if="unknown" class="form-check form-check-inline mb-0 me-4">
|
<div v-if="unknown" class="form-check form-check-inline mb-0 me-4">
|
||||||
<input class="form-check-input" type="checkbox" id="inlineCheckbox1" v-model="isCheck" />
|
<input class="form-check-input" type="checkbox" :id="`checkboxAnnonymous${commnetId}`" v-model="isCheck" />
|
||||||
<label class="form-check-label" for="inlineCheckbox1">익명</label>
|
<label class="form-check-label" :for="`checkboxAnnonymous${commnetId}`">익명</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 비밀번호 입력 필드 (익명이 선택된 경우에만 표시) -->
|
<!-- 비밀번호 입력 필드 (익명이 선택된 경우에만 표시) -->
|
||||||
@ -71,6 +71,13 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
maxLength: {
|
||||||
|
type: Number,
|
||||||
|
default: 500,
|
||||||
|
},
|
||||||
|
commnetId: {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const $common = inject('common');
|
const $common = inject('common');
|
||||||
@ -122,7 +129,6 @@ watch(
|
|||||||
if (!props.passwordAlert) {
|
if (!props.passwordAlert) {
|
||||||
resetCommentForm();
|
resetCommentForm();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ul class="list-unstyled mt-10">
|
<ul class="list-unstyled mt-10">
|
||||||
<li
|
<li v-for="comment in comments" :key="comment.commentId" class="mt-6 border-bottom pb-6">
|
||||||
v-for="comment in comments"
|
|
||||||
:key="comment.commentId"
|
|
||||||
class="mt-6 border-bottom pb-6"
|
|
||||||
>
|
|
||||||
<BoardComment
|
<BoardComment
|
||||||
:unknown="unknown"
|
:unknown="unknown"
|
||||||
:comment="comment"
|
:comment="comment"
|
||||||
@ -21,22 +17,50 @@
|
|||||||
@submitComment="submitComment"
|
@submitComment="submitComment"
|
||||||
@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"
|
@update:password="updatePassword"
|
||||||
|
>
|
||||||
|
<!-- 대댓글 -->
|
||||||
|
<template #reply>
|
||||||
|
<ul v-if="comment.children && comment.children.length" class="list-unstyled">
|
||||||
|
<li v-for="(child, index) in comment.children" :key="child.commentId" class="mt-8 pt-6 ps-10 border-top">
|
||||||
|
<BoardComment
|
||||||
|
:comment="child"
|
||||||
|
:unknown="child.author === '익명'"
|
||||||
|
:isPlusButton="false"
|
||||||
|
:isLike="true"
|
||||||
|
:isCommentProfile="true"
|
||||||
|
:isCommentAuthor="child.isCommentAuthor"
|
||||||
|
:isCommentPassword="isCommentPassword"
|
||||||
|
:currentPasswordCommentId="currentPasswordCommentId"
|
||||||
|
:passwordCommentAlert="passwordCommentAlert"
|
||||||
|
:password="password"
|
||||||
|
@editClick="handleReplyEditClick"
|
||||||
|
@deleteClick="$emit('deleteClick', child)"
|
||||||
|
@submitEdit="(comment, editedContent) => $emit('submitEdit', comment, editedContent)"
|
||||||
|
@cancelEdit="$emit('cancelEdit', child)"
|
||||||
|
@submitComment="submitComment"
|
||||||
|
@updateReaction="handleUpdateReaction"
|
||||||
|
@submitPassword="$emit('submitPassword', child, password)"
|
||||||
|
@update:password="$emit('update:password', $event)"
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
</BoardComment>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { defineProps, defineEmits } from 'vue';
|
import { defineProps, defineEmits } from 'vue';
|
||||||
import BoardComment from './BoardComment.vue'
|
import BoardComment from './BoardComment.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
comments: {
|
comments: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
default: () => []
|
default: () => [],
|
||||||
},
|
},
|
||||||
unknown: {
|
unknown: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -60,19 +84,31 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
passwordCommentAlert: {
|
passwordCommentAlert: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: '',
|
||||||
},
|
},
|
||||||
currentPasswordCommentId: {
|
currentPasswordCommentId: {
|
||||||
type: Number
|
type: Number,
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
type: String
|
type: String,
|
||||||
|
},
|
||||||
|
index: {
|
||||||
|
type: Number,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['submitComment', 'updateReaction', 'editClick', 'deleteClick', 'submitPassword', 'clearPassword','submitEdit', 'update:password']);
|
const emit = defineEmits([
|
||||||
|
'submitComment',
|
||||||
|
'updateReaction',
|
||||||
|
'editClick',
|
||||||
|
'deleteClick',
|
||||||
|
'submitPassword',
|
||||||
|
'clearPassword',
|
||||||
|
'submitEdit',
|
||||||
|
'update:password',
|
||||||
|
]);
|
||||||
|
|
||||||
const submitComment = (replyData) => {
|
const submitComment = replyData => {
|
||||||
emit('submitComment', replyData);
|
emit('submitComment', replyData);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,13 +120,13 @@ const handleUpdateReaction = (reactionData, commentId, boardId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
emit('updateReaction', updatedReactionData);
|
emit('updateReaction', updatedReactionData);
|
||||||
}
|
};
|
||||||
|
|
||||||
const submitPassword = (comment, password) => {
|
const submitPassword = (comment, password) => {
|
||||||
emit('submitPassword', comment, password);
|
emit('submitPassword', comment, password);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEditClick = (comment) => {
|
const handleEditClick = comment => {
|
||||||
if (comment.parentId) {
|
if (comment.parentId) {
|
||||||
emit('editClick', comment); // 대댓글
|
emit('editClick', comment); // 대댓글
|
||||||
} else {
|
} else {
|
||||||
@ -99,10 +135,10 @@ const handleEditClick = (comment) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmitEdit = (comment, editedContent) => {
|
const handleSubmitEdit = (comment, editedContent) => {
|
||||||
emit("submitEdit", comment, editedContent);
|
emit('submitEdit', comment, editedContent);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeleteClick = (comment) => {
|
const handleDeleteClick = comment => {
|
||||||
if (comment.parentId) {
|
if (comment.parentId) {
|
||||||
emit('deleteClick', comment); // 대댓글 삭제
|
emit('deleteClick', comment); // 대댓글 삭제
|
||||||
} else {
|
} else {
|
||||||
@ -110,7 +146,7 @@ const handleDeleteClick = (comment) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCancelEdit = (comment) => {
|
const handleCancelEdit = comment => {
|
||||||
if (comment.parentId) {
|
if (comment.parentId) {
|
||||||
emit('cancelEdit', comment); // 대댓글 수정 취소
|
emit('cancelEdit', comment); // 대댓글 수정 취소
|
||||||
} else {
|
} else {
|
||||||
@ -118,7 +154,11 @@ const handleCancelEdit = (comment) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const updatePassword = (newPassword) => {
|
const updatePassword = newPassword => {
|
||||||
emit('update:password', newPassword);
|
emit('update:password', newPassword);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleReplyEditClick = comment => {
|
||||||
|
emit('editClick', comment);
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -40,7 +40,7 @@
|
|||||||
const defaultProfile = '/img/icons/icon.png';
|
const defaultProfile = '/img/icons/icon.png';
|
||||||
|
|
||||||
// 서버의 이미지 경로 (Vue 환경 변수 사용 가능)
|
// 서버의 이미지 경로 (Vue 환경 변수 사용 가능)
|
||||||
const baseUrl = 'http://localhost:10325/'; // API 서버 URL
|
const baseUrl = import.meta.env.VITE_SERVER; // API 서버 URL
|
||||||
|
|
||||||
// Props 정의
|
// Props 정의
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<template v-if="isRecommend">
|
<template v-if="isRecommend">
|
||||||
<button class="btn btn-label-primary btn-icon" :class="{'clicked': likeClicked, 'big': bigBtn}" @click="handleLike">
|
<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">{{ likeCount }}</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="{'clicked': dislikeClicked, 'big': bigBtn}" @click="handleDislike">
|
<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">{{ dislikeCount }}</span>
|
<i class="fa-regular fa-thumbs-down"></i> <span class="num">{{ dislikeCount }}</span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
@ -73,7 +73,6 @@ const handleDislike = () => {
|
|||||||
dislikeClicked.value = isDislike;
|
dislikeClicked.value = isDislike;
|
||||||
likeClicked.value = false;
|
likeClicked.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -105,7 +104,7 @@ const handleDislike = () => {
|
|||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
width: 55px;
|
width: 55px;
|
||||||
height: 30px;
|
/* height: 30px; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn.big {
|
.btn.big {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isOpen" class="vac-modal-dialog" @click.self="closeModal">
|
<div v-if="isOpen" class="vac-modal-dialog" @click.self="closeModal">
|
||||||
<div class="vac-modal-content p-5 modal-scroll">
|
<div class="vac-modal-content p-5 modal-scroll">
|
||||||
<h5 class="vac-modal-title">📅 내 연차 사용 내역</h5>
|
<h5 class="vac-modal-title">📅 내 연차 상세 내역</h5>
|
||||||
<button class="close-btn" @click="closeModal">✖</button>
|
<button class="close-btn" @click="closeModal">✖</button>
|
||||||
<!-- 연차 목록 -->
|
<!-- 연차 목록 -->
|
||||||
<div class="vac-modal-body" v-if="mergedVacations.length > 0">
|
<div class="vac-modal-body" v-if="mergedVacations.length > 0">
|
||||||
@ -26,8 +26,8 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<!-- 연차 데이터 없음 -->
|
<!-- 연차 데이터 없음 -->
|
||||||
<p v-else class="text-sm-center mt-10 text-gray">
|
<p v-else class="text-sm-center mt-10 text-gray vac-modal-title">
|
||||||
🚫 사용한 연차가 없습니다.
|
🚫 연차 내역이 없습니다.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -58,19 +58,15 @@ const emit = defineEmits(["close"]);
|
|||||||
// 사용한 휴가(선물,연차사용)
|
// 사용한 휴가(선물,연차사용)
|
||||||
let globalCounter = 0;
|
let globalCounter = 0;
|
||||||
const usedVacations = computed(() => {
|
const usedVacations = computed(() => {
|
||||||
const result = [];
|
return props.myVacations.flatMap((v) => {
|
||||||
props.myVacations.forEach((v) => {
|
|
||||||
const count = v.used_quota || 1;
|
const count = v.used_quota || 1;
|
||||||
for (let i = 0; i < count; i++) {
|
return Array.from({ length: count }, (_, i) => ({
|
||||||
result.push({
|
|
||||||
...v,
|
...v,
|
||||||
category: "used",
|
category: "used",
|
||||||
code: v.LOCVACTYP,
|
code: v.LOCVACTYP,
|
||||||
_expandIndex: globalCounter++,
|
_expandIndex: globalCounter++,
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 받은 휴가
|
// 받은 휴가
|
||||||
|
|||||||
@ -69,12 +69,18 @@ nextTick(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const sortedUserList = computed(() => {
|
const sortedUserList = computed(() => {
|
||||||
if (!employeeId.value) return userList.value;
|
if (!employeeId.value) return [];
|
||||||
const myProfile = userList.value.find(user => user.MEMBERSEQ === employeeId.value);
|
|
||||||
const otherUsers = userList.value.filter(user => user.MEMBERSEQ !== employeeId.value);
|
// 관리자가 아닌 사용자만 필터링
|
||||||
return myProfile ? [myProfile, ...otherUsers] : userList.value;
|
const nonAdminUsers = userList.value.filter(user => user.MEMBERROL !== "ROLE_ADMIN");
|
||||||
|
|
||||||
|
const myProfile = nonAdminUsers.find(user => user.MEMBERSEQ === employeeId.value);
|
||||||
|
const otherUsers = nonAdminUsers.filter(user => user.MEMBERSEQ !== employeeId.value);
|
||||||
|
|
||||||
|
return myProfile ? [myProfile, ...otherUsers] : otherUsers;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const getUserProfileImage = (profilePath) =>
|
const getUserProfileImage = (profilePath) =>
|
||||||
profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
|
profilePath && profilePath.trim() ? `${baseUrl}upload/img/profile/${profilePath}` : defaultProfile;
|
||||||
|
|
||||||
@ -94,11 +100,11 @@ if (windowWidth.value >= 1650) {
|
|||||||
if (totalUsers <= 15) return "30px";
|
if (totalUsers <= 15) return "30px";
|
||||||
return "20px";
|
return "20px";
|
||||||
} else if (windowWidth.value >= 1024) {
|
} else if (windowWidth.value >= 1024) {
|
||||||
if (totalUsers <= 10) return "35px";
|
if (totalUsers <= 10) return "40px";
|
||||||
if (totalUsers <= 15) return "30px";
|
if (totalUsers <= 15) return "30px";
|
||||||
return "20px";
|
return "20px";
|
||||||
} else {
|
} else {
|
||||||
return "20px";
|
return "30px";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -92,6 +92,7 @@
|
|||||||
:unknown="unknown"
|
:unknown="unknown"
|
||||||
:commentAlert="commentAlert"
|
:commentAlert="commentAlert"
|
||||||
:passwordAlert="passwordAlert"
|
:passwordAlert="passwordAlert"
|
||||||
|
:maxLength="500"
|
||||||
@submitComment="handleCommentSubmit"
|
@submitComment="handleCommentSubmit"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -237,7 +238,6 @@
|
|||||||
const data = response.data.data;
|
const data = response.data.data;
|
||||||
|
|
||||||
profileName.value = data.author || '익명';
|
profileName.value = data.author || '익명';
|
||||||
console.log(data.author);
|
|
||||||
authorId.value = data.authorId;
|
authorId.value = data.authorId;
|
||||||
boardTitle.value = data.title || '제목 없음';
|
boardTitle.value = data.title || '제목 없음';
|
||||||
boardContent.value = data.content || '';
|
boardContent.value = data.content || '';
|
||||||
@ -580,7 +580,7 @@
|
|||||||
LOCBRDPWD: password.value,
|
LOCBRDPWD: password.value,
|
||||||
LOCBRDSEQ: currentBoardId.value,
|
LOCBRDSEQ: currentBoardId.value,
|
||||||
});
|
});
|
||||||
console.log('response: ', response);
|
|
||||||
if (response.data.code === 200 && response.data.data === true) {
|
if (response.data.code === 200 && response.data.data === true) {
|
||||||
password.value = '';
|
password.value = '';
|
||||||
isPassword.value = false;
|
isPassword.value = false;
|
||||||
|
|||||||
@ -142,7 +142,7 @@ const calendarOptions = reactive({
|
|||||||
datesSet: handleMonthChange,
|
datesSet: handleMonthChange,
|
||||||
events: calendarEvents,
|
events: calendarEvents,
|
||||||
});
|
});
|
||||||
// 캘린더 월 변경경
|
// 캘린더 월 변경
|
||||||
function handleMonthChange(viewInfo) {
|
function handleMonthChange(viewInfo) {
|
||||||
const currentDate = viewInfo.view.currentStart;
|
const currentDate = viewInfo.view.currentStart;
|
||||||
const year = currentDate.getFullYear();
|
const year = currentDate.getFullYear();
|
||||||
@ -154,6 +154,7 @@ function handleDateClick(info) {
|
|||||||
const clickedDateStr = info.dateStr;
|
const clickedDateStr = info.dateStr;
|
||||||
const clickedDate = info.date;
|
const clickedDate = info.date;
|
||||||
const todayStr = new Date().toISOString().split("T")[0];
|
const todayStr = new Date().toISOString().split("T")[0];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
clickedDate.getDay() === 0 ||
|
clickedDate.getDay() === 0 ||
|
||||||
clickedDate.getDay() === 6 ||
|
clickedDate.getDay() === 6 ||
|
||||||
@ -163,7 +164,7 @@ function handleDateClick(info) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const isMyVacation = myVacations.value.some(vac => {
|
const isMyVacation = myVacations.value.some(vac => {
|
||||||
const vacDate = vac.date ? String(vac.date).substring(0, 10) : "";
|
const vacDate = vac.date ? vac.date.substring(0, 10) : "";
|
||||||
return vacDate === clickedDateStr && !vac.receiverId;
|
return vacDate === clickedDateStr && !vac.receiverId;
|
||||||
});
|
});
|
||||||
if (isMyVacation) {
|
if (isMyVacation) {
|
||||||
@ -186,7 +187,6 @@ function handleDateClick(info) {
|
|||||||
selectedDates.value.set(clickedDateStr, type);
|
selectedDates.value.set(clickedDateStr, type);
|
||||||
halfDayType.value = null;
|
halfDayType.value = null;
|
||||||
updateCalendarEvents();
|
updateCalendarEvents();
|
||||||
// 날짜 선택 후 반차버튼 초기화
|
|
||||||
if (halfDayButtonsRef.value) {
|
if (halfDayButtonsRef.value) {
|
||||||
halfDayButtonsRef.value.resetHalfDay();
|
halfDayButtonsRef.value.resetHalfDay();
|
||||||
}
|
}
|
||||||
@ -266,15 +266,24 @@ const handleProfileClick = async (user) => {
|
|||||||
const fetchUserList = async () => {
|
const fetchUserList = async () => {
|
||||||
try {
|
try {
|
||||||
await userListStore.fetchUserList();
|
await userListStore.fetchUserList();
|
||||||
userList.value = userListStore.userList;
|
|
||||||
|
// "ROLE_ADMIN"을 제외하고 필터링
|
||||||
|
const filteredUsers = userListStore.userList.filter(user => user.MEMBERROL !== "ROLE_ADMIN");
|
||||||
|
|
||||||
|
// 필터링된 리스트를 userList에 반영
|
||||||
|
userList.value = [...filteredUsers];
|
||||||
|
|
||||||
if (!userList.value.length) {
|
if (!userList.value.length) {
|
||||||
console.warn("📌 사용자 목록이 비어 있음!");
|
console.warn("📌 사용자 목록이 비어 있음!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 사용자별 색상 저장
|
||||||
userColors.value = {};
|
userColors.value = {};
|
||||||
userList.value.forEach((user) => {
|
userList.value.forEach((user) => {
|
||||||
userColors.value[user.MEMBERSEQ] = user.usercolor;
|
userColors.value[user.MEMBERSEQ] = user.usercolor;
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("📌 사용자 목록 불러오기 오류:", error);
|
console.error("📌 사용자 목록 불러오기 오류:", error);
|
||||||
}
|
}
|
||||||
@ -310,47 +319,57 @@ const filteredReceivedVacations = computed(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* 휴가 저장 */
|
||||||
/* 휴가 변경사항 저장 */
|
// 휴가 변경사항 저장
|
||||||
async function saveVacationChanges() {
|
async function saveVacationChanges() {
|
||||||
if (!hasChanges.value) return;
|
if (!hasChanges.value) return;
|
||||||
const selectedDatesArray = Array.from(selectedDates.value);
|
const selectedDatesArray = Array.from(selectedDates.value);
|
||||||
const vacationsToAdd = selectedDatesArray
|
const vacationChangesByYear = selectedDatesArray.reduce((acc, [date, type]) => {
|
||||||
.filter(([date, type]) => type !== "delete")
|
const year = date.split("-")[0]; // YYYY-MM-DD에서 YYYY 추출
|
||||||
.filter(([date, type]) =>
|
if (!acc[year]) acc[year] = { add: [], delete: [] };
|
||||||
!myVacations.value.some(vac => vac.date && vac.date.startsWith(date)) ||
|
if (type !== "delete") {
|
||||||
myVacations.value.some(vac => vac.date && vac.date.startsWith(date) && vac.receiverId)
|
acc[year].add.push({ date, type });
|
||||||
)
|
} else {
|
||||||
.map(([date, type]) => ({ date, type }));
|
acc[year].delete.push(date);
|
||||||
const vacationsToDelete = myVacations.value
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
try {
|
||||||
|
for (const year of Object.keys(vacationChangesByYear)) {
|
||||||
|
const vacationsToAdd = vacationChangesByYear[year].add;
|
||||||
|
// 즉시 삭제 반영을 위해 삭제 대상 id 가져오기
|
||||||
|
const vacationsToDeleteForYear = myVacations.value
|
||||||
.filter(vac => {
|
.filter(vac => {
|
||||||
if (!vac.date) return false;
|
if (!vac.date) return false;
|
||||||
const date = vac.date.split("T")[0];
|
const vacDate = vac.date.split("T")[0];
|
||||||
return selectedDates.value.get(date) === "delete" && !vac.receiverId;
|
return vacationChangesByYear[year].delete.includes(vacDate);
|
||||||
})
|
|
||||||
.map(vac => {
|
|
||||||
const id = vac.id;
|
|
||||||
return typeof id === "number" ? Number(id) : id;
|
|
||||||
});
|
});
|
||||||
try {
|
const vacationIdsToDelete = vacationsToDeleteForYear.map(vac => vac.id);
|
||||||
|
if (vacationsToAdd.length > 0 || vacationIdsToDelete.length > 0) {
|
||||||
const response = await axios.post("vacation/batchUpdate", {
|
const response = await axios.post("vacation/batchUpdate", {
|
||||||
add: vacationsToAdd,
|
add: vacationsToAdd,
|
||||||
delete: vacationsToDelete
|
delete: vacationIdsToDelete,
|
||||||
});
|
});
|
||||||
if (response.data && response.data.status === "OK") {
|
if (response.data && response.data.status === "OK") {
|
||||||
toastStore.onToast('휴가 변경 사항이 저장되었습니다.', 's');
|
toastStore.onToast(`휴가 변경 사항이 저장되었습니다.`, 's');
|
||||||
await fetchVacationHistory(lastRemainingYear.value);
|
// 즉시 삭제 반영: myVacations에서 해당 ID 삭제
|
||||||
await fetchRemainingVacation();
|
myVacations.value = myVacations.value.filter(vac => !vacationIdsToDelete.includes(vac.id));
|
||||||
if (isModalOpen.value) {
|
// 서버에서 최신 데이터 가져와 반영
|
||||||
await fetchVacationHistory(lastRemainingYear.value);
|
const updatedVacations = await fetchVacationHistory(year);
|
||||||
|
if (updatedVacations) {
|
||||||
|
myVacations.value = updatedVacations; // 최신 데이터 적용
|
||||||
}
|
}
|
||||||
const currentDate = fullCalendarRef.value.getApi().getDate();
|
} else {
|
||||||
await loadCalendarData(currentDate.getFullYear(), currentDate.getMonth() + 1);
|
toastStore.onToast(`휴가 변경 중 오류가 발생했습니다.`, 'e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await fetchRemainingVacation();
|
||||||
selectedDates.value.clear();
|
selectedDates.value.clear();
|
||||||
updateCalendarEvents();
|
updateCalendarEvents();
|
||||||
} else {
|
// 현재 보고 있는 연도의 데이터 새로고침
|
||||||
toastStore.onToast('휴가 저장 중 오류가 발생했습니다.', 'e');
|
const currentDate = fullCalendarRef.value.getApi().getDate();
|
||||||
}
|
await loadCalendarData(currentDate.getFullYear(), currentDate.getMonth() + 1);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("🚨 휴가 변경 저장 실패:", error);
|
console.error("🚨 휴가 변경 저장 실패:", error);
|
||||||
toastStore.onToast('휴가 저장 요청에 실패했습니다.', 'e');
|
toastStore.onToast('휴가 저장 요청에 실패했습니다.', 'e');
|
||||||
@ -363,15 +382,17 @@ async function fetchVacationHistory(year) {
|
|||||||
try {
|
try {
|
||||||
const response = await axios.get(`vacation/history?year=${year}`);
|
const response = await axios.get(`vacation/history?year=${year}`);
|
||||||
if (response.status === 200 && response.data) {
|
if (response.status === 200 && response.data) {
|
||||||
myVacations.value = response.data.data.usedVacations || [];
|
const newVacations = response.data.data.usedVacations || [];
|
||||||
receivedVacations.value = response.data.data.receivedVacations || [];
|
|
||||||
} else {
|
const uniqueVacations = Array.from(
|
||||||
console.warn("❌ 연차 내역을 불러오지 못했습니다.");
|
new Map([...myVacations.value, ...newVacations].map(v => [`${v.date}-${v.type}`, v]))
|
||||||
myVacations.value = [];
|
.values()
|
||||||
receivedVacations.value = [];
|
);
|
||||||
|
|
||||||
|
myVacations.value = uniqueVacations;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("🚨 연차 데이터 불러오기 실패:", error);
|
console.error(`🚨 ${year}년 휴가 데이터 불러오기 실패:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 모든 사원 연차 내역 및 그래프화
|
// 모든 사원 연차 내역 및 그래프화
|
||||||
@ -462,16 +483,20 @@ function toggleHalfDay(type) {
|
|||||||
/* 페이지 이동 시 변경 사항 확인 */
|
/* 페이지 이동 시 변경 사항 확인 */
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
if (hasChanges.value) {
|
if (hasChanges.value) {
|
||||||
|
console.log('휴가!!!!!');
|
||||||
const answer = window.confirm("저장하지 않은 변경 사항이 있습니다. 이동하시겠습니까?");
|
const answer = window.confirm("저장하지 않은 변경 사항이 있습니다. 이동하시겠습니까?");
|
||||||
if (!answer) {
|
if (!answer) {
|
||||||
return next(false);
|
return next(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectedDates.value.clear();
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.removeEventListener("beforeunload", preventUnsavedChanges);
|
window.removeEventListener("beforeunload", preventUnsavedChanges);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
/* 새로고침 또는 페이지 종료 시 알림 */
|
||||||
function preventUnsavedChanges(event) {
|
function preventUnsavedChanges(event) {
|
||||||
if (hasChanges.value) {
|
if (hasChanges.value) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -480,8 +505,10 @@ function preventUnsavedChanges(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* watch */
|
/* watch */
|
||||||
watch(lastRemainingYear, async (newYear, oldYear) => {
|
watch(() => lastRemainingYear.value, async (newYear, oldYear) => {
|
||||||
|
if (newYear !== oldYear) {
|
||||||
await fetchVacationHistory(newYear);
|
await fetchVacationHistory(newYear);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// `selectedDates` 변경 시 반차 버튼 초기화
|
// `selectedDates` 변경 시 반차 버튼 초기화
|
||||||
watch(
|
watch(
|
||||||
@ -533,6 +560,9 @@ onMounted(async () => {
|
|||||||
altFormat: "F Y"
|
altFormat: "F Y"
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
onOpen: function() {
|
||||||
|
document.querySelector('.flatpickr-input').style.visibility = 'hidden';
|
||||||
|
},
|
||||||
onChange: function(selectedDatesArr, dateStr) {
|
onChange: function(selectedDatesArr, dateStr) {
|
||||||
// 선택한 달의 첫날로 달력을 이동
|
// 선택한 달의 첫날로 달력을 이동
|
||||||
fullCalendarRef.value.getApi().gotoDate(dateStr + "-01");
|
fullCalendarRef.value.getApi().gotoDate(dateStr + "-01");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user