휴가 수정정

This commit is contained in:
dyhj625 2025-02-27 10:33:48 +09:00
parent f71651729e
commit bb1715c71b
5 changed files with 223 additions and 53 deletions

View File

@ -44,6 +44,30 @@
.flatpickr-calendar:after {
display: none !important;
}
/* 기본 스타일은 그대로 두고, 데이트피커 인풋의 추가 스타일 정의 */
.fc-toolbar-title {
cursor: pointer;
}
/* 클릭 가능한 날짜 (오늘 + 미래) */
.fc-daygrid-day.clickable {
cursor: pointer;
transition: background-color 0.2s ease-in-out;
}
/* 마우스를 올렸을 때 효과 */
.fc-daygrid-day.clickable:hover {
background-color: rgba(0, 0, 0, 0.05); /* 연한 배경 효과 */
}
/* 주말 (토요일, 일요일) 및 공휴일 */
.fc-day-sat-sun {
cursor: not-allowed !important;
opacity: 0.6; /* 흐려 보이게 */
}
/* 과거 날짜 (오늘 이전) */
.fc-daygrid-day.past {
cursor: not-allowed !important;
opacity: 0.6; /* 흐려 보이게 */
}
/* 본인 모달 */
@ -116,12 +140,32 @@
/* 버튼 호버 효과 */
.custom-button:hover i {
color: #007BFF; /* 호버 시 아이콘 색상 변경 (파란색) */
color: #ff0800; /* 호버 시 아이콘 색상 변경 */
}
/* 모달 배경 투명하게 */
.modal-dialog {
background: none !important; /* 배경 제거 */
box-shadow: none !important; /* 음영 제거 */
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
/* 모달 내용 스타일 */
.modal-content {
background: #fff; /* 기존 흰색 배경 유지 */
border-radius: 8px;
box-shadow: none !important; /* 내부 음영 제거 */
padding: 20px;
max-width: 500px;
width: 100%;
}
.grayscaleImg {
filter: grayscale(100%);

View File

@ -2,21 +2,21 @@
<div class="menu gap-4 justify-content-center mt-5">
<button
class="btn btn-info"
class="btn btn-warning"
:class="{ active: halfDayType === 'AM' }"
@click="toggleHalfDay('AM')"
>
<i class="bi bi-sun"></i>
</button>
<button
class="btn btn-warning"
class="btn btn-info"
:class="{ active: halfDayType === 'PM' }"
@click="toggleHalfDay('PM')"
>
<i class="bi bi-moon"></i>
</button>
<div class="save-button-container">
<button class="btn btn-success" @click="addVacationRequests">
<button class="btn btn-success" @click="addVacationRequests" :disabled="isDisabled">
</button>
</div>
@ -26,6 +26,10 @@
<script setup>
import { defineEmits, ref } from "vue";
defineProps({
isDisabled: Boolean
});
const emit = defineEmits(["toggleHalfDay", "addVacationRequests"]);
const halfDayType = ref(null);
@ -39,6 +43,73 @@ const addVacationRequests = () => {
};
</script>
<style scoped>
<style scoped>
/* 버튼 기본 스타일 */
.btn {
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease-in-out;
}
/* 마우스를 올렸을 때 */
.btn:hover {
filter: brightness(90%);
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.2);
transform: scale(1.05);
}
/* 버튼이 눌렸을 때 */
.btn:active {
transform: scale(0.9);
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2);
}
/* 선택된 (눌린) 버튼 */
.btn.active {
border: 3px solid #fff; /* 흰색 테두리 강조 */
box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.3);
transform: scale(1.1);
}
/* AM 버튼 (선택된 상태) */
.btn-warning.active {
background-color: #ffca2c !important; /* 진한 노란색 */
color: black;
}
/* PM 버튼 (선택된 상태) */
.btn-info.active {
background-color: #0b5ed7 !important; /* 진한 파란색 */
color: white;
}
/* ✔ 버튼 */
.btn-success {
font-size: 24px;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease-in-out;
}
/* ✔ 버튼 마우스 오버 */
.btn-success:hover {
background-color: #198754;
box-shadow: 0px 4px 10px rgba(25, 135, 84, 0.4);
transform: scale(1.1);
}
/* ✔ 버튼 클릭 */
.btn-success:active {
transform: scale(0.95);
box-shadow: 0px 2px 5px rgba(25, 135, 84, 0.2);
}
</style>

View File

@ -7,7 +7,7 @@
<div class="modal-body">
<p>선물할 연차 개수를 선택하세요.</p>
<div class="justify-content-center d-sm-flex gap-sm-3 align-items-md-center">
<div class="justify-content-center d-sm-flex gap-sm-3 align-items-md-center mt-8">
<button @click="decreaseCount" :disabled="grantCount < 2" class="count-btn">-</button>
<span class="text-dark fw-bold fs-4">{{ grantCount }}</span>
<button @click="increaseCount" :disabled="grantCount >= availableQuota" class="count-btn">+</button>

View File

@ -31,7 +31,7 @@
</div>
<!-- 연차 데이터 없음 -->
<p v-else class="text-sm-center mt-5 text-gray">
<p v-else class="text-sm-center mt-10 text-gray">
🚫 사용한 연차가 없습니다.
</p>
</div>

View File

