달력 템플릿이랑 똑같이 만듬 (조금 다를 수 있음)

This commit is contained in:
ckx6954 2024-12-19 13:31:10 +09:00
parent cea58be11a
commit c60b6fda52
7 changed files with 231 additions and 45 deletions

View File

@ -45,7 +45,7 @@
<script src="/vendor/js/helpers.js"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Template customizer: To hide customizer set displayCustomizer value false in config.js. -->
<!-- <script src="/vendor/js/template-customizer.js"></script> -->
<script src="/vendor/js/template-customizer.js"></script>
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="/js/config.js"></script>

23
package-lock.json generated
View File

@ -18,11 +18,13 @@
"axios": "^1.7.9",
"bootstrap": "^5.3.3",
"dayjs": "^1.11.13",
"flatpickr": "^4.6.13",
"front": "file:",
"pinia": "^2.2.6",
"pinia-plugin-persist": "^1.0.0",
"quill": "^2.0.3",
"vue": "^3.5.13",
"vue-flatpickr-component": "^11.0.5",
"vue-router": "^4.4.5"
},
"devDependencies": {
@ -2730,6 +2732,12 @@
"node": ">=16"
}
},
"node_modules/flatpickr": {
"version": "4.6.13",
"resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz",
"integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==",
"license": "MIT"
},
"node_modules/flatted": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
@ -4511,6 +4519,21 @@
"url": "https://opencollective.com/eslint"
}
},
"node_modules/vue-flatpickr-component": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/vue-flatpickr-component/-/vue-flatpickr-component-11.0.5.tgz",
"integrity": "sha512-Vfwg5uVU+sanKkkLzUGC5BUlWd5wlqAMq/UpQ6lI2BCZq0DDrXhOMX7hrevt8bEgglIq2QUv0K2Nl84Me/VnlA==",
"license": "MIT",
"dependencies": {
"flatpickr": "^4.6.13"
},
"engines": {
"node": ">=14.13.0"
},
"peerDependencies": {
"vue": "^3.2.0"
}
},
"node_modules/vue-router": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz",

View File

