diff --git a/src/common/common.js b/src/common/common.js index 4ed7af5..d408742 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -47,11 +47,11 @@ const common = { * * @param {string} dateStr * @return - * 1. Date type 인 경우 예시 '25-02-24 12:02' + * 1. Date type 인 경우 예시 '2025-02-24 12:02' * 2. Date type 이 아닌 경우 입력값 리턴 * */ - dateFormatter(dateStr) { + dateFormatter(dateStr, type = null) { const date = new Date(dateStr); const dateCheck = date.getTime(); @@ -59,13 +59,26 @@ const common = { return dateStr; } else { const { year, month, day, hours, minutes } = this.formatDateTime(date); - return `${year}-${month}-${day} ${hours}:${minutes}`; + let callback = ''; + + if (type == 'YMD') { + callback = `${year}-${month}-${day}`; + } else if (type == 'MD') { + callback = `${month}-${day}`; + } else { + callback = `${year}-${month}-${day} ${hours}:${minutes}`; + } + + return callback; } }, - formatDateTime(date) { - const zeroFormat = num => (num < 10 ? `0${num}` : num); + formatDateTime(dateObj) { + const date = new Date(dateObj); + const dateCheck = date.getTime(); + if (isNaN(dateCheck)) return dateStr; + const zeroFormat = num => (num < 10 ? `0${num}` : num); return { year: date.getFullYear(), month: zeroFormat(date.getMonth() + 1), diff --git a/src/components/main/MainEventCalendar.vue b/src/components/main/MainEventCalendar.vue index 65ecac2..cb2609c 100644 --- a/src/components/main/MainEventCalendar.vue +++ b/src/components/main/MainEventCalendar.vue @@ -140,34 +140,6 @@ todaysCommuter(); }; - // 프로젝트 드롭 이벤트 핸들러 (ProjectList 컴포넌트에서 전달받음) - const handleProjectDrop = ({ event, targetProject }) => { - // 드래그된 프로젝트 데이터 가져오기 - const draggedProjectData = JSON.parse(event.dataTransfer.getData('application/json')); - - // 드래그한 프로젝트와 드롭한 프로젝트가 같으면 아무 동작 안 함 - if (draggedProjectData.PROJCTSEQ === targetProject.PROJCTSEQ) { - return; - } - - // 선택된 프로젝트 변경 - checkedInProject.value = targetProject; - projectStore.setSelectedProject(targetProject); - - // select 값도 변경 - selectedProject.value = targetProject.PROJCTSEQ; - - $api.patch('commuters/update', { - projctSeq: targetProject.PROJCTSEQ, - memberSeq: user.value.id, - }).then(res => { - if (res.status === 200) { - todaysCommuter(); - loadCommuters(); - } - }); - }; - // 오늘 출근 모든 사용자 조회 const todaysCommuter = async () => { const res = await $api.get(`commuters/todays`); @@ -195,14 +167,85 @@ const { data } = await $api.get(`main/eventList?${param}`); const res = data?.data; + // 기존의 공휴일 이벤트는 유지 + const holidayEvents = calendarEvents.value.filter(event => event.classNames?.includes('holiday-event')); + calendarEvents.value = [...holidayEvents]; + // 생일자 - if (res?.memberBirthdayList?.length) monthBirthdayList.value = [...res.memberBirthdayList]; + if (res?.memberBirthdayList?.length) { + monthBirthdayList.value = [...res.memberBirthdayList]; + res.memberBirthdayList.forEach(member => { + addEvent($common.dateFormatter(member.MEMBERBTH, 'YMD'), 'birthday', `${member.MEMBERNAM}`); + }); + } // 휴가자 - if (res?.memberVacationList?.length) monthVacationList.value = [...res.memberVacationList]; + if (res?.memberVacationList?.length) { + monthVacationList.value = [...res.memberVacationList]; + res.memberVacationList.forEach(member => { + addEvent($common.dateFormatter(member.LOCVACUDT, 'YMD'), 'vacation', `${member.MEMBERNAM}`); + }); + } + + // 생일파티 + if (res?.birthdayPartyList?.length) { + res.birthdayPartyList.forEach(party => { + addEvent(party.EVENTDT, 'birthdayParty', '생일파티'); + }); + } + + // 회식 + if (res?.dinnerList?.length) { + res.dinnerList.forEach(dinner => { + addEvent(dinner.EVENTDT, 'dinner', '회식'); + }); + } + + // 티타임 + if (res?.teaTimeList?.length) { + res.teaTimeList.forEach(tea => { + addEvent(tea.EVENTDT, 'teaTime', '티타임'); + }); + } + + // 워크샵 + if (res?.workshopList?.length) { + res.workshopList.forEach(workshop => { + addEvent(workshop.EVENTDT, 'workshop', '워크샵'); + }); + } }; - // 해당일 기준 이벤트 리스트 + // 달력에 이벤트 데이터 추가 + const addEvent = (date, type, title) => { + // 생일의 경우 달력의 현재 년도로 변경하여 처리 + if (type === 'birthday') { + const calendarApi = fullCalendarRef.value?.getApi(); + if (calendarApi) { + const dateArr = calendarApi.currentData.viewTitle.split(' '); + const calendarYear = parseInt(dateArr[0].replace(/\D/g, ''), 10); + const birthDate = $common.dateFormatter(date, 'MD'); + date = `${calendarYear}-${birthDate}`; + } + } + + // 같은 날짜와 타입의 이벤트가 이미 있는지 확인 + const existingEvent = calendarEvents.value.find( + event => $common.dateFormatter(event.start, 'MD') === $common.dateFormatter(date, 'MD') && event.type == type, + ); + + // 없는 경우에만 추가 + if (!existingEvent) { + calendarEvents.value.push({ + start: date, + type: type, + title: title, + classNames: [`${type}-event`], + }); + } + }; + + // 해당일 기준 이벤트 리스트 필터링 const useFilterEventList = (month, day) => { // 생일자 if (monthBirthdayList.value) { @@ -234,13 +277,14 @@ try { // 현재 표시 중인 월의 공휴일 정보 가져오기 const holidayEvents = await fetchHolidays(currentYear, String(currentMonth).padStart(2, '0')); - // 기존 이벤트에서 공휴일 이벤트를 제외한 이벤트만 필터링 - const existingEvents = calendarEvents.value.filter(event => !event.classNames?.includes('holiday-event')); - // 필터링된 이벤트와 새로 가져온 공휴일 이벤트 병합 - calendarEvents.value = [...existingEvents, ...holidayEvents]; + calendarEvents.value = [...holidayEvents]; // 공휴일 정보로 초기화 - // 출근 정보 - await loadCommuters(); + // 이벤트 데이터 가져오기 + const param = new URLSearchParams(); + param.append('year', currentYear); + param.append('month', currentMonth); + param.append('day', '1'); // 해당 월의 첫날 + await fetchEventList(param); } catch (error) { console.error('공휴일 정보 로딩 실패:', error); } @@ -274,47 +318,81 @@ return !isWeekend && !isHoliday; }; - // 날짜 클릭 이벤트 함수 - let todayElement = null; + // 이벤트 타입 정의 + const eventTypes = [ + { type: 'birthdayParty', code: '300203', title: '생일파티' }, + { type: 'dinner', code: '300204', title: '회식' }, + { type: 'teaTime', code: '300205', title: '티타임' }, + { type: 'workshop', code: '300206', title: '워크샵' }, + ]; + + // 날짜 클릭 이벤트 함수 수정 const handleDateClick = info => { - const clickedDate = dayjs(info.date).format('YYYY-MM-DD'); + // const clickedDate = dayjs(info.dateStr); + const { month, day } = $common.formatDateTime(new Date(info.dateStr)); - // 클릭한 날짜에 월별 출근 정보가 있는지 확인 - const dateCommuters = monthlyCommuters.value.filter(commuter => commuter.COMMUTDAY === clickedDate); + // 클릭한 날짜의 이벤트 리스트 필터링 + useFilterEventList(month, day); - // 출근 기록이 있는 경우에만 모달 열기 - if (dateCommuters.length > 0) { - eventDate.value = clickedDate; - isModalOpen.value = true; - } + // 팝오버 HTML 생성 + const popoverContent = ` +
+ ${eventTypes + .map( + event => ` + + `, + ) + .join('')} +
+ `; - if (isSelectableDate(info.date)) { - const isToday = dayjs(info.date).isSame(dayjs(), 'day'); + // 기존 팝오버 제거 + const existingPopovers = document.querySelectorAll('.event-popover'); + existingPopovers.forEach(popover => popover.remove()); - if (isToday) { - // 오늘 날짜 클릭 시 클래스 제거하고 요소 저장 - todayElement = info.dayEl; - todayElement.classList.remove('fc-day-today'); - } else if (todayElement) { - // 다른 날짜 클릭 시 저장된 오늘 요소에 클래스 다시 추가 - todayElement.classList.add('fc-day-today'); - todayElement = null; + // 새 팝오버 생성 + const popover = document.createElement('div'); + popover.className = 'event-popover position-absolute bg-white shadow rounded z-3'; + popover.innerHTML = popoverContent; + popover.style.top = `${info.jsEvent.pageY}px`; + popover.style.left = `${info.jsEvent.pageX}px`; + + // 아이콘 클릭 이벤트 추가 + popover.addEventListener('click', e => { + const target = e.target; + if (target.classList.contains('event-icon-select')) { + const eventType = target.dataset.eventType; + const eventTitle = target.dataset.eventTitle; + const date = target.dataset.date; + + // 이벤트 추가 + addEvent(date, eventType, eventTitle); + + // 팝오버 닫기 + popover.remove(); } - } + }); + + // 문서에 팝오버 추가 + document.body.appendChild(popover); + + // 팝오버 외부 클릭 시 닫기 + document.addEventListener('click', e => { + if (!e.target.closest('.event-popover') && !e.target.closest('.fc-daygrid-day')) { + const popovers = document.querySelectorAll('.event-popover'); + popovers.forEach(p => p.remove()); + } + }); }; - // 바깥 클릭 시 todayElement 클래스 복구 - document.addEventListener( - 'click', - event => { - if (todayElement && !event.target.closest('.fc-daygrid-day')) { - todayElement.classList.add('fc-day-today'); - todayElement = null; - } - }, - true, - ); - // 날짜 셀 클래스 추가 함수 const getCellClassNames = arg => { const cellDate = dayjs(arg.date); @@ -379,6 +457,50 @@ } }; + // 이벤트 아이콘 표시 함수 + const handleEventContent = item => { + if (!item.event) return null; + + // 공휴일인 경우 텍스트로 표시 + if (item.event.classNames?.includes('holiday-event')) { + return { + html: `
${item.event.title}
`, + }; + } + + // 현재 이벤트의 타입만 확인 + const eventType = item.event.extendedProps.type; + if (!eventType) return null; + + let iconCode = ''; + switch (eventType) { + case 'birthday': + iconCode = '300201'; + break; + case 'vacation': + iconCode = '300202'; + break; + case 'birthdayParty': + iconCode = '300203'; + break; + case 'dinner': + iconCode = '300204'; + break; + case 'teaTime': + iconCode = '300205'; + break; + case 'workshop': + iconCode = '300206'; + break; + default: + return null; + } + + return { + html: ``, + }; + }; + // 캘린더 옵션 설정 const calendarOptions = reactive({ plugins: [dayGridPlugin, interactionPlugin], @@ -392,19 +514,14 @@ events: calendarEvents, eventOrder: 'sortIdx', contentHeight: 'auto', - // eventContent: calendarCommuter, - // 날짜 선택 관련 옵션 수정 + eventContent: handleEventContent, selectable: true, selectAllow: selectInfo => isSelectableDate(selectInfo.start), dateClick: handleDateClick, dayCellClassNames: getCellClassNames, - - // 날짜 클릭 비활성화를 위한 추가 설정 unselectAuto: true, droppable: false, eventDisplay: 'block', - - // 커스텀 버튼 정의 customButtons: { prev: { text: 'PREV', @@ -470,9 +587,6 @@ checkedInProject.value = storedProject; } - // 이벤트 카테고리 호출 - await fetchCategoryList(); - // 오늘 기준 데이터 호출 const { year, month, day } = $common.getToday(); const param = new URLSearchParams(); @@ -480,7 +594,24 @@ param.append('month', month); param.append('day', day); + // 이벤트 카테고리 호출 + await fetchCategoryList(); await fetchEventList(param); useFilterEventList(month, day); }); + diff --git a/src/components/main/MainEventList.vue b/src/components/main/MainEventList.vue index b7b6fdc..7c87e96 100644 --- a/src/components/main/MainEventList.vue +++ b/src/components/main/MainEventList.vue @@ -1,7 +1,10 @@