달력 템플릿이랑 똑같이 만듬 (조금 다를 수 있음)
This commit is contained in:
parent
cea58be11a
commit
c60b6fda52
@ -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
23
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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
1
src/assets/css/app-calendar.css
Normal file
1
src/assets/css/app-calendar.css
Normal 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}
|
||||
@ -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>
|
||||
|
||||
47
src/components/modal/CenterModal.vue
Normal file
47
src/components/modal/CenterModal.vue
Normal 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>
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user