728x90
반응형
캘린더 라이브러리를 쓰다가 문득 직접 만들어 보고 싶어서 vue3로 커스텀 캘린더를 만들었습니다.
뭔가 옛날스런 느낌으로 만들고 싶었기에 조악한 디자인... 을 갖고 있지만ㅋㅋㅋㅋㅋㅋ
소스코드
<template>
<div class="common-calendar my-5" v-show="ymd != 0">
<div>
<span class="font-weight-bold">시작: </span>
<select v-model="year">
<option v-for="option in yearOptions" :key="option" :value="option">
{{ option }}
</option>
</select>
<transition name="fade">
<select v-show="ymd == 1 || ymd == 2" v-model="month">
<option v-for="option in monthOptions" :key="option" :value="option">
{{ option }}
</option>
</select>
</transition>
<transition name="fade">
<select v-show="ymd == 1" v-model="day">
<option
v-for="option in startDayOptions"
:key="option"
:value="option"
>
{{ option }}
</option>
</select>
</transition>
</div>
<span class="mx-8">~</span>
<div>
<span class="font-weight-bold">끝: </span>
<select v-model="endYear">
<option v-for="option in yearOptions" :key="option" :value="option">
{{ option }}
</option>
</select>
<transition name="fade">
<select v-show="ymd == 1 || ymd == 2" v-model="endMonth">
<option v-for="option in monthOptions" :key="option" :value="option">
{{ option }}
</option>
</select>
</transition>
<transition name="fade">
<select v-show="ymd == 1" v-model="endDay">
<option v-for="option in endDayOptions" :key="option" :value="option">
{{ option }}
</option>
</select>
</transition>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watchEffect } from "vue";
interface Props {
ymd: number;
}
const props = defineProps<Props>();
const today = new Date();
const year = ref(today.getFullYear());
const month = ref(
today.getMonth() < 9 ? "0" + (today.getMonth() + 1) : today.getMonth() + 1
);
const day = ref(
ref(today.getDate() < 10 ? "0" + today.getDate() : today.getDate())
);
const endYear = ref(today.getFullYear());
const endMonth = ref(
today.getMonth() < 9 ? "0" + (today.getMonth() + 1) : today.getMonth() + 1
);
const endDay = ref(
ref(today.getDate() < 10 ? "0" + today.getDate() : today.getDate())
);
const todayString = `${year.value}-${
month.value < 10 ? `0${month.value}` : month.value
}-${day.value < 10 ? `0${day.value}` : day.value}`;
const startDate = ref(todayString);
const endDate = ref(todayString);
const yearOptions = [];
for (let i = 1990; i <= year.value; i++) yearOptions.push(i);
const monthOptions = [];
for (let i = 1; i <= 12; i++) {
if (i < 10) monthOptions.push("0" + i);
else monthOptions.push(i);
}
const checkYear = (year) => {
if (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) return true;
else return false;
};
const startDayOptions = ref([]);
const endDayOptions = ref([]);
const getMaxDay = (year, month) => {
switch (parseInt(month)) {
case 2:
if (checkYear(year)) return 29;
else return 28;
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
return 31;
case 4:
case 6:
case 9:
case 11:
return 30;
}
};
for (let i = 1; i <= 28; i++) {
if (i < 10) startDayOptions.value.push("0" + i);
else startDayOptions.value.push(i.toString());
}
for (let i = 1; i <= 28; i++) {
if (i < 10) endDayOptions.value.push("0" + i);
else endDayOptions.value.push(i.toString());
}
watchEffect(() => {
var start = `${year.value}-${
month.value < 10 ? `${month.value}` : month.value
}-${day.value < 10 ? `${day.value}` : day.value}`;
var end = `${endYear.value}-${
endMonth.value < 10 ? `${endMonth.value}` : endMonth.value
}-${endDay.value < 10 ? `${endDay.value}` : endDay.value}`;
if (start > end) {
alert("시작 날짜가 끝 날짜보다 작아야 합니다.");
endYear.value = year.value;
endMonth.value = month.value;
endDay.value = day.value;
} else {
startDate.value = start;
endDate.value = end;
}
});
watch(month, (newValue, _) => {
let maxDay = getMaxDay(year.value, newValue);
startDayOptions.value.length = 0;
for (let i = 1; i <= maxDay; i++) {
if (i < 10) startDayOptions.value.push("0" + i);
else startDayOptions.value.push(i);
}
});
watch(year, (newValue, _) => {
let maxDay = getMaxDay(year.value, month.value);
startDayOptions.value.length = 0;
for (let i = 1; i <= maxDay; i++) {
if (i < 10) startDayOptions.value.push("0" + i);
else startDayOptions.value.push(i);
}
});
watch(endMonth, (newValue, _) => {
let maxDay = getMaxDay(endYear.value, newValue);
endDayOptions.value.length = 0;
for (let i = 1; i <= maxDay; i++) {
if (i < 10) endDayOptions.value.push("0" + i);
else endDayOptions.value.push(i);
}
});
watch(endYear, (newValue, _) => {
let maxDay = getMaxDay(endYear.value, endMonth.value);
endDayOptions.value.length = 0;
for (let i = 1; i <= maxDay; i++) {
if (i < 10) endDayOptions.value.push("0" + i);
else endDayOptions.value.push(i);
}
});
</script>
<style lang="scss" scoped>
.common-calendar {
display: flex;
flex-direction: row;
transition: all 1s;
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from {
opacity: 1;
}
.fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
select {
border: 2px dotted grey;
}
}
</style>
생각보다 소스코드가 긴데 간단합니다.
기본적으로 Date() 객체를 사용하여 핸들링 했으며 윤년처리, 시작과 끝 날짜 에러 처리, 날짜 스트링 처리 등의 코드로 양만 많은 것이므로 쫄 것 없이 읽어보면 쉽게 이해됩니다.
728x90
반응형