휴가 여려년도 삭제 수정

This commit is contained in:
dyhj625 2025-03-10 10:21:23 +09:00
parent 9c01a84749
commit 3ac834dd4a
4 changed files with 93 additions and 70 deletions

View File

@ -3,6 +3,9 @@
/* 휴가 */ /* 휴가 */
.fc-daygrid-event {
pointer-events: none !important;
}
/* 이벤트 선 없게 */ /* 이벤트 선 없게 */
.fc-event { .fc-event {
border: none; border: none;

View File

@ -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>
@ -114,7 +114,7 @@ const mergedVacations = computed(() => {
// //
const closeModal = () => { const closeModal = () => {
emit("close"); emit("close");
}; };
</script> </script>
<style scoped> <style scoped>

View File

@ -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;

View File

@ -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,16 +154,17 @@ 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 ||
holidayDates.value.has(clickedDateStr) || holidayDates.value.has(clickedDateStr) ||
clickedDateStr < todayStr clickedDateStr < todayStr
){ ) {
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,54 @@ 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
.filter(([date, type]) => type !== "delete") const vacationChangesByYear = selectedDatesArray.reduce((acc, [date, type]) => {
.filter(([date, type]) => const year = date.split("-")[0]; // YYYY-MM-DD YYYY
!myVacations.value.some(vac => vac.date && vac.date.startsWith(date)) || if (!acc[year]) acc[year] = { add: [], delete: [] };
myVacations.value.some(vac => vac.date && vac.date.startsWith(date) && vac.receiverId)
) if (type !== "delete") {
.map(([date, type]) => ({ date, type })); acc[year].add.push({ date, type });
const vacationsToDelete = myVacations.value
.filter(vac => {
if (!vac.date) return false;
const date = vac.date.split("T")[0];
return selectedDates.value.get(date) === "delete" && !vac.receiverId;
})
.map(vac => {
const id = vac.id;
return typeof id === "number" ? Number(id) : id;
});
try {
const response = await axios.post("vacation/batchUpdate", {
add: vacationsToAdd,
delete: vacationsToDelete
});
if (response.data && response.data.status === "OK") {
toastStore.onToast('휴가 변경 사항이 저장되었습니다.', 's');
await fetchVacationHistory(lastRemainingYear.value);
await fetchRemainingVacation();
if (isModalOpen.value) {
await fetchVacationHistory(lastRemainingYear.value);
}
const currentDate = fullCalendarRef.value.getApi().getDate();
await loadCalendarData(currentDate.getFullYear(), currentDate.getMonth() + 1);
selectedDates.value.clear();
updateCalendarEvents();
} else { } else {
toastStore.onToast('휴가 저장 중 오류가 발생했습니다.', 'e'); acc[year].delete.push(date);
} }
return acc;
}, {});
try {
for (const year of Object.keys(vacationChangesByYear)) {
const vacationsToAdd = vacationChangesByYear[year].add;
const vacationsToDeleteForYear = myVacations.value
.filter(vac => {
if (!vac.date) return false;
const vacDate = vac.date.split("T")[0];
return vacationChangesByYear[year].delete.includes(vacDate);
})
.map(vac => vac.id);
if (vacationsToAdd.length > 0 || vacationsToDeleteForYear.length > 0) {
const response = await axios.post("vacation/batchUpdate", {
add: vacationsToAdd,
delete: vacationsToDeleteForYear,
});
if (response.data && response.data.status === "OK") {
toastStore.onToast(`휴가 변경 사항이 저장되었습니다.`, 's');
await fetchVacationHistory(year);
} else {
toastStore.onToast(`휴가 변경 중 오류가 발생했습니다.`, 'e');
}
}
}
await fetchRemainingVacation();
selectedDates.value.clear();
updateCalendarEvents();
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 +379,11 @@ 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 || []; // +
receivedVacations.value = response.data.data.receivedVacations || []; myVacations.value = [...myVacations.value, ...response.data.data.usedVacations || []];
} else {
console.warn("❌ 연차 내역을 불러오지 못했습니다.");
myVacations.value = [];
receivedVacations.value = [];
} }
} catch (error) { } catch (error) {
console.error("🚨 연차 데이터 불러오기 실패:", error); console.error(`🚨 ${year}년 휴가 데이터 불러오기 실패:`, error);
} }
} }
// //
@ -480,8 +492,10 @@ function preventUnsavedChanges(event) {
} }
/* watch */ /* watch */
watch(lastRemainingYear, async (newYear, oldYear) => { watch(() => lastRemainingYear.value, async (newYear, oldYear) => {
await fetchVacationHistory(newYear); if (newYear !== oldYear) {
await fetchVacationHistory(newYear);
}
}); });
// `selectedDates` // `selectedDates`
watch( watch(
@ -551,17 +565,17 @@ onMounted(async () => {
if (titleEl) { if (titleEl) {
titleEl.style.cursor = 'pointer'; titleEl.style.cursor = 'pointer';
titleEl.addEventListener('click', () => { titleEl.addEventListener('click', () => {
const dpEl = calendarDatepicker.value; const dpEl = calendarDatepicker.value;
dpEl.style.display = 'block'; dpEl.style.display = 'block';
dpEl.style.position = 'fixed'; dpEl.style.position = 'fixed';
dpEl.style.top = '25%'; dpEl.style.top = '25%';
dpEl.style.left = '50%'; dpEl.style.left = '50%';
dpEl.style.transform = 'translate(-50%, -50%)'; dpEl.style.transform = 'translate(-50%, -50%)';
dpEl.style.zIndex = '9999'; dpEl.style.zIndex = '9999';
dpEl.style.border = 'none'; dpEl.style.border = 'none';
dpEl.style.outline = 'none'; dpEl.style.outline = 'none';
dpEl.style.backgroundColor = 'transparent'; dpEl.style.backgroundColor = 'transparent';
fpInstance.open(); fpInstance.open();
}); });
} }
}); });