250328_work

This commit is contained in:
nevermoregb 2025-03-28 09:22:44 +09:00
parent c4ff151581
commit a540feb851
5 changed files with 268 additions and 115 deletions

View File

@ -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),

View File

@ -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 = `
<div class="d-flex flex-wrap gap-2 p-2">
${eventTypes
.map(
event => `
<img
src="${baseUrl}img/main-category-img/main-${event.code}.png"
class="event-icon-select"
data-event-type="${event.type}"
data-event-title="${event.title}"
data-date="${info.dateStr}"
style="width: 25px; height: 25px; cursor: pointer;"
/>
`,
)
.join('')}
</div>
`;
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: `<div class="holiday-text" style="color: white;">${item.event.title}</div>`,
};
}
//
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: `<img src="${baseUrl}img/main-category-img/main-${iconCode}.png" class="calendar-event-icon" style="width: 20px; height: 20px; margin: 2px;" />`,
};
};
//
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);
});
</script>
<style>
.fc-h-event {
background-color: transparent;
}
.event-popover {
padding: 8px;
border: 1px solid #ddd;
}
.event-icon-select:hover {
transform: scale(1.1);
transition: transform 0.2s;
}
</style>

View File

@ -1,7 +1,10 @@
<template>
<div class="">
<template v-for="category in categoryList" :key="category.CMNCODVAL">
<div class="border border-2 mt-3 card p-2">
<div
v-if="(category.CMNCODVAL === 300201 && birthdayList?.length) || (category.CMNCODVAL === 300202 && vacationList?.length)"
class="border border-2 mt-3 card p-2"
>
<div class="row g-2">
<div class="col-3 mx-0 px-0">
<div class="ratio ratio-1x1">
@ -14,36 +17,10 @@
</div>
<div class="col-9 mx-0 px-0">
<template v-if="category.CMNCODVAL === 300201">
<div class="ms-2">
<ul class="row gx-1 mb-0 list-inline d-flex align-items-center">
<li class="col-4 me-0" v-for="member in birthdayList" :key="member.MEMBERSEQ">
<div class="ratio ratio-1x1 mb-0 profile-list">
<img
:src="`${baseUrl}upload/img/profile/${member.MEMBERPRF}`"
alt="User Profile"
class="rounded-circle"
@error="$event.target.src = '/img/icons/icon.png'"
/>
</div>
</li>
</ul>
</div>
<MainMemberProfile :members="birthdayList" :baseUrl="baseUrl" />
</template>
<template v-if="category.CMNCODVAL === 300202">
<div class="ms-2">
<ul class="row gx-1 mb-0 list-inline d-flex align-items-center">
<li class="col-4 me-0" v-for="member in vacationList" :key="member.MEMBERSEQ">
<div class="ratio ratio-1x1 mb-0 profile-list">
<img
:src="`${baseUrl}upload/img/profile/${member.MEMBERPRF}`"
alt="User Profile"
class="rounded-circle"
@error="$event.target.src = '/img/icons/icon.png'"
/>
</div>
</li>
</ul>
</div>
<MainMemberProfile :members="vacationList" :baseUrl="baseUrl" />
</template>
</div>
</div>
@ -54,6 +31,7 @@
<script setup>
import { defineEmits } from 'vue';
import MainMemberProfile from '@c/main/MainMemberProfile.vue';
const props = defineProps({
project: {

View File

@ -0,0 +1,31 @@
<template>
<div class="ms-2">
<ul class="row gx-1 mb-0 list-inline d-flex align-items-center">
<li class="col-4 me-0" v-for="(member, index) in members" :key="index">
<div class="ratio ratio-1x1 mb-0 profile-list">
<img
:src="`${baseUrl}upload/img/profile/${member.MEMBERPRF}`"
alt="User Profile"
class="rounded-circle"
@error="$event.target.src = '/img/icons/icon.png'"
/>
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
members: {
type: Array,
required: true,
},
baseUrl: {
type: String,
required: true,
},
},
};
</script>

View File

@ -1,7 +1,7 @@
<template>
<MainCalendar />
<MainEventCalendar />
</template>
<script setup>
import MainCalendar from '@/components/main/MainEventCalendar.vue';
import MainEventCalendar from '@/components/main/MainEventCalendar.vue';
</script>