뷰(Vue)

Vue에 캘린더 달력(Vue Date Picker) 공통 컴퍼넌트화 해서 적용하기.

뭘하지 2024. 1. 11. 17:08
반응형

 

Vue를 사용하더라도 캘린더나 이런부분들은 사용해야 하기때문에 기존에 jquery나 js를 사용할때 잘 쓰던 datepicker를 그대로 사용하려고 했다. 설치하는 부분의 경우에는 공식문서에서 자세하게 설명해주고 있기 때문에 간단하게만 정리하였다.

Vue프로젝트에 date-picker 적용하기.

설치

npm install @vuepic/vue-datepicker

main.js에 컴포넌트 및 css 추가적용.

import VueDatePicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css';
createApp(App)
    .component('VueDatePicker', VueDatePicker)
    .use(router)
    .use(pinia)
    .mount('#app');

 

공식주소

Installation | Vue Datepicker

 

Installation | Vue Datepicker

 

vue3datepicker.com

 

프로젝트에서 사용할때  DatePicker에서 제공하는 기능이 많긴하지만 다 필요한건 아니였고 최근에 지원하는 다른 라이브러리들과 조금 다르게 내부소스가 타입스크립트(ts)로 되어있어서 상세한 옵션값을 알고싶다면 결국 공식 홈페이지를 찾아가야만 하는 형태가 되어버린다.

 

그래서 별도로 공통으로 사용하기 위한 컴포넌트를 하나 정리했다.
필요한 옵션값들을 따로 정리하고 공식홈페이지에서 제공하는 옵션값 중 이해하기 어려운 느낌의 값들은 임의로 변경하여 읽기 쉽게 하고자 했다.

 

공통적으로 사용하기위해 생성한 CommonCalendar.vue파일

정말 필요할 것만 같은 옵션값들만 골라서 정리하고 수행했음에도 양이 많았다.
대부분의 경우 default값을 넣어주어 고정된 형태를 사용하게 한 뒤 변경을 원하는 부분만 컴포넌트의 내부로 들어와서 옵션값을 보고 지정하여 사용하면 된다.

<template>
    <div class="field p-fluid">
        <label>{{ label }}</label>
        <VueDatePicker
            v-model="pickerDate"
            :year-picker="setMode === 'year'"
            :month-picker="setMode === 'month'"
            :time-picker="setMode === 'time'"
            :week-picker="setMode === 'week'"
            :range="useRange"
            :format="format"
            :model-type="compuedModelType"
            :month-change-on-scroll="useMonthScroll"
            :auto-apply="useAutoApply"
            :placeholder="placeholder"
            :no-today="noDisplayTodayMark"
            :disabled="disabled"
            :readonly="readonly"
            :action-row="{
                showNow: showToday,
                showPreview: showPreview,
                showCancel: showCancel,
                showSelect: showSelect,
            }"
            :year-first="showYearFirst"
            :partial-range="computedPartialRange"
            :min-range="minRange"
            :max-range="maxRange"
            :multi-calendars="useMultiCalendars"
            :locale="setLocale"
            :select-text="selectText"
            :cancel-text="cancelText"
            :now-button-label="todayText"
            :day-names="customDayNames"
            :enable-time-picker="useTimepicker"
            :year-range="[2000, 2100]"
            :clearable="useClear"
            :start-date="setStartDate"
            :disabled-week-days="computedDisableWeekDays"
        ></VueDatePicker>
    </div>
</template>
<script setup>
import VueDatePicker from '@vuepic/vue-datepicker';
import { computed, ref, watch } from 'vue';
/** date-picker v-model 연동용 변수 */
let pickerDate = ref();
/**
 * label 설명라벨
 * format 날짜포멧 지정 ('yyyy-MM-dd', 'yyyy-MM', 'yyyy')
 * useMonthScroll 마우스 스크롤을 사용한 월 단위 이동 사용여부
 * useClear clear버튼 사용여부 (true,false)
 * useRange 날짜 범위로 지정하는 옵션 설정 (true,false)
 * useAutoApply select버튼 없이 날짜로 바로 선택하는 기능 사용 (true,false)
 * placeholder input박스 placeholder 지정
 * noDisplayTodayMark 현재일 표시 date-picker에 표시 여부 (true,false)
 * disabled disabled 적용 (true,false)
 * readonly readonly 적용 (true,false)
 * showToday today버튼 표시여부 (true,false)
 * showPreview 미리보기 정보 표시 여부 (true,false)
 * showSelect select버튼 표시여부 (true,false)
 * showCancel cancel버튼 표시여부 (true,false)
 * showYearFirst 년월형태로 표시여부 (true,false)
 * minRange useRange 사용시(날짜2개 선택시) 최소 범위 지정 (Number)
 * maxRange useRange 사용시(날짜2개 선택시) 최대 범위 지정 (Number)
 * useMultiCalendars 멀티 캘린더 형태로 사용여부 (true,false)
 * setMode 특정형태의 모습을 보여줄때 사용 (year,month,week,time)
 * setLocale 달력 locale 설정 (String)
 * selectText select버튼 텍스트 설정 (String)
 * cancelText cancel버튼 텍스트 설정 (String)
 * todayText today버튼 텍스트 설정 (String)
 * customDayNames 표시되는 이름 변경옵션 ex) 기존 ko일때 커스터마이징 하던것과 동일함 ['월','화','수','목','금','토','일']
 * useTimepicker timepicker 사용여부 (time단위까지 입력필요할시 format형태도 추가 필요함.) (true,false)
 * setStartDate date-picker 보여지는 날짜 지정
 * setDisableWeekDays 특정 요일만 선택못하게 하는경우 사용 (Array) ex) ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
 *
 */