@ -9,6 +9,7 @@
<HalfDayButtons
@toggleHalfDay="toggleHalfDay"
@addVacationRequests="saveVacationChanges"
:isDisabled="!hasChanges"
/>
</div>
<ProfileList
@ -106,6 +107,64 @@
const calendarDatepicker = ref(null);
let fpInstance = null;
/** ✅ 변경사항 여부 확인 */
const hasChanges = computed(() => {
return (
selectedDates.value.size > 0 ||
myVacations.value.some(vac => selectedDates.value.has(vac.date.split("T")[0]))
);
});
/** ✅ selectedDates가 변경될 때 버튼 상태 즉시 업데이트 */
watch(
() => Array.from(selectedDates.value.keys()), // keys() Array
(newKeys) => {
},
{ deep: true }
);
function handleDateClick(info) {
const clickedDateStr = info.dateStr;
const clickedDate = info.date;
const todayStr = new Date().toISOString().split("T")[0];
if (
clickedDate.getDay() === 0 ||
clickedDate.getDay() === 6 ||
holidayDates.value.has(clickedDateStr) ||
clickedDateStr < todayStr
) {
return;
}
const isMyVacation = myVacations.value.some(vac => {
const vacDate = vac.date ? String(vac.date).substring(0, 10) : "";
return vacDate === clickedDateStr && !vac.receiverId;
});
if (isMyVacation) {
if (selectedDates.value.get(clickedDateStr) === "delete") {
selectedDates.value.delete(clickedDateStr);
} else {
selectedDates.value.set(clickedDateStr, "delete");
}
updateCalendarEvents();
return;
}
if (selectedDates.value.has(clickedDateStr)) {
selectedDates.value.delete(clickedDateStr);
updateCalendarEvents();
return;
}
const type = halfDayType.value
? (halfDayType.value === "AM" ? "700101" : "700102")
: "700103";
selectedDates.value.set(clickedDateStr, type);
halfDayType.value = null;
updateCalendarEvents();
}
const calendarOptions = reactive({
plugins: [dayGridPlugin, interactionPlugin],
initialView: "dayGridMonth",
@ -309,47 +368,6 @@
return "full-day";
};
function handleDateClick(info) {
const clickedDateStr = info.dateStr;
const clickedDate = info.date;
const todayStr = new Date().toISOString().split("T")[0];
if (
clickedDate.getDay() === 0 ||
clickedDate.getDay() === 6 ||
holidayDates.value.has(clickedDateStr) ||
clickedDateStr < todayStr
) {
return;
}
const isMyVacation = myVacations.value.some(vac => {
const vacDate = vac.date ? String(vac.date).substring(0, 10) : "";
return vacDate === clickedDateStr && !vac.receiverId;
});
if (isMyVacation) {
if (selectedDates.value.get(clickedDateStr) === "delete") {
selectedDates.value.delete(clickedDateStr);
} else {
selectedDates.value.set(clickedDateStr, "delete");
}
updateCalendarEvents();
return;
}
if (selectedDates.value.has(clickedDateStr)) {
selectedDates.value.delete(clickedDateStr);
updateCalendarEvents();
return;
}
const type = halfDayType.value
? (halfDayType.value === "AM" ? "700101" : "700102")
: "700103";
selectedDates.value.set(clickedDateStr, type);
halfDayType.value = null;
updateCalendarEvents();
}
function toggleHalfDay(type) {
halfDayType.value = halfDayType.value === type ? null : type;
}
@ -392,6 +410,7 @@
}
async function saveVacationChanges() {
if (!hasChanges.value) return;
const selectedDatesArray = Array.from(selectedDates.value);
const vacationsToAdd = selectedDatesArray
.filter(([date, type]) => type !== "delete")
@ -458,6 +477,46 @@
await nextTick();
fullCalendarRef.value.getApi().refetchEvents();
}
/** ✅ 오늘 이후의 날짜만 클릭 가능하도록 설정 */
function markClickableDates() {
nextTick(() => {
const todayStr = new Date().toISOString().split("T")[0]; // YYYY-MM-DD
const todayObj = new Date(todayStr);
document.querySelectorAll(".fc-daygrid-day").forEach((cell) => {
const dateStr = cell.getAttribute("data-date");
if (!dateStr) return; //
const dateObj = new Date(dateStr);
// (, )
if (dateObj.getDay() === 0 || dateObj.getDay() === 6 || holidayDates.value.has(dateStr)) {
cell.classList.remove("clickable");
cell.classList.add("fc-day-sat-sun");
}
// ( )
else if (dateObj < todayObj) {
cell.classList.remove("clickable");
cell.classList.add("past"); //
}
// & ( )
else {
cell.classList.add("clickable");
cell.classList.remove("past", "fc-day-sat-sun");
}
});
});
}
/** ✅ onMounted 및 달력 변경 시 실행 */
onMounted(() => {
markClickableDates();
});
watch([holidayDates, lastRemainingYear, lastRemainingMonth], () => {
markClickableDates();
});
onMounted(async () => {
await fetchUserList();
@ -471,10 +530,6 @@
</script>
<style>
/* 기본 스타일은 그대로 두고, 데이트피커 인풋의 추가 스타일 정의 */
.fc-toolbar-title {
cursor: pointer;
}
/* 데이트피커 인풋은 Flatpickr에서 동적으로 스타일 적용됨 */
</style>