메인 이벤트 달력

This commit is contained in:
nevermoregb 2025-03-31 11:22:26 +09:00
parent a540feb851
commit c1274cf9a0
5 changed files with 486 additions and 286 deletions

View File

@ -76,7 +76,7 @@ const common = {
formatDateTime(dateObj) {
const date = new Date(dateObj);
const dateCheck = date.getTime();
if (isNaN(dateCheck)) return dateStr;
if (isNaN(dateCheck)) return dateObj;
const zeroFormat = num => (num < 10 ? `0${num}` : num);
return {

View File

@ -0,0 +1,201 @@
<template>
<div class="event-modal position-fixed bg-white shadow rounded" :style="modalStyle">
<!-- 이벤트 선택 화면 -->
<div v-if="!selectedEventType" class="d-flex flex-wrap gap-2 p-2">
<div v-for="event in eventTypes" :key="event.code" class="event-icon-wrapper position-relative">
<img
:src="`${baseUrl}img/main-category-img/main-${event.code}.png`"
class="event-icon-select"
style="width: 25px; height: 25px; cursor: pointer"
@click="handleEventClick(event)"
/>
<!-- X 표시 수정 -->
<span v-if="isEventExists(event.type)" class="cancel-mark"> × </span>
</div>
</div>
<!-- 입력 화면 -->
<div v-else class="p-2" style="min-width: 200px">
<div class="d-flex justify-content-between align-items-center mb-2">
<small class="text-muted">{{ getEventTitle(selectedEventType) }}</small>
<button class="btn-close btn-close-sm" style="font-size: 8px" @click="resetForm"></button>
</div>
<div class="mb-2">
<input
type="text"
class="form-control form-control-sm py-1"
style="height: 25px; font-size: 12px"
placeholder="장소"
v-model="eventPlace"
/>
</div>
<div class="mb-2">
<input type="time" class="form-control form-control-sm py-1" style="height: 25px; font-size: 12px" v-model="eventTime" />
</div>
<div class="text-end">
<button class="btn btn-primary btn-sm py-1" style="font-size: 12px; height: 25px; line-height: 1" @click="handleSubmit">
등록
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
const props = defineProps({
position: {
type: Object,
required: true,
default: () => ({ x: 0, y: 0 }),
},
selectedDate: {
type: String,
required: true,
},
baseUrl: {
type: String,
required: true,
},
dateEvents: {
type: Array,
},
});
const emit = defineEmits(['select', 'delete', 'insert']);
//
const selectedEventType = ref(null);
const eventPlace = ref('');
const eventTime = ref('');
const eventTypes = [
{ type: 'birthdayParty', code: '300203', title: '생일파티' },
{ type: 'dinner', code: '300204', title: '회식' },
{ type: 'teaTime', code: '300205', title: '티타임' },
{ type: 'workshop', code: '300206', title: '워크샵' },
];
const getEventTitle = type => {
return eventTypes.find(event => event.type === type)?.title || '';
};
const isEventExists = type => {
return props.dateEvents?.some(event => event.type === type);
};
const handleEventClick = event => {
if (isEventExists(event.type)) {
if (confirm('이벤트를 취소하시겠습니까?')) {
emit('delete', {
date: props.selectedDate,
code: event.code,
title: event.title,
});
}
} else {
selectedEventType.value = event.code;
}
};
const handleSubmit = () => {
if (!eventPlace.value || !eventTime.value) {
alert('장소와 시간을 모두 입력해주세요');
return;
}
emit('insert', {
date: props.selectedDate,
code: selectedEventType.value,
title: getEventTitle(selectedEventType.value),
place: eventPlace.value,
time: eventTime.value,
});
};
const resetForm = () => {
selectedEventType.value = null;
eventPlace.value = '';
eventTime.value = '';
};
// computed
const modalStyle = computed(() => {
const modalWidth = 200; //
const modalHeight = 150; //
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
let x = props.position?.x || 0;
let y = props.position?.y || 0;
//
if (x + modalWidth > viewportWidth) {
x = viewportWidth - modalWidth - 10;
}
if (x < 0) {
x = 10;
}
if (y + modalHeight > viewportHeight) {
y = viewportHeight - modalHeight - 10;
}
if (y < 0) {
y = 10;
}
return {
left: `${x}px`,
top: `${y}px`,
zIndex: 1050,
maxWidth: '90vw', // 90%
maxHeight: '90vh', // 90%
};
});
</script>
<style scoped>
.event-icon-wrapper {
position: relative;
display: inline-block;
}
.event-icon-select {
transition: transform 0.2s;
}
.event-icon-select:hover {
transform: scale(1.1);
}
.cancel-mark {
position: absolute;
top: -8px;
right: -8px;
width: 16px;
height: 16px;
background-color: #dc3545;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: bold;
line-height: 1;
}
.event-modal {
min-width: 120px;
max-width: 300px;
overflow: auto;
}
/* 작은 화면에서의 스타일 */
@media (max-width: 576px) {
.event-modal {
min-width: 100px;
font-size: 0.9em;
}
}
</style>

View File

@ -15,30 +15,18 @@
{{ user.name }}
</p>
<CommuterBtn
:userId="user.id"
:checkedInProject="checkedInProject || {}"
@workTimeUpdated="handleWorkTimeUpdate"
@leaveTimeUpdated="handleLeaveTimeUpdate"
ref="workTimeComponentRef"
/>
<CommuterBtn :userId="user.id" :checkedInProject="checkedInProject || {}" ref="workTimeComponentRef" />
<MainEventList
:categoryList="categoryList"
:baseUrl="baseUrl"
:birthdayList="birthdayList"
:vacationList="vacationList"
:birthdayPartyList="birthdayPartyList"
:dinnerList="dinnerList"
:teaTimeList="teaTimeList"
:workShopList="workShopList"
/>
<!-- <CommuterProjectList
:categoryList="categoryList"
:project="project"
:commuters="commuters"
:baseUrl="baseUrl"
:user="user"
:selectedProject="selectedProject"
:checkedInProject="checkedInProject"
@drop="handleProjectDrop"
/> -->
</div>
</div>
@ -60,41 +48,24 @@
</div>
</div>
<CenterModal :display="isModalOpen" @close="closeModal">
<template #title>
{{ eventDate }}
</template>
<template #body>
<div v-if="selectedDateCommuters.length > 0">
<div v-for="(commuter, index) in selectedDateCommuters" :key="index">
<div class="d-flex align-items-center my-2">
<img
:src="`${baseUrl}upload/img/profile/${commuter.profile}`"
class="rounded-circle me-2 w-px-50 h-px-50"
@error="$event.target.src = '/img/icons/icon.png'"
<EventModal
v-if="showModal"
:position="modalPosition"
:selected-date="selectedDate"
:base-url="baseUrl"
:date-events="currentDateEvents"
@select="handleEventSelect"
@delete="handleEventDelete"
@insert="handleEventInsert"
@close="handleCloseModal"
/>
<span class="text-white fw-bold rounded py-1 px-3" :style="`background: ${commuter.projctcolor} !important;`">{{
commuter.memberName
}}</span>
<div class="ms-auto text-start fw-bold">{{ commuter.COMMUTCMT }} ~ {{ commuter.COMMUTLVE || '00:00:00' }}</div>
</div>
</div>
</div>
</template>
<template #footer>
<BackBtn @click="closeModal" />
</template>
</CenterModal>
</template>
<script setup>
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 { inject, onMounted, reactive, ref, watch } from 'vue';
import $api from '@api';
import 'flatpickr/dist/flatpickr.min.css';
import '@/assets/css/app-calendar.css';
@ -102,13 +73,12 @@
import { useUserInfoStore } from '@/stores/useUserInfoStore';
import { useProjectStore } from '@/stores/useProjectStore';
import CommuterBtn from '@c/commuters/CommuterBtn.vue';
import CommuterProjectList from '@c/commuters/CommuterProjectList.vue';
import MainEventList from '@c/main/MainEventList.vue';
import BackBtn from '@c/button/BackBtn.vue';
import EventModal from '@c/main/EventModal.vue';
import { useToastStore } from '@s/toastStore';
const baseUrl = import.meta.env.VITE_DOMAIN;
const user = ref({});
const project = ref({});
const userStore = useUserInfoStore();
const projectStore = useProjectStore();
@ -116,39 +86,45 @@
const fullCalendarRef = ref(null);
const workTimeComponentRef = ref(null);
const calendarEvents = ref([]);
const eventDate = ref('');
const selectedProject = ref(null);
const checkedInProject = ref(null);
//
const isModalOpen = ref(false);
const commuters = ref([]);
const monthlyCommuters = ref([]);
//
const showModal = ref(false);
const modalPosition = ref({ x: 0, y: 0 });
const selectedDate = ref('');
//
const $common = inject('common');
const toastStore = useToastStore();
//
const handleWorkTimeUpdate = () => {
todaysCommuter();
loadCommuters();
};
//
const pressTimer = ref(null);
const longPressDelay = 500; // 0.5
const handleLeaveTimeUpdate = () => {
todaysCommuter();
};
// //
// const handleWorkTimeUpdate = () => {
// todaysCommuter();
// //loadCommuters();
// };
//
const todaysCommuter = async () => {
const res = await $api.get(`commuters/todays`);
if (res.status === 200) {
commuters.value = res.data.data;
}
};
// const handleLeaveTimeUpdate = () => {
// todaysCommuter();
// };
// //
// const todaysCommuter = async () => {
// const res = await $api.get(`commuters/todays`);
// if (res.status === 200) {
// commuters.value = res.data.data;
// }
// };
/************* category ***************/
//
const categoryList = ref([]);
const fetchCategoryList = async () => {
@ -159,8 +135,19 @@
/************* init ***************/
const monthBirthdayList = ref([]);
const monthVacationList = ref([]);
const monthBirthdayPartyList = ref([]);
const monthDinnerList = ref([]);
const monthTeaTimeList = ref([]);
const monthWorkShopList = ref([]);
const birthdayList = ref([]);
const vacationList = ref([]);
const birthdayPartyList = ref([]);
const dinnerList = ref([]);
const teaTimeList = ref([]);
const workShopList = ref([]);
const currentDateEvents = ref([]);
// , ,
const fetchEventList = async param => {
@ -187,31 +174,35 @@
});
}
//
if (res?.birthdayPartyList?.length) {
res.birthdayPartyList.forEach(party => {
addEvent(party.EVENTDT, 'birthdayParty', '생일파티');
});
}
//
monthBirthdayPartyList.value = [];
monthDinnerList.value = [];
monthTeaTimeList.value = [];
monthTeaTimeList.value = [];
//
if (res?.dinnerList?.length) {
res.dinnerList.forEach(dinner => {
addEvent(dinner.EVENTDT, 'dinner', '회식');
});
}
if (res?.eventList?.length) {
res.eventList.forEach(item => {
switch (item.CMNCODVAL) {
case 300203:
monthBirthdayPartyList.value = [...monthBirthdayPartyList.value, item];
addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'birthdayParty', '생일파티');
break;
//
if (res?.teaTimeList?.length) {
res.teaTimeList.forEach(tea => {
addEvent(tea.EVENTDT, 'teaTime', '티타임');
});
}
case 300204:
monthDinnerList.value = [...monthDinnerList.value, item];
addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'dinner', '회식');
break;
//
if (res?.workshopList?.length) {
res.workshopList.forEach(workshop => {
addEvent(workshop.EVENTDT, 'workshop', '워크샵');
case 300205:
monthTeaTimeList.value = [...monthTeaTimeList.value, item];
addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'teaTime', '티타임');
break;
case 300206:
monthWorkShopList.value = [...monthWorkShopList.value, item];
addEvent($common.dateFormatter(item.LOCEVTTME, 'YMD'), 'workshop', '워크샵');
break;
}
});
}
};
@ -221,11 +212,12 @@
//
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 calendarDate = calendarApi.currentData.currentDate;
const { year } = $common.formatDateTime(new Date(calendarDate));
const birthDate = $common.dateFormatter(date, 'MD');
date = `${calendarYear}-${birthDate}`;
date = `${year}-${birthDate}`;
}
}
@ -256,6 +248,26 @@
if (monthVacationList.value) {
vacationList.value = $common.filterTargetByDate(monthVacationList.value, 'LOCVACUDT', month, day);
}
//
if (monthBirthdayPartyList.value) {
birthdayPartyList.value = $common.filterTargetByDate(monthBirthdayPartyList.value, 'LOCEVTTME', month, day);
}
//
if (monthDinnerList.value) {
dinnerList.value = $common.filterTargetByDate(monthDinnerList.value, 'LOCEVTTME', month, day);
}
//
if (monthTeaTimeList.value) {
teaTimeList.value = $common.filterTargetByDate(monthTeaTimeList.value, 'LOCEVTTME', month, day);
}
//
if (monthWorkShopList.value) {
workShopList.value = $common.filterTargetByDate(monthWorkShopList.value, 'LOCEVTTME', month, day);
}
};
//
@ -264,26 +276,20 @@
const calendarApi = fullCalendarRef.value?.getApi();
if (!calendarApi) return;
// ,
const date = calendarApi.currentData.viewTitle;
const dateArr = date.split(' ');
let currentYear = dateArr[0].trim();
let currentMonth = dateArr[1].trim();
const regex = /\D/g;
//
currentYear = parseInt(currentYear.replace(regex, ''), 10);
currentMonth = parseInt(currentMonth.replace(regex, ''), 10);
const date = calendarApi.currentData.currentDate;
const { year, month } = $common.formatDateTime(new Date(date));
try {
//
const holidayEvents = await fetchHolidays(currentYear, String(currentMonth).padStart(2, '0'));
const holidayEvents = await fetchHolidays(year, month);
calendarEvents.value = [...holidayEvents]; //
//
const param = new URLSearchParams();
param.append('year', currentYear);
param.append('month', currentMonth);
param.append('year', year);
param.append('month', month);
param.append('day', '1'); //
await fetchEventList(param);
} catch (error) {
console.error('공휴일 정보 로딩 실패:', error);
@ -318,81 +324,6 @@
return !isWeekend && !isHoliday;
};
//
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.dateStr);
const { month, day } = $common.formatDateTime(new Date(info.dateStr));
//
useFilterEventList(month, day);
// 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>
`;
//
const existingPopovers = document.querySelectorAll('.event-popover');
existingPopovers.forEach(popover => popover.remove());
//
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());
}
});
};
//
const getCellClassNames = arg => {
const cellDate = dayjs(arg.date);
@ -406,58 +337,106 @@
return classes;
};
//
const loadCommuters = async () => {
const calendarApi = fullCalendarRef.value?.getApi();
if (!calendarApi) return;
const date = calendarApi.currentData.viewTitle;
const dateArr = date.split(' ');
let currentYear = dateArr[0].trim();
let currentMonth = dateArr[1].trim();
const regex = /\D/g;
currentYear = parseInt(currentYear.replace(regex, ''), 10);
currentMonth = parseInt(currentMonth.replace(regex, ''), 10);
const res = await $api.get('commuters/month', {
params: {
year: currentYear,
month: currentMonth,
},
});
if (res.status === 200) {
//
monthlyCommuters.value = res.data.data;
document.querySelectorAll('.fc-daygrid-day-events img.rounded-circle').forEach(img => {
img.remove();
});
monthlyCommuters.value.forEach(commuter => {
const date = commuter.COMMUTDAY;
const dateCell =
document.querySelector(`.fc-day[data-date="${date}"]`) ||
document.querySelector(`.fc-daygrid-day[data-date="${date}"]`);
if (dateCell) {
const dayEvents = dateCell.querySelector('.fc-daygrid-day-events');
if (dayEvents) {
dayEvents.classList.add('text-center');
//
const profileImg = document.createElement('img');
profileImg.src = `${baseUrl}upload/img/profile/${commuter.profile}`;
profileImg.className = 'rounded-circle w-px-20 h-px-20 mx-1 mb-1 position-relative z-5 m-auto';
profileImg.style.border = `2px solid ${commuter.projctcolor}`;
profileImg.onerror = () => {
profileImg.src = '/img/icons/icon.png';
//
const handleDateClick = info => {
const { month, day } = $common.formatDateTime(new Date(info.dateStr));
useFilterEventList(month, day);
};
dayEvents.appendChild(profileImg);
}
}
});
//
const handleMouseDown = (date, jsEvent) => {
if (showModal.value) showModal.value = false;
//
const dateEvents = calendarEvents.value.filter(
event => $common.dateFormatter(event.start, 'YMD') === $common.dateFormatter(date, 'YMD'),
);
pressTimer.value = setTimeout(() => {
modalPosition.value = {
x: jsEvent.clientX,
y: jsEvent.clientY,
};
selectedDate.value = date;
currentDateEvents.value = dateEvents;
showModal.value = true;
pressTimer.value = null;
}, longPressDelay);
};
//
const handleMouseUp = () => {
if (pressTimer.value) {
clearTimeout(pressTimer.value);
pressTimer.value = null;
}
};
//
// api
const toggleEvent = async (date, code, title) => {
const { data } = await $api.post('main/toggleEvent', {
date: date,
code: code,
title: title,
});
if (data?.code === 200) toastStore.onToast(data.message);
const { year, month, day } = $common.formatDateTime(new Date(date));
const param = new URLSearchParams();
param.append('year', year);
param.append('month', month);
param.append('day', day);
await fetchEventList(param);
useFilterEventList(month, year);
};
// api
const insertEvent = async (date, code, title, place, time) => {
const dateTime = $common.dateFormatter(`${date} ${time}`);
const { data } = await $api.post('main/inserEvent', {
date: dateTime,
code: code,
title: title,
place: place,
});
if (data?.code === 200) toastStore.onToast(data.message);
const { year, month, day } = $common.formatDateTime(new Date(date));
const param = new URLSearchParams();
param.append('year', year);
param.append('month', month);
param.append('day', day);
await fetchEventList(param);
useFilterEventList(month, year);
};
//
const handleEventSelect = data => {
toggleEvent(data.date, data.code, data.title);
showModal.value = false;
};
const handleEventInsert = data => {
insertEvent(data.date, data.code, data.title, data.place, data.time);
showModal.value = false;
};
const handleEventDelete = data => {
toggleEvent(data.date, data.code, data.title);
showModal.value = false;
};
//
const handleCloseModal = () => {
showModal.value = false;
};
//
const handleEventContent = item => {
if (!item.event) return null;
@ -518,6 +497,17 @@
selectable: true,
selectAllow: selectInfo => isSelectableDate(selectInfo.start),
dateClick: handleDateClick,
dayCellDidMount: arg => {
const dateCell = arg.el;
//
dateCell.addEventListener('mousedown', e => {
const date = $common.dateFormatter(arg.date, 'YMD');
handleMouseDown(date, e);
});
dateCell.addEventListener('mouseup', handleMouseUp);
dateCell.addEventListener('mouseleave', handleMouseUp);
},
dayCellClassNames: getCellClassNames,
unselectAuto: true,
droppable: false,
@ -546,39 +536,9 @@
},
);
// selectedProject
watch(
() => projectStore.selectedProject,
newProject => {
if (newProject) {
selectedProject.value = newProject.PROJCTSEQ;
checkedInProject.value = newProject;
}
},
);
//
const openModal = () => {
isModalOpen.value = true;
};
//
const closeModal = () => {
isModalOpen.value = false;
};
const selectedDateCommuters = computed(() => {
return monthlyCommuters.value.filter(commuter => commuter.COMMUTDAY === eventDate.value);
});
onMounted(async () => {
await fetchData();
await userStore.userInfo();
user.value = userStore.user;
await projectStore.getProjectList('', '', 'true');
project.value = projectStore.projectList;
await todaysCommuter();
//
const storedProject = projectStore.getSelectedProject();
@ -598,6 +558,13 @@
await fetchCategoryList();
await fetchEventList(param);
useFilterEventList(month, day);
//
// document.addEventListener('click', e => {
// if (showModal.value && !e.target.closest('.event-modal') && !e.target.closest('.fc-daygrid-day')) {
// showModal.value = false;
// }
// });
});
</script>
<style>
@ -605,7 +572,7 @@
background-color: transparent;
}
.event-popover {
.event-modal {
padding: 8px;
border: 1px solid #ddd;
}
@ -614,4 +581,9 @@
transform: scale(1.1);
transition: transform 0.2s;
}
/* 이벤트 모달 노출 시 텍스트 선택 방지 */
.fc-daygrid-day {
user-select: none;
}
</style>

View File

@ -2,10 +2,17 @@
<div class="">
<template v-for="category in categoryList" :key="category.CMNCODVAL">
<div
v-if="(category.CMNCODVAL === 300201 && birthdayList?.length) || (category.CMNCODVAL === 300202 && vacationList?.length)"
v-if="
(category.CMNCODVAL === 300201 && birthdayList?.length) ||
(category.CMNCODVAL === 300202 && vacationList?.length) ||
(category.CMNCODVAL === 300203 && birthdayPartyList?.length) ||
(category.CMNCODVAL === 300204 && dinnerList?.length) ||
(category.CMNCODVAL === 300205 && teaTimeList?.length) ||
(category.CMNCODVAL === 300206 && workShopList?.length)
"
class="border border-2 mt-3 card p-2"
>
<div class="row g-2">
<div class="row g-2 position-relative">
<div class="col-3 mx-0 px-0">
<div class="ratio ratio-1x1">
<img
@ -22,6 +29,38 @@
<template v-if="category.CMNCODVAL === 300202">
<MainMemberProfile :members="vacationList" :baseUrl="baseUrl" />
</template>
<template v-if="category.CMNCODVAL === 300203">
<div>
{{ birthdayPartyList[0].LOCEVTPLC }}
</div>
<div>
{{ $common.dateFormatter(birthdayPartyList[0].LOCEVTTME) }}
</div>
</template>
<template v-if="category.CMNCODVAL === 300204">
<div>
{{ dinnerList[0].LOCEVTPLC }}
</div>
<div>
{{ $common.dateFormatter(dinnerList[0].LOCEVTTME) }}
</div>
</template>
<template v-if="category.CMNCODVAL === 300205">
<div>
{{ teaTimeList[0].LOCEVTPLC }}
</div>
<div>
{{ $common.dateFormatter(teaTimeList[0].LOCEVTTME) }}
</div>
</template>
<template v-if="category.CMNCODVAL === 300206">
<div>
{{ workShopList[0].LOCEVTPLC }}
</div>
<div>
{{ $common.dateFormatter(workShopList[0].LOCEVTTME) }}
</div>
</template>
</div>
</div>
</div>
@ -67,30 +106,17 @@
vacationList: {
type: Array,
},
birthdayPartyList: {
type: Array,
},
dinnerList: {
type: Array,
},
teaTimeList: {
type: Array,
},
workShopList: {
type: Array,
},
});
const emit = defineEmits(['drop', 'update:selectedProject', 'update:checkedInProject']);
//
const isCurrentUser = commuter => {
return props.user && commuter && commuter.MEMBERSEQ === props.user.id;
};
//
const dragStart = (event, project) => {
//
event.dataTransfer.setData('application/json', JSON.stringify(project));
event.dataTransfer.effectAllowed = 'copy';
};
//
const allowDrop = event => {
event.preventDefault();
};
//
const handleDrop = (event, targetProject) => {
event.preventDefault();
emit('drop', { event, targetProject });
};
</script>

View File

@ -5,8 +5,9 @@
<div class="ratio ratio-1x1 mb-0 profile-list">
<img
:src="`${baseUrl}upload/img/profile/${member.MEMBERPRF}`"
:style="`border-color: ${member.usercolor} !important;`"
alt="User Profile"
class="rounded-circle"
class="rounded-circle border border-2"
@error="$event.target.src = '/img/icons/icon.png'"
/>
</div>