@ -21,11 +21,13 @@
"axios": "^1.7.9",
"bootstrap": "^5.3.3",
"dayjs": "^1.11.13",
"flatpickr": "^4.6.13",
"front": "file:",
"pinia": "^2.2.6",
"pinia-plugin-persist": "^1.0.0",
"quill": "^2.0.3",
"vue": "^3.5.13",
"vue-flatpickr-component": "^11.0.5",
"vue-router": "^4.4.5"
},
"devDependencies": {

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.app-calendar-wrapper{position:relative;border-radius:.375rem}.app-calendar-wrapper .app-calendar-sidebar{position:absolute;overflow:hidden;flex-grow:0;flex-basis:18.75rem;left:calc(-18.75rem - 1.2rem);height:100%;width:18.75rem;transition:all .2s;z-index:4}.app-calendar-wrapper .app-calendar-sidebar.show{left:0}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar{box-shadow:none}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-month,.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-weekday,.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-weekdays{background:rgba(0,0,0,0)}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar .flatpickr-days{border:0}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-calendar:focus{outline:0}.app-calendar-wrapper .app-calendar-content{position:relative}.app-calendar-wrapper .fc-toolbar h2{font-size:1.5rem;line-height:2.375rem}@media(max-width: 767.98px){.app-calendar-wrapper .fc-toolbar h2{font-size:1rem}}.app-calendar-wrapper .fc-toolbar-chunk{overflow:auto}.app-calendar-wrapper table.fc-scrollgrid{border-left:0;border-right:0}.app-calendar-wrapper table.fc-scrollgrid th,.app-calendar-wrapper table.fc-scrollgrid td{border-right:0}.app-calendar-wrapper .fc-timeGridDay-view table.fc-scrollgrid tbody tr:not(.fc-scrollgrid-section:first-of-type) td,.app-calendar-wrapper .fc-timeGridWeek-view table.fc-scrollgrid tbody tr:not(.fc-scrollgrid-section:first-of-type) td{border-bottom:0}.app-calendar-wrapper .fc-dayGridMonth-view table.fc-scrollgrid td{border-bottom:0 !important}.app-calendar-wrapper .fc-header-toolbar{margin-bottom:1.5rem !important}.app-calendar-wrapper .fc-view-container{margin:0 -1.6rem}.app-calendar-wrapper .event-sidebar .ql-editor{min-height:5rem}.app-calendar-wrapper .event-sidebar .select2 .select2-selection__choice{display:flex}.app-calendar-wrapper .event-sidebar .select2 .select2-selection__choice .avatar{display:none}@media(min-width: 992px){.app-calendar-wrapper .app-calendar-sidebar{position:static;height:auto;background-color:rgba(0,0,0,0) !important}.app-calendar-wrapper .app-calendar-sidebar .flatpickr-days{background-color:rgba(0,0,0,0)}}[dir=rtl] .app-calendar-wrapper .fc .fc-toolbar .fc-sidebarToggle-button{order:1}[dir=rtl] .app-calendar-wrapper .app-calendar-sidebar{left:auto;right:calc(-18.75rem - 1.2rem)}[dir=rtl] .app-calendar-wrapper .app-calendar-sidebar.show{left:auto;right:0}.light-style .app-calendar-wrapper .app-calendar-sidebar{background-color:#fff}.dark-style .app-calendar-wrapper .app-calendar-sidebar{background-color:#2b2c40}

View File

@ -1,25 +1,91 @@
<template>
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<FullCalendar ref="fullCalendarRef" :options="calendarOptions" defaultView="dayGridMonth" />
<div class="card app-calendar-wrapper">
<div class="row g-0">
<div class="col app-calendar-sidebar border-end" id="app-calendar-sidebar">
<div class="border-bottom p-6 my-sm-0 mb-4">
<button class="btn btn-primary btn-toggle-sidebar w-100" @click="showModal">
<i class="bx bx-plus bx-16px me-2"></i>
<span class="align-middle">Add Event</span>
</button>
</div>
<div class="px-3 pt-2">
<flat-pickr v-model="selectedDate" :config="flateCalendarConfig" @on-change="onDateChange"></flat-pickr>
</div>
<hr class="mb-6 mx-n4 mt-3" />
<div class="px-6 pb-2">
<div>
<h5>Event</h5>
</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 } from 'vue';
import axios from 'axios';
import { isEmpty } from '@/common/utils';
import FormInput from '../input/FormInput.vue';
import FlatPickr from 'vue-flatpickr-component';
import 'flatpickr/dist/flatpickr.min.css'
import '@/assets/css/app-calendar.css'
//
//dark
const isDarkMode = ref(false);
const key1 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY%2B4DmNpmasTex%2FNDySN7FDYwBqPj1p%2BxXLg13BzYfgHPt6eipWhH8Q%3D%3D';
const key2 = 'vrkIY7jUy82WRHIozbBtz4n2L89jAqNDY+4DmNpmasTex/NDySN7FDYwBqPj1p+xXLg13BzYfgHPt6eipWhH8Q==';
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 fetchData = () => {
const date = fullCalendarRef.value?.getApi().currentData.viewTitle;
@ -58,44 +124,44 @@ const fetchData = () => {
// back
const callApi = (yearArr, monthArr) => {
const callUrl = 'http://apis.data.go.kr/B090041/openapi/service/SpcdeInfoService/getRestDeInfo';
for (let i = 0; i < yearArr.length; i++) {
axios.get(callUrl , {
params: {
solYear: String(yearArr[i]),
solMonth: String(monthArr[i]),
serviceKey: key2,
},
})
.then(data => {
const itemData = data.data.response.body.items.item;
if(!isEmpty(itemData)){
if(Array.isArray(itemData)){
for(const item of itemData){
const locdate = item.locdate;
axios
.get(callUrl, {
params: {
solYear: String(yearArr[i]),
solMonth: String(monthArr[i]),
serviceKey: key2,
},
})
.then(data => {
const itemData = data.data.response.body.items.item;
if (!isEmpty(itemData)) {
if (Array.isArray(itemData)) {
for (const item of itemData) {
const locdate = item.locdate;
dateFormat(locdate);
}
} else {
const locdate = itemData.locdate;
dateFormat(locdate);
}
}else{
const locdate = itemData.locdate;
dateFormat(locdate);
}
}
})
.catch(err => {
console.error('공휴일 불러오기 실패');
});
})
.catch(err => {
console.error('공휴일 불러오기 실패');
});
}
};
const dateFormat = (locdate) => {
const day = dayjs(String(locdate) , 'YYYYMMDD');
const dateFormat = locdate => {
const day = dayjs(String(locdate), 'YYYYMMDD');
const formatDate = day.format('YYYY년 M월 D일');
const targetEle = document.querySelector(`[aria-label="${formatDate}"]`);
if(targetEle != null){
if (targetEle != null) {
targetEle.style.color = 'red';
}
}
};
const handleDateClick = arg => {
console.log(arg.dateStr);
@ -115,6 +181,40 @@ const moveCalendar = (value = 0) => {
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 });
closeModal();
}
};
const checkEvent = () => {
const title = isEmpty(eventTitle.value);
const date = isEmpty(eventDate.value);
return title || date;
};
const onDateChange = selectedDates => {
console.log('변경된 날짜:', selectedDates);
};
const flateCalendarConfig = reactive({
dateFormat: 'Y-m-d', //
enableTime: false, //
inline: true,
});
const calendarOptions = reactive({
plugins: [dayGridPlugin, interactionPlugin],
initialView: 'dayGridMonth',
@ -125,7 +225,7 @@ const calendarOptions = reactive({
right: 'prev,next',
},
locale: 'kr', //
events: [],
events: calendarEvents,
eventOrder: 'sortIdx',
selectable: true,
dateClick: handleDateClick,
@ -152,24 +252,26 @@ const calendarOptions = reactive({
},
});
onMounted(() => {
const loadFlatpickrTheme = async () => {
if (isDarkMode.value) {
await import('flatpickr/dist/themes/dark.css');
} else {
await import('flatpickr/dist/themes/light.css');
}
}
onMounted(async () => {
const storedStyle = localStorage.getItem('templateCustomizer-vertical-menu-template--Style');
if (storedStyle === 'dark') {
isDarkMode.value = true;
}
await loadFlatpickrTheme();
fetchData();
});
</script>
<style>
.fc-button-group {
margin-right: 15px !important;
}
.fc-today-button {
margin-left: 15px !important;
}
.fc-header-toolbar {
margin-top: 10px !important;
}
.fc-day-sun .fc-col-header-cell-cushion,
.fc-day-sun a {
color: red;
@ -179,4 +281,15 @@ onMounted(() => {
.fc-day-sat a {
color: blue;
}
.flatpickr-calendar-only input.flatpickr-input {
display: none;
}
.flatpickr-input {
display: none;
}
.flatpickr-calendar{
width: 100% !important;
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<div @click="closeModal" class="modal fade" :class="{ 'show': display, 'display-block': display , 'modal-back' : display }" id="modalCenter" tabindex="-1" aria-modal="true" role="dialog">
<div @click.stop class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="modalCenterTitle">
<slot name="title">Modal Title</slot>
</h5>
<button type="button" class="btn-close" @click="closeModal" aria-label="Close"></button>
</div>
<div class="modal-body">
<slot name="body">Modal body</slot>
</div>
<div class="modal-footer">
<slot name="footer">Modal foot</slot>
</div>
</div>
</div>
</div>
</template>
<script setup>
const prop = defineProps({
display : {
type: Boolean,
default: false,
required: true,
},
});
const emit = defineEmits(['close']);
const closeModal = () => {
emit('close' , false);
};
</script>
<style>
.modal-back {
background: rgba(0, 0, 0, 0.5);
}
</style>