248 lines
9.4 KiB
Vue
248 lines
9.4 KiB
Vue
<template>
|
|
<div class="container-xxl flex-grow-1 container-p-y">
|
|
<div class="card app-calendar-wrapper">
|
|
<div class="row g-0">
|
|
<div class="col-3 border-end text-center">
|
|
<div class="card-body pb-0">
|
|
<img v-if="user" :src="`${baseUrl}upload/img/profile/${user.profile}`" alt="Profile Image" class="w-px-50 h-auto rounded-circle" @error="$event.target.src = '/img/icons/icon.png'"/>
|
|
<p class="mt-2">
|
|
{{ user.name }}
|
|
</p>
|
|
|
|
<div class="row g-0">
|
|
<div class="col-6 pe-1">
|
|
<p>출근시간</p>
|
|
<button class="btn btn-outline-primary border-3 w-100 py-0">
|
|
<i class='bx bx-run fs-2'></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="col-6 ps-1">
|
|
<p>퇴근시간</p>
|
|
<button class="btn btn-outline-secondary border-3 w-100 py-0">
|
|
<i class='bx bxs-door-open fs-2'></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div v-for="post in project" :key="post.PROJCTSEQ" class="border border-2 mt-3" :style="`border-color: ${post.projctcolor} !important; color: ${post.projctcolor} !important;`">
|
|
{{ post.PROJCTNAM }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col app-calendar-content">
|
|
<div class="card shadow-none border-0">
|
|
<div class="card-body pb-0">
|
|
<full-calendar
|
|
ref="fullCalendarRef"
|
|
:events="calendarEvents"
|
|
:options="calendarOptions"
|
|
defaultView="dayGridMonth"
|
|
class="flatpickr-calendar-only"
|
|
>
|
|
</full-calendar>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<center-modal :display="isModalVisible" @close="isModalVisible = $event">
|
|
<template #title> Add Event </template>
|
|
<template #body>
|
|
<FormInput
|
|
title="이벤트 제목"
|
|
name="event"
|
|
:is-essential="true"
|
|
:is-alert="eventAlert"
|
|
@update:data="eventTitle = $event"
|
|
/>
|
|
<FormInput
|
|
title="이벤트 날짜"
|
|
type="date"
|
|
name="eventDate"
|
|
:is-essential="true"
|
|
:is-alert="eventDateAlert"
|
|
@update:data="eventDate = $event"
|
|
/>
|
|
</template>
|
|
<template #footer>
|
|
<button @click="addEvent">추가</button>
|
|
</template>
|
|
</center-modal>
|
|
</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 { inject, onMounted, reactive, ref, watch } from 'vue';
|
|
import $api from '@api';
|
|
import { isEmpty } from '@/common/utils';
|
|
import FormInput from '../input/FormInput.vue';
|
|
import 'flatpickr/dist/flatpickr.min.css';
|
|
import '@/assets/css/app-calendar.css';
|
|
import { fetchHolidays } from '@c/calendar/holiday';
|
|
import { useUserInfoStore } from '@/stores/useUserInfoStore';
|
|
import { useProjectStore } from '@/stores/useProjectStore';
|
|
|
|
const baseUrl = $api.defaults.baseURL.replace(/api\/$/, '');
|
|
const user = ref({});
|
|
const project = ref({});
|
|
const userStore = useUserInfoStore();
|
|
const projectStore = useProjectStore();
|
|
|
|
const dayjs = inject('dayjs');
|
|
const fullCalendarRef = ref(null);
|
|
const calendarEvents = ref([]);
|
|
const isModalVisible = ref(false);
|
|
const eventAlert = ref(false);
|
|
const eventDateAlert = ref(false);
|
|
const eventTitle = ref('');
|
|
const eventDate = ref('');
|
|
const selectedDate = ref(null);
|
|
|
|
|
|
// 날짜 선택 핸들러
|
|
const handleDateSelect = (selectedDates) => {
|
|
if (selectedDates.length > 0) {
|
|
// 선택된 첫 번째 날짜를 YYYY-MM-DD 형식으로 변환
|
|
const selectedDate = dayjs(selectedDates[0]).format('YYYY-MM-DD');
|
|
eventDate.value = selectedDate;
|
|
showModal(); // 모달 표시
|
|
}
|
|
};
|
|
|
|
// 캘린더 데이터 가져오기
|
|
const fetchData = async () => {
|
|
// FullCalendar API 인스턴스 가져오기
|
|
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);
|
|
|
|
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];
|
|
} catch (error) {
|
|
console.error('공휴일 정보 로딩 실패:', error);
|
|
}
|
|
};
|
|
|
|
// 캘린더 이동 함수 (이전, 다음, 오늘)
|
|
const moveCalendar = async (value = 0) => {
|
|
const calendarApi = fullCalendarRef.value?.getApi();
|
|
|
|
if (value === 1) {
|
|
calendarApi.prev(); // 이전 달로 이동
|
|
} else if (value === 2) {
|
|
calendarApi.next(); // 다음 달로 이동
|
|
} else if (value === 3) {
|
|
calendarApi.today(); // 오늘 날짜로 이동
|
|
}
|
|
|
|
// 캘린더 이동 후 데이터 다시 가져오기
|
|
await fetchData();
|
|
};
|
|
|
|
// 모달 표시 함수
|
|
const showModal = () => {
|
|
isModalVisible.value = true;
|
|
};
|
|
|
|
// 모달 닫기 함수
|
|
const closeModal = () => {
|
|
isModalVisible.value = false;
|
|
// 입력 필드 초기화
|
|
eventTitle.value = '';
|
|
eventDate.value = '';
|
|
};
|
|
|
|
// 이벤트 추가 함수
|
|
const addEvent = () => {
|
|
// 이벤트 유효성 검사
|
|
if (!checkEvent()) {
|
|
// 유효성 검사 통과 시 이벤트 추가
|
|
calendarEvents.value.push({
|
|
title: eventTitle.value,
|
|
start: eventDate.value,
|
|
backgroundColor: '#4CAF50' // 일반 이벤트 색상
|
|
});
|
|
closeModal(); // 모달 닫기
|
|
}
|
|
};
|
|
|
|
// 이벤트 유효성 검사 함수
|
|
const checkEvent = () => {
|
|
// 제목과 날짜가 비어있는지 확인
|
|
eventAlert.value = isEmpty(eventTitle.value);
|
|
eventDateAlert.value = isEmpty(eventDate.value);
|
|
// 하나라도 비어있으면 true 반환 (유효성 검사 실패)
|
|
return eventAlert.value || eventDateAlert.value;
|
|
};
|
|
|
|
// 캘린더 옵션 설정
|
|
const calendarOptions = reactive({
|
|
plugins: [dayGridPlugin, interactionPlugin], // 사용할 플러그인
|
|
initialView: 'dayGridMonth', // 초기 뷰 (월간)
|
|
headerToolbar: { // 상단 툴바 구성
|
|
left: 'today', // 왼쪽: 오늘 버튼
|
|
center: 'title', // 중앙: 제목(연월)
|
|
right: 'prev,next', // 오른쪽: 이전/다음 버튼
|
|
},
|
|
locale: 'kr', // 한국어 지역화
|
|
events: calendarEvents, // 표시할 이벤트 데이터
|
|
eventOrder: 'sortIdx', // 이벤트 정렬 기준
|
|
selectable: true, // 날짜 선택 가능 여부
|
|
dateClick: handleDateSelect, // 날짜 클릭 이벤트 핸들러
|
|
droppable: false, // 드래그 앤 드롭 비활성화
|
|
eventDisplay: 'block', // 이벤트 표시 방식
|
|
|
|
// 커스텀 버튼 정의
|
|
customButtons: {
|
|
prev: {
|
|
text: 'PREV', // 이전 버튼 텍스트
|
|
click: () => moveCalendar(1), // 클릭 시 이전 달로 이동
|
|
},
|
|
today: {
|
|
text: 'TODAY', // 오늘 버튼 텍스트
|
|
click: () => moveCalendar(3), // 클릭 시 오늘로 이동
|
|
},
|
|
next: {
|
|
text: 'NEXT', // 다음 버튼 텍스트
|
|
click: () => moveCalendar(2), // 클릭 시 다음 달로 이동
|
|
},
|
|
},
|
|
});
|
|
|
|
// 달력 뷰 변경 감지 (월 변경 시 데이터 다시 가져오기)
|
|
watch(() => fullCalendarRef.value?.getApi().currentData.viewTitle, async () => {
|
|
await fetchData();
|
|
});
|
|
|
|
|
|
console.log(project)
|
|
onMounted(async () => {
|
|
await fetchData();
|
|
await userStore.userInfo();
|
|
user.value = userStore.user;
|
|
await projectStore.getProjectList();
|
|
project.value = projectStore.projectList;
|
|
});
|
|
</script>
|