diff --git a/src/components/commuters/CommuterCalendar.vue b/src/components/commuters/CommuterCalendar.vue index 1c6a2b6..b4c9b15 100644 --- a/src/components/commuters/CommuterCalendar.vue +++ b/src/components/commuters/CommuterCalendar.vue @@ -40,6 +40,7 @@ class="flatpickr-calendar-only" > + @@ -79,7 +80,7 @@ import FullCalendar from '@fullcalendar/vue3'; import dayGridPlugin from '@fullcalendar/daygrid'; import interactionPlugin from '@fullcalendar/interaction'; import CenterModal from '@c/modal/CenterModal.vue'; -import { computed, inject, onMounted, reactive, ref, watch } from 'vue'; +import { computed, inject, nextTick, onMounted, reactive, ref, watch } from 'vue'; import $api from '@api'; import 'flatpickr/dist/flatpickr.min.css'; import '@/assets/css/app-calendar.css'; @@ -89,6 +90,9 @@ import { useProjectStore } from '@/stores/useProjectStore'; import CommuterBtn from '@c/commuters/CommuterBtn.vue'; import CommuterProjectList from '@c/commuters/CommuterProjectList.vue'; import BackBtn from '@c/button/BackBtn.vue'; +import flatpickr from 'flatpickr'; +import monthSelectPlugin from 'flatpickr/dist/plugins/monthSelect/index'; +import 'flatpickr/dist/plugins/monthSelect/style.css'; const baseUrl = $api.defaults.baseURL.replace(/api\/$/, ''); const user = ref({}); @@ -110,6 +114,8 @@ const isModalOpen = ref(false); const commuters = ref([]); const monthlyCommuters = ref([]); +const calendarDatepicker = ref(null); +let fpInstance = null; // 출퇴근 컴포넌트 이벤트 핸들러 const handleWorkTimeUpdate = () => { @@ -400,5 +406,66 @@ onMounted(async () => { selectedProject.value = storedProject.PROJCTSEQ; checkedInProject.value = storedProject; } + + nextTick(() => { + // 달력 데이트피커를 위한 input 요소 동적 생성 + const datePickerInput = document.createElement('input'); + datePickerInput.type = 'text'; + datePickerInput.style.display = 'none'; + document.body.appendChild(datePickerInput); + calendarDatepicker.value = datePickerInput; + + // Flatpickr 초기화 (달 선택) + fpInstance = flatpickr(calendarDatepicker.value, { + dateFormat: "Y-m", + plugins: [ + new monthSelectPlugin({ + shorthand: true, + dateFormat: "Y-m", + altFormat: "F Y" + }) + ], + onOpen: function() { + document.querySelector('.flatpickr-input').style.visibility = 'hidden'; + }, + onChange: function(selectedDatesArr, dateStr) { + // 선택한 달의 첫날로 달력을 이동 + fullCalendarRef.value.getApi().gotoDate(dateStr + "-01"); + const [year, month] = dateStr.split("-"); + lastRemainingYear.value = parseInt(year, 10); + lastRemainingMonth.value = month; + loadCalendarData(lastRemainingYear.value, lastRemainingMonth.value); + }, + onClose: function() { + calendarDatepicker.value.style.display = "none"; + } + }); + + // FullCalendar 년월월(.fc-toolbar-title) 클릭 시 데이트피커 열기 + const titleEl = document.querySelector('.fc-toolbar-title'); + if (titleEl) { + titleEl.style.cursor = 'pointer'; + titleEl.addEventListener('click', () => { + const rect = titleEl.getBoundingClientRect(); + const dpEl = calendarDatepicker.value; + + dpEl.style.display = 'block'; + dpEl.style.position = 'fixed'; + dpEl.style.top = `${rect.bottom + window.scrollY}px`; + dpEl.style.left = `${rect.left + window.scrollX}px`; + dpEl.style.transform = 'translate(-50%, -50%)'; + dpEl.style.zIndex = '9999'; + dpEl.style.border = 'none'; + dpEl.style.outline = 'none'; + dpEl.style.backgroundColor = 'transparent'; + // 제목의 중앙 아래에 위치하도록 계산 + // 또는 CSS transform 사용 + // dpEl.style.setProperty('--left-position', `${rect.left + window.scrollX}px`); + // dpEl.style.transform = 'translateX(-50%)'; + fpInstance.open(); + }); + } + }); }); + diff --git a/src/components/input/UserFormInput.vue b/src/components/input/UserFormInput.vue index 37e076f..3545bf2 100644 --- a/src/components/input/UserFormInput.vue +++ b/src/components/input/UserFormInput.vue @@ -31,6 +31,8 @@ :maxLength="maxlength" :placeholder="title" @blur="$emit('blur')" + @click="handleDateClick" + ref="inputElement" />
{{ title }}를 확인해주세요.
@@ -39,7 +41,7 @@ diff --git a/src/components/list/ProjectCard.vue b/src/components/list/ProjectCard.vue index 5f841f9..7574d64 100644 --- a/src/components/list/ProjectCard.vue +++ b/src/components/list/ProjectCard.vue @@ -129,27 +129,31 @@ - - - - - - + +
+ +
+ +
+ +
{ + if (startInputElement) { + startInputElement.showPicker(); + } + }; + + const openEndDatePicker = () => { + if (endInputElement) { + endInputElement.showPicker(); + } + }; + + // 사용자 목록 업데이트 핸들러 const handleEditUserListUpdate = (userLists) => { selectedUsers.value = userLists; @@ -581,8 +606,18 @@ onMounted(async () => { user.value = userStore.user; convertAddressToCoordinates(); + + if (startDateInput.value) { + // FormInput 내부 input 찾기 + startInputElement = startDateInput.value.$el.querySelector('input[type="date"]'); + } + + if (endDateInput.value) { + endInputElement = endDateInput.value.$el.querySelector('input[type="date"]'); + } }); + diff --git a/src/components/projectlist/ProjectList.vue b/src/components/projectlist/ProjectList.vue index 92baeea..ef0c1ce 100644 --- a/src/components/projectlist/ProjectList.vue +++ b/src/components/projectlist/ProjectList.vue @@ -71,25 +71,29 @@ - - - - +
+ +
+
+ +
{ + if (startInputElement) { + startInputElement.showPicker(); + } + }; + + const openEndDatePicker = () => { + if (endInputElement) { + endInputElement.showPicker(); + } + }; + const addressData = ref({ postcode: '', @@ -333,6 +357,13 @@ await userStore.userInfo(); user.value = userStore.user; + if (startDateInput.value) { + // FormInput 내부 input 찾기 + startInputElement = startDateInput.value.$el.querySelector('input[type="date"]'); + } - }); + if (endDateInput.value) { + endInputElement = endDateInput.value.$el.querySelector('input[type="date"]'); + } + });