휴가 선택 활성화 수정정
This commit is contained in:
parent
02f0cda3b4
commit
09e79cb690
@ -4,29 +4,29 @@
|
|||||||
<div class="row g-0">
|
<div class="row g-0">
|
||||||
<!-- Sidebar: 사이드바 영역 -->
|
<!-- Sidebar: 사이드바 영역 -->
|
||||||
<div class="col-3 app-calendar-sidebar border-end" id="app-calendar-sidebar">
|
<div class="col-3 app-calendar-sidebar border-end" id="app-calendar-sidebar">
|
||||||
<!-- 모달들은 화면 오버레이로 동작하므로 사이드바 내부에 두어도 무방 -->
|
|
||||||
<VacationModal
|
|
||||||
v-if="isModalOpen"
|
|
||||||
:isOpen="isModalOpen"
|
|
||||||
:myVacations="filteredMyVacations"
|
|
||||||
:receivedVacations="filteredReceivedVacations"
|
|
||||||
:userColors="userColors"
|
|
||||||
@close="isModalOpen = false"
|
|
||||||
/>
|
|
||||||
<VacationGrantModal
|
|
||||||
v-if="isGrantModalOpen"
|
|
||||||
:isOpen="isGrantModalOpen"
|
|
||||||
:targetUser="selectedUser"
|
|
||||||
:remainingQuota="remainingVacationData[selectedUser?.MEMBERSEQ] || 0"
|
|
||||||
@close="isGrantModalOpen = false"
|
|
||||||
@updateVacation="fetchRemainingVacation"
|
|
||||||
/>
|
|
||||||
<div class="sidebar-content">
|
<div class="sidebar-content">
|
||||||
<!-- 사원 프로필 리스트 -->
|
<!-- 사원 프로필 리스트 -->
|
||||||
<ProfileList
|
<ProfileList
|
||||||
@profileClick="handleProfileClick"
|
@profileClick="handleProfileClick"
|
||||||
:remainingVacationData="remainingVacationData"
|
:remainingVacationData="remainingVacationData"
|
||||||
/>
|
/>
|
||||||
|
<!-- 모달들은 화면 오버레이로 동작하므로 사이드바 내부에 두어도 무방 -->
|
||||||
|
<VacationModal
|
||||||
|
v-if="isModalOpen"
|
||||||
|
:isOpen="isModalOpen"
|
||||||
|
:myVacations="filteredMyVacations"
|
||||||
|
:receivedVacations="filteredReceivedVacations"
|
||||||
|
:userColors="userColors"
|
||||||
|
@close="isModalOpen = false"
|
||||||
|
/>
|
||||||
|
<VacationGrantModal
|
||||||
|
v-if="isGrantModalOpen"
|
||||||
|
:isOpen="isGrantModalOpen"
|
||||||
|
:targetUser="selectedUser"
|
||||||
|
:remainingQuota="remainingVacationData[selectedUser?.MEMBERSEQ] || 0"
|
||||||
|
@close="isGrantModalOpen = false"
|
||||||
|
@updateVacation="fetchRemainingVacation"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar-actions text-center my-3">
|
<div class="sidebar-actions text-center my-3">
|
||||||
<!-- 액션 버튼 -->
|
<!-- 액션 버튼 -->
|
||||||
@ -54,7 +54,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref, onMounted, nextTick, computed, watch } from "vue";
|
import { reactive, ref, onMounted, nextTick, computed, watch } from "vue";
|
||||||
import axios from "@api";
|
import axios from "@api";
|
||||||
@ -76,11 +75,11 @@
|
|||||||
const userListStore = useUserStore();
|
const userListStore = useUserStore();
|
||||||
const userList = ref([]);
|
const userList = ref([]);
|
||||||
const userColors = ref({});
|
const userColors = ref({});
|
||||||
const myVacations = ref([]); // 전체 "사용한 연차" 목록
|
const myVacations = ref([]); // 전체 "사용한 연차" 목록 (로그인한 사원의 휴가만)
|
||||||
const receivedVacations = ref([]); // 전체 "받은 연차" 목록
|
const receivedVacations = ref([]); // 전체 "받은 연차" 목록
|
||||||
const isModalOpen = ref(false);
|
const isModalOpen = ref(false);
|
||||||
const remainingVacationData = ref({});
|
const remainingVacationData = ref({});
|
||||||
|
const modalYear = ref(new Date().getFullYear());
|
||||||
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);
|
||||||
@ -95,6 +94,9 @@
|
|||||||
const holidayDates = ref(new Set());
|
const holidayDates = ref(new Set());
|
||||||
const fetchedEvents = ref([]);
|
const fetchedEvents = ref([]);
|
||||||
|
|
||||||
|
// 추가: 토글 상태 변수 (필요 시 사용 가능)
|
||||||
|
const toggledDates = ref(new Set());
|
||||||
|
|
||||||
const calendarOptions = reactive({
|
const calendarOptions = reactive({
|
||||||
plugins: [dayGridPlugin, interactionPlugin],
|
plugins: [dayGridPlugin, interactionPlugin],
|
||||||
initialView: "dayGridMonth",
|
initialView: "dayGridMonth",
|
||||||
@ -113,24 +115,32 @@
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await userStore.userInfo();
|
await userStore.userInfo();
|
||||||
await fetchRemainingVacation();
|
await fetchRemainingVacation();
|
||||||
|
// 초기 vacation history도 가져오기
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
await fetchVacationHistory(currentYear);
|
||||||
});
|
});
|
||||||
|
|
||||||
// lastRemainingYear 값이 변경될 때마다 해당 연도의 연차 내역을 재조회
|
// 연차 내역 API (초기 호출용)
|
||||||
watch(lastRemainingYear, async (newYear, oldYear) => {
|
async function fetchVacationHistory(year) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`vacation/history?year=${newYear}`);
|
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 || [];
|
myVacations.value = response.data.data.usedVacations || [];
|
||||||
receivedVacations.value = response.data.data.receivedVacations || [];
|
receivedVacations.value = response.data.data.receivedVacations || [];
|
||||||
} else {
|
} else {
|
||||||
console.warn("❌ 연차 내역을 불러오지 못했습니다.");
|
console.warn("❌ 연차 내역을 불러오지 못했습니다.");
|
||||||
myVacations.value = [];
|
myVacations.value = [];
|
||||||
receivedVacations.value = [];
|
receivedVacations.value = [];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("🚨 연차 데이터 불러오기 실패:", error);
|
console.error("🚨 연차 데이터 불러오기 실패:", error);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// lastRemainingYear 값이 변경될 때마다 해당 연도의 연차 내역 재조회
|
||||||
|
watch(lastRemainingYear, async (newYear, oldYear) => {
|
||||||
|
await fetchVacationHistory(newYear);
|
||||||
|
});
|
||||||
|
|
||||||
const fetchRemainingVacation = async () => {
|
const fetchRemainingVacation = async () => {
|
||||||
try {
|
try {
|
||||||
@ -147,42 +157,31 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 프로필 클릭 시 연차 내역 가져오기
|
// 프로필 클릭 시 연차 내역 가져오기
|
||||||
// 프로필 클릭 시 연차 내역 가져오기
|
const handleProfileClick = async (user) => {
|
||||||
const handleProfileClick = async (user) => {
|
try {
|
||||||
try {
|
if (isModalOpen.value) {
|
||||||
// 이미 모달이 열려있다면 토글하여 닫음
|
|
||||||
if (isModalOpen.value) {
|
|
||||||
isModalOpen.value = false;
|
isModalOpen.value = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (user.MEMBERSEQ === userStore.user.id) {
|
if (isGrantModalOpen.value) {
|
||||||
const year = new Date().getFullYear(); // 현재 연도
|
isGrantModalOpen.value = false;
|
||||||
// 연도 파라미터를 전달하여 전체 연도의 연차 내역을 조회
|
return;
|
||||||
const response = await axios.get(`vacation/history?year=${year}`);
|
}
|
||||||
if (response.status === 200 && response.data) {
|
if (user.MEMBERSEQ === userStore.user.id) {
|
||||||
myVacations.value = response.data.data.usedVacations || [];
|
const year = new Date().getFullYear();
|
||||||
receivedVacations.value = response.data.data.receivedVacations || [];
|
await fetchVacationHistory(year);
|
||||||
isModalOpen.value = true;
|
isModalOpen.value = true;
|
||||||
// 모달을 열 때 기준 연도 갱신
|
|
||||||
lastRemainingYear.value = year;
|
lastRemainingYear.value = year;
|
||||||
isGrantModalOpen.value = false;
|
isGrantModalOpen.value = false;
|
||||||
} else {
|
} else {
|
||||||
console.warn("❌ 연차 내역을 불러오지 못했습니다.");
|
selectedUser.value = user;
|
||||||
|
isGrantModalOpen.value = true;
|
||||||
|
isModalOpen.value = false;
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
// 이미 모달이 열려있다면 토글하여 닫음
|
console.error("🚨 연차 데이터 불러오기 실패:", error);
|
||||||
if (isGrantModalOpen.value) {
|
|
||||||
isGrantModalOpen.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
selectedUser.value = user;
|
|
||||||
isGrantModalOpen.value = true;
|
|
||||||
isModalOpen.value = false;
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
};
|
||||||
console.error("🚨 연차 데이터 불러오기 실패:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchUserList = async () => {
|
const fetchUserList = async () => {
|
||||||
try {
|
try {
|
||||||
@ -221,30 +220,29 @@ const handleProfileClick = async (user) => {
|
|||||||
return vacationCodeMap.value[typeCode] || "기타";
|
return vacationCodeMap.value[typeCode] || "기타";
|
||||||
};
|
};
|
||||||
|
|
||||||
// computed: lastRemainingYear과과 일치하는 항목만 필터링
|
// computed: lastRemainingYear과 일치하는 항목만 필터링
|
||||||
const filteredMyVacations = computed(() => {
|
const filteredMyVacations = computed(() => {
|
||||||
const filtered = myVacations.value.filter(vac => {
|
const filtered = myVacations.value.filter(vac => {
|
||||||
// vac.date가 없으면 vac.LOCVACUDT를 사용하도록 함
|
const dateStr = vac.date || vac.LOCVACUDT;
|
||||||
const dateStr = vac.date || vac.LOCVACUDT;
|
const year = dateStr ? dateStr.split("T")[0].substring(0, 4) : null;
|
||||||
const year = dateStr ? dateStr.split("T")[0].substring(0, 4) : null;
|
console.log("vacation year:", year, "lastRemainingYear:", lastRemainingYear.value);
|
||||||
console.log("vacation year:", year, "lastRemainingYear:", lastRemainingYear.value);
|
return year === String(lastRemainingYear.value);
|
||||||
return year === String(lastRemainingYear.value);
|
});
|
||||||
|
console.log("filteredMyVacations:", filtered);
|
||||||
|
return filtered;
|
||||||
});
|
});
|
||||||
console.log("filteredMyVacations:", filtered);
|
|
||||||
return filtered;
|
|
||||||
});
|
|
||||||
|
|
||||||
const filteredReceivedVacations = computed(() => {
|
const filteredReceivedVacations = computed(() => {
|
||||||
return receivedVacations.value.filter(vac => {
|
return receivedVacations.value.filter(vac => {
|
||||||
const dateStr = vac.date || vac.LOCVACUDT;
|
const dateStr = vac.date || vac.LOCVACUDT;
|
||||||
const year = dateStr ? dateStr.split("T")[0].substring(0, 4) : null;
|
const year = dateStr ? dateStr.split("T")[0].substring(0, 4) : null;
|
||||||
console.log("vacation year:", year, "lastRemainingYear:", lastRemainingYear.value);
|
console.log("vacation year:", year, "lastRemainingYear:", lastRemainingYear.value);
|
||||||
return dateStr && year === String(lastRemainingYear.value);
|
return dateStr && year === String(lastRemainingYear.value);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
function updateCalendarEvents() {
|
function updateCalendarEvents() {
|
||||||
|
// selectedDates에서 "delete"가 아닌 경우에 대해 이벤트 생성
|
||||||
const selectedEvents = Array.from(selectedDates.value)
|
const selectedEvents = Array.from(selectedDates.value)
|
||||||
.filter(([date, type]) => type !== "delete")
|
.filter(([date, type]) => type !== "delete")
|
||||||
.map(([date, type]) => ({
|
.map(([date, type]) => ({
|
||||||
@ -255,12 +253,18 @@ const filteredReceivedVacations = computed(() => {
|
|||||||
display: "background",
|
display: "background",
|
||||||
classNames: [getVacationTypeClass(type), "selected-event"]
|
classNames: [getVacationTypeClass(type), "selected-event"]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
// fetchedEvents에서, 만약 해당 날짜가 "delete" 상태라면
|
||||||
|
// 로그인한 사용자의(내) 이벤트만 제거하고, 다른 사용자의 이벤트는 그대로 둠.
|
||||||
const filteredFetchedEvents = fetchedEvents.value.filter(event => {
|
const filteredFetchedEvents = fetchedEvents.value.filter(event => {
|
||||||
if (event.saved) {
|
if (event.saved && selectedDates.value.get(event.start) === "delete") {
|
||||||
return selectedDates.value.get(event.start) !== "delete";
|
if (event.memberSeq === userStore.user.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
calendarEvents.value = [...filteredFetchedEvents, ...selectedEvents];
|
calendarEvents.value = [...filteredFetchedEvents, ...selectedEvents];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +278,7 @@ const filteredReceivedVacations = computed(() => {
|
|||||||
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 ||
|
||||||
@ -282,22 +287,36 @@ const filteredReceivedVacations = computed(() => {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 내 휴가 여부: LOCVACUDT의 앞 10자가 clickedDateStr과 같고, LOCVACRMM가 없거나 빈 문자열인 경우
|
||||||
|
const isMyVacation = myVacations.value.some(vac => {
|
||||||
|
const vacDate = vac.date ? String(vac.date).substring(0, 10) : "";
|
||||||
|
return vacDate === clickedDateStr &&
|
||||||
|
(!vac.LOCVACRMM || String(vac.LOCVACRMM).trim() === "");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isMyVacation) {
|
||||||
|
// 내 휴가인 경우: selectedDates에 "delete" 상태를 토글 (내 이벤트만 영향을 줌)
|
||||||
|
if (selectedDates.value.get(clickedDateStr) === "delete") {
|
||||||
|
selectedDates.value.delete(clickedDateStr);
|
||||||
|
} else {
|
||||||
|
selectedDates.value.set(clickedDateStr, "delete");
|
||||||
|
}
|
||||||
|
updateCalendarEvents();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 내 휴가가 없는 날짜: 일반 선택/토글 처리
|
||||||
if (selectedDates.value.has(clickedDateStr)) {
|
if (selectedDates.value.has(clickedDateStr)) {
|
||||||
|
console.log("일반 날짜 토글 off: 기존 선택 해제");
|
||||||
selectedDates.value.delete(clickedDateStr);
|
selectedDates.value.delete(clickedDateStr);
|
||||||
updateCalendarEvents();
|
updateCalendarEvents();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const unsentVacation = myVacations.value.find(
|
const type = halfDayType.value
|
||||||
(vac) => vac.LOCVACUDT && vac.LOCVACUDT.startsWith(clickedDateStr) && !vac.LOCVACRMM
|
? (halfDayType.value === "AM" ? "700101" : "700102")
|
||||||
);
|
: "700103";
|
||||||
if (unsentVacation) {
|
console.log("일반 날짜 토글 on: 선택 및 타입", type);
|
||||||
selectedDates.value.set(clickedDateStr, "delete");
|
selectedDates.value.set(clickedDateStr, type);
|
||||||
} else {
|
|
||||||
const type = halfDayType.value
|
|
||||||
? (halfDayType.value === "AM" ? "700101" : "700102")
|
|
||||||
: "700103";
|
|
||||||
selectedDates.value.set(clickedDateStr, type);
|
|
||||||
}
|
|
||||||
halfDayType.value = null;
|
halfDayType.value = null;
|
||||||
updateCalendarEvents();
|
updateCalendarEvents();
|
||||||
}
|
}
|
||||||
@ -307,43 +326,42 @@ const filteredReceivedVacations = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchVacationData(year, month) {
|
async function fetchVacationData(year, month) {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(`vacation/list/${year}/${month}`);
|
const response = await axios.get(`vacation/list/${year}/${month}`);
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
const vacationList = response.data;
|
const vacationList = response.data;
|
||||||
// 모달이 열려 있더라도 전달받은 연도가 기존 lastRemainingYear 다르면 업데이트
|
if (lastRemainingYear.value !== year) {
|
||||||
if (lastRemainingYear.value !== year) {
|
// 로그인한 사원의 휴가만 저장
|
||||||
myVacations.value = vacationList.filter(
|
myVacations.value = vacationList.filter(
|
||||||
(vac) => vac.MEMBERSEQ === userStore.user.id
|
(vac) => vac.MEMBERSEQ === userStore.user.id
|
||||||
);
|
);
|
||||||
lastRemainingYear.value = year;
|
lastRemainingYear.value = year;
|
||||||
// modalMonth는 그대로 유지 (월은 모달 업데이트 조건에서 제외)
|
}
|
||||||
|
const events = vacationList
|
||||||
|
.filter((vac) => !vac.LOCVACRMM)
|
||||||
|
.map((vac) => {
|
||||||
|
let dateStr = vac.LOCVACUDT ? vac.LOCVACUDT.split("T")[0] : "";
|
||||||
|
let backgroundColor = userColors.value[vac.MEMBERSEQ] || "#FFFFFF";
|
||||||
|
return {
|
||||||
|
title: getVacationType(vac.LOCVACTYP),
|
||||||
|
start: dateStr,
|
||||||
|
backgroundColor,
|
||||||
|
classNames: [getVacationTypeClass(vac.LOCVACTYP)],
|
||||||
|
saved: true,
|
||||||
|
memberSeq: vac.MEMBERSEQ, // 이벤트 소유자 정보
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((event) => event.start);
|
||||||
|
return events;
|
||||||
|
} else {
|
||||||
|
console.warn("📌 휴가 데이터를 불러오지 못함");
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
// 캘린더 이벤트 매핑
|
} catch (error) {
|
||||||
const events = vacationList
|
console.error("Error fetching vacation data:", error);
|
||||||
.filter((vac) => !vac.LOCVACRMM)
|
|
||||||
.map((vac) => {
|
|
||||||
let dateStr = vac.LOCVACUDT ? vac.LOCVACUDT.split("T")[0] : "";
|
|
||||||
let backgroundColor = userColors.value[vac.MEMBERSEQ] || "#FFFFFF";
|
|
||||||
return {
|
|
||||||
title: getVacationType(vac.LOCVACTYP),
|
|
||||||
start: dateStr,
|
|
||||||
backgroundColor,
|
|
||||||
classNames: [getVacationTypeClass(vac.LOCVACTYP)],
|
|
||||||
saved: true,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.filter((event) => event.start);
|
|
||||||
return events;
|
|
||||||
} else {
|
|
||||||
console.warn("📌 휴가 데이터를 불러오지 못함");
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
console.error("Error fetching vacation data:", error);
|
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function saveVacationChanges() {
|
async function saveVacationChanges() {
|
||||||
const selectedDatesArray = Array.from(selectedDates.value);
|
const selectedDatesArray = Array.from(selectedDates.value);
|
||||||
@ -416,6 +434,7 @@ const filteredReceivedVacations = computed(() => {
|
|||||||
const today = new Date();
|
const today = new Date();
|
||||||
const year = today.getFullYear();
|
const year = today.getFullYear();
|
||||||
const month = String(today.getMonth() + 1).padStart(2, "0");
|
const month = String(today.getMonth() + 1).padStart(2, "0");
|
||||||
|
await fetchVacationData(year, month);
|
||||||
await loadCalendarData(year, month);
|
await loadCalendarData(year, month);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user