From 39fee9ef5f17db041c9c51ab996d7e4834b11419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EB=A7=8C=EC=96=B5=20=28Jo=29?= Date: Tue, 11 Nov 2025 06:19:49 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=BF=A0=ED=8F=B0=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- layers/components/blocks/DatePicker.vue | 447 +++++++++++++++++------- layers/templates/FxCoupon01/index.vue | 79 ++--- 2 files changed, 355 insertions(+), 171 deletions(-) diff --git a/layers/components/blocks/DatePicker.vue b/layers/components/blocks/DatePicker.vue index f9e805f..2cf9dc2 100644 --- a/layers/components/blocks/DatePicker.vue +++ b/layers/components/blocks/DatePicker.vue @@ -5,7 +5,6 @@ import { startOfDay, globalDateFormat, } from '@seed-next/date' -import type { PublicMethods } from '@vuepic/vue-datepicker' interface Props { date: Date | string | null @@ -26,23 +25,27 @@ const emits = defineEmits(['update:date']) const breakpoints = useResponsiveBreakpoints() // Refs ----- -const { tm, locale } = useI18n() +const { t, locale } = useI18n() const datePickerRef = ref(null) const resultDate = ref('') const formatDate = ref('') -const currentYear = ref(new Date().getFullYear()) -const currentMonth = ref(new Date().getMonth()) -const currentDecade = ref<{ start: number; end: number } | null>(null) +const monthList = ref>([]) +const currentYear = ref(getYear(new Date())) +const currentMonth = ref(getMonth(new Date())) +const currentDecade = ref<{ start: number; end: number; year: number }>({ + start: 1970, + end: Math.floor(currentYear.value / 10) * 10 + 9, + year: currentYear.value, +}) +const currentDecadeList = ref | null>(null) // 상태값 const isMenuOpened = ref(false) const isShowYearPicker = ref(false) const isShowMonthPicker = ref(false) -const isShowDecadePicker = ref(false) +const isDisabledYearNav = ref({ prev: false, next: false }) +const isDisabledDecadeNav = ref({ prev: false, next: false }) // Computed -const yearMonthText = computed(() => { - return `${currentYear.value}년 ${currentMonth.value + 1}월` -}) const formatMinDate = computed(() => { if (props.minDate !== null) { return globalDateFormat(props.minDate, locale.value) @@ -68,6 +71,9 @@ const formatPlaceholder = computed(() => { }) // Functions +/** + * @description 날짜 Computed값 포맷 셋팅 함수입니다. + */ const setFormatDate = () => { const toDay = startOfDay(new Date()) resultDate.value = @@ -75,112 +81,242 @@ const setFormatDate = () => { formatDate.value = globalDateFormat(resultDate.value, locale.value) as string const dateObj = props.date === null ? toDay : new Date(props.date) - currentYear.value = dateObj.getFullYear() - currentMonth.value = dateObj.getMonth() + currentYear.value = getYear(dateObj) + currentMonth.value = getMonth(dateObj) + updateDecade(currentYear.value) } +/** + * @description 선택한 날짜로 업데이트하는 함수입니다. + * @param _date 선택한 날짜 + */ const updateDate = (_date: Date | null) => { if (_date != null) { formatDate.value = globalDateFormat(_date, locale.value) as string - currentYear.value = _date.getFullYear() - currentMonth.value = _date.getMonth() + currentYear.value = getYear(_date) + currentMonth.value = getMonth(_date) emits('update:date', formatDate.value) } } +/** + * @description 월/년도가 변경되었을 때 업데이트하는 함수입니다. + * @param newDate> 변경된 월/년도 + */ +const changeMonthYear = (newDate: any) => { + currentYear.value = newDate.year + currentMonth.value = newDate.month +} + +/** + * @description 날짜 포맷 함수입니다. + * @param _date 날짜 + */ const format = (_date: Date) => { return globalDateFormat(_date, locale.value) } +/** + * @description 캘린더 메뉴 Open 함수입니다. + */ const handleMenuOpen = () => { isMenuOpened.value = true } +/** + * @description 캘린더 메뉴 Close 함수입니다. + */ const handleMenuClose = () => { isMenuOpened.value = false isShowMonthPicker.value = false isShowYearPicker.value = false + + if (currentMonth.value !== getMonth(resultDate.value)) { + currentMonth.value = getMonth(resultDate.value) + } + + if (currentYear.value !== getYear(resultDate.value)) { + currentYear.value = getYear(resultDate.value) + } } +/** + * @description 월 선택 창으로 이동하는 함수입니다. + */ const handleOpenMonthPicker = () => { isShowMonthPicker.value = true isShowYearPicker.value = false + + updateYearList(currentYear.value) } +/** + * @description 년도 선택 창으로 이동하는 함수입니다. + */ const handleOpenYearPicker = () => { isShowYearPicker.value = true isShowMonthPicker.value = false } +/** + * @description 월 선택 함수입니다. + * @param month 선택 월 + */ const handleMonthSelect = (month: number) => { currentMonth.value = month isShowMonthPicker.value = false - // 선택한 년도/월로 날짜 업데이트 - const newDate = new Date(currentYear.value, currentMonth.value, 1) - if (props.minDate && newDate < props.minDate) { - newDate.setDate(props.minDate.getDate()) - } - if (props.maxDate && newDate > props.maxDate) { - newDate.setDate(props.maxDate.getDate()) - } - // DatePicker의 내부 상태 업데이트 if (datePickerRef.value) { - datePickerRef.value.selectDate(newDate) + datePickerRef.value.setMonthYear({ month: month, year: currentYear.value }) } - - updateDate(newDate) } +/** + * @description 년도 선택 함수입니다. + * @param year 선택 년도 + */ const handleYearSelect = (year: number) => { currentYear.value = year isShowYearPicker.value = false + isShowMonthPicker.value = true - // 선택한 년도/월로 날짜 업데이트 - const newDate = new Date(currentYear.value, currentMonth.value, 1) - if (props.minDate && newDate < props.minDate) { - newDate.setDate(props.minDate.getDate()) - } - if (props.maxDate && newDate > props.maxDate) { - newDate.setDate(props.maxDate.getDate()) - } - - // DatePicker의 내부 상태 업데이트 - if (datePickerRef.value) { - datePickerRef.value.selectDate(newDate) - } - - updateDate(newDate) -} - -const generateYearList = () => { - const years = [] - const minYear = props.minDate ? props.minDate.getFullYear() : 1900 - const maxYear = props.maxDate ? props.maxDate.getFullYear() : 2100 - - for (let year = minYear; year <= maxYear; year++) { - years.push(year) - } - return years + updateYearList(currentYear.value) } +/** + * @description 월 리스트 생성 함수입니다. + */ const generateMonthList = () => { - const months = [] const minMonth = - props.minDate && props.minDate.getFullYear() === currentYear.value - ? props.minDate.getMonth() + props.minDate && getYear(props.minDate) === currentYear.value + ? getMonth(props.minDate) : 0 const maxMonth = - props.maxDate && props.maxDate.getFullYear() === currentYear.value - ? props.maxDate.getMonth() + props.maxDate && getYear(props.maxDate) === currentYear.value + ? getMonth(props.maxDate) : 11 + const length = maxMonth - minMonth + 1 - for (let month = minMonth; month <= maxMonth; month++) { - months.push(month) + monthList.value = Array.from({ length }, (_, i) => minMonth + i) +} + +/** + * @description 날짜 범위 차이 계산 함수입니다. + */ +const getDateRangeDifference = () => { + if (!props.minDate || !props.maxDate) { + return null } - return months + + const minYear = getYear(props.minDate) + const maxYear = getYear(props.maxDate) + const yearDiff = maxYear - minYear + + return { + yearDiff, + minYear, + maxYear, + minMonth: getMonth(props.minDate), + maxMonth: getMonth(props.maxDate), + isShortRange: yearDiff < 10, // 10년 미만인 경우 true, 10년 이상인 경우 false + } +} + +/** + * @description 년도 리스트 생성 및 년도 네비게이션 버튼 비활성화 여부 체크 함수입니다. + * @param year 선택 년도 + */ +const updateYearList = (year: number) => { + const rangeInfo = getDateRangeDifference() + + isDisabledYearNav.value.prev = false + isDisabledYearNav.value.next = false + + if (year === (rangeInfo?.minYear ?? 1970)) { + isDisabledYearNav.value.prev = true + currentYear.value = rangeInfo?.minYear ?? 1970 + + generateMonthList() + return + } else if (year === (rangeInfo?.maxYear ?? getYear(new Date()))) { + isDisabledYearNav.value.next = true + currentYear.value = rangeInfo?.maxYear ?? getYear(new Date()) + + generateMonthList() + return + } else { + currentYear.value = year + + generateMonthList() + } +} + +/** + * @description 선택 년도가 속한 00 ~ 09년의 범위로 10년 단위 년도 리스트 업데이트 함수입니다. + * @param year 선택 년도 + */ +const updateDecade = (year: number) => { + const rangeInfo = getDateRangeDifference() + + if (rangeInfo?.isShortRange) { + const minYear = rangeInfo.minYear + const maxYear = rangeInfo.maxYear + + // 현재 년도가 범위를 벗어나면 조정 + if (year < minYear) { + year = minYear + } else if (year > maxYear) { + year = maxYear + } + + // decade가 아니라 실제 년도 범위만 사용 + const start = minYear + const end = maxYear + + isDisabledDecadeNav.value.prev = true + isDisabledDecadeNav.value.next = true + + currentDecade.value = { start, end, year } + + // 실제 년도 리스트 생성 (minYear부터 maxYear까지) + const length = end - start + 1 + currentDecadeList.value = Array.from({ length }, (_, i) => start + i) + + return + } + + const firstYear = props.minDate + ? Math.floor(getYear(props.minDate) / 10) * 10 + : 1970 + const lastYear = props.maxDate + ? Math.floor(getYear(props.maxDate) / 10) * 10 + 9 + : Math.floor(getYear(new Date()) / 10) * 10 + 9 + const start = Math.floor(year / 10) * 10 + const end = start + 9 + + if (start < firstYear) { + return + } + if (end > lastYear) { + return + } + + isDisabledDecadeNav.value.prev = false + isDisabledDecadeNav.value.next = false + + if (start <= firstYear) { + isDisabledDecadeNav.value.prev = true + } + if (end >= lastYear) { + isDisabledDecadeNav.value.next = true + } + + const length = end - start + 1 + + currentDecade.value = { start: start, end: end, year } + currentDecadeList.value = Array.from({ length }, (_, i) => start + i) } watch( @@ -199,25 +335,29 @@ onMounted(() => { - diff --git a/layers/templates/FxCoupon01/index.vue b/layers/templates/FxCoupon01/index.vue index 94ad505..ed172d8 100644 --- a/layers/templates/FxCoupon01/index.vue +++ b/layers/templates/FxCoupon01/index.vue @@ -1,5 +1,6 @@