let props = defineProps({
    label: { Type: String, default: '' },
    format: {
        Type: String,
        default: 'yyyy-MM-dd',
        validator(value) {
            const allowedFormats = ['yyyy-MM-dd', 'yyyy-MM', 'yyyy'];
            if (!allowedFormats.includes(value)) {
                console.warn(`Invalid format: ${value}. one of 'yyyy-MM-dd', 'yyyy-MM', 'yyyy'`);
                return false;
            }
            return true;
        },
    },
    useMonthScroll: { Type: Boolean, default: true },
    useClear: { Type: Boolean, default: true },
    useRange: { Type: Boolean, default: false },
    useAutoApply: { type: Boolean, default: true },
    placeholder: { type: String },
    noDisplayTodayMark: { type: Boolean, default: false },
    disabled: { type: Boolean, default: false },
    readonly: { type: Boolean, default: false },
    showToday: { type: Boolean, default: true },
    showPreview: { type: Boolean, default: false },
    showSelect: { type: Boolean, default: true },
    showCancel: { type: Boolean, default: true },
    showYearFirst: { type: Boolean, default: true },
    minRange: { type: Number, default: 0 },
    maxRange: { type: Number, default: 60 },
    useMultiCalendars: { type: Boolean, default: false },
    setMode: { type: String, default: '' },
    setLocale: { type: String, default: 'ko' },
    selectText: { type: String, default: 'Select' },
    cancelText: { type: String, default: 'Cancel' },
    todayText: { type: String, default: 'today' },
    customDayNames: { type: Array },
    useTimepicker: { type: Boolean, default: false },
    setStartDate: { type: Date, default: new Date() },
    setDisableWeekDays: { type: Array },
});
let emits = defineEmits(['update:modelValue']);
/**
 * range를 사용할때는 1개만 선택하면 안되는모드 지정.
 */
const computedPartialRange = computed(() => {
    if (props.useRange) {
        return false;
    }
    return true;
});
/**
 * format에 지정된 형식을 따라간다.
 * @type {ComputedRef<any>}
 */
const compuedModelType = computed(() => {
    return props.format;
});
/**
 * 특정 요일 지정 불가능 옵션값으로 변경처리
 * @type {ComputedRef<*>}
 */
const computedDisableWeekDays = computed(() => {
    return props.setDisableWeekDays.map((item) => {
        return changeDisableWeekDays(item);
    });
});

/**
 * vue-date-picker에 명시된 값으로 변경처리
 * Sunday --> 0
 * Monday --> 1
 * Tuesday --> 2
 * Wednesday --> 3
 * Thursday --> 4
 * Friday --> 5
 * Saturday --> 6
 * @param item
 * @returns {number}
 */
function changeDisableWeekDays(item) {
    switch (item) {
        case 'Sunday':
            return 0;
            break;
        case 'Monday':
            return 1;
            break;
        case 'Tuesday':
            return 2;
            break;
        case 'Wednesday':
            return 3;
            break;
        case 'Thursday':
            return 4;
            break;
        case 'Friday':
            return 5;
            break;
        case 'Saturday':
            return 6;
            break;
    }
}
/**
 * vue-date-picker에 연결한 pickerDate 값이 바뀌면 전달받은 v-model에 값 연결
 */
watch(pickerDate, (newValue, oldValue) => {
    emits('update:modelValue', newValue);
});
</script>

 

사용예시


- 일요일과 월요일은 선택 못하게 한다.
- 멀티캘린더 형태로 사용한다.
- 날짜클릭시 자동적용을 하지않는다.
- 포맷은 yyyy-MM-dd 형태로 사용한다.
- searchDate에 값을 연결한다.

            <div class="field col-12 md:col-3">
                <CommonCalendar
                    label="등록일시"
                    v-model="searchDate"
                    :use-auto-apply="false"
                    :use-multi-calendars="true"
                    placeholder="날짜를 선택하세요"
                    format="yyyy-MM-dd"
                    :setDisableWeekDays="['Sunday', 'Monday']"
                ></CommonCalendar>
            </div>

<script setup>
import { ref } from 'vue';
let searchDate = ref();
</script>

 

사용하면서 필요한게 더 있다면 이제 해당 컴포넌트를 업데이트해서 추가적용 하여 사용하면 될 것 같다.

반응형