Frond-end/Vue

[트러블슈팅/ Vue3] 컴포넌트에 데이터 바인딩이 되지 않는 문제(feat. ref, watch)

Nellie Kim 2024. 3. 25. 12:24
728x90

문제 상황

달력 컴포넌트(ExampleFull)에 날짜와 타입을 전달하여, 달력에 보여주고 싶었다. 

26일자에 '입원'이라는 문구가 떠야하는데 아래와 같이 보였다. 

원했던 화면 / 문제 화면

 

기존의 일정 list 뷰 코드

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { RETURN_CODE } from '@/apis/common'
import { ReserveModel, ReserveScheduleModel, getReserveScheduleList } from '@/apis/reserve'
import { useRouter } from 'vue-router'
import { Dialog } from '@varlet/ui'

/**
|--------------------------------------------------
| 😃  1. 변수
|--------------------------------------------------
*/
const isRefresh = ref(false)
const data = ref<ReserveModel>({} as ReserveModel)
const scheList = ref<ReserveScheduleModel[] | undefined>() // 예약 데이터 안의 reserveScheduleList
const router = useRouter()
const bottom = ref(false)

const isPopup = ref(false)

const reserveType = ref<any>('')
const reserveDate = ref<any>('')
const patientData = ref<any>('')

/**
|--------------------------------------------------
| 🔑 2. 일반 함수
|--------------------------------------------------
*/

/** 1. 상세페이지로 이동하는 함수 */
// function goToDetail(idx: any) {
//   if (idx !== undefined) {
//     // 상세페이지 이동
//     router.push(`/hospital/checkinout/${idx}`)
//   } else {
//     // ID가 없는 경우 처리할 로직 추가
//     console.error('Item ID is undefined!')
//   }
// }

function goToDetail() {
  isPopup.value = true
  console.log('ddddddd')
}

function goToDetail2() {
  isPopup.value = true
  openPopup()
  console.log('ddddddd')
}

/** .
 *  토큰 확인 함수 */
function tokenError(code: string) {
  if (/^30002\d{3}$/.test(code)) {
    Snackbar.error('로그인 후 이용 부탁드립니다')
    router.replace('/login')
  }
}

/** 3. 새로고침 함수  */
function handleRefresh() {
  isRefresh.value = false
}

/** 8. 팝업을 오픈하는 함수 */
function openPopup() {
  Dialog({
    title: '2024.10.10',
    message: '건강검진'
  })
}

/**
|--------------------------------------------------
| 🎁 3. fetch 함수
|--------------------------------------------------
*/
/**------------------------
  | 🎈 fetch 함수 1
  |------------------------*/
/** 1. 예약 스케줄 리스트 조회 호출 */
async function fetchData() {
  const params = {
    reserveType: 'INPATIENT' // 입원 예약 환자만 조회
  } as ReserveModel

  if (history.state.type) params.reserveType = history.state.type
  if (history.state.type) params.reserveDt = history.state.date

  const response = await getReserveScheduleList(params)
  if (response.code !== RETURN_CODE.SUCCESS) {
    console.log('예약 정보를 가져오는 중 오류가 발생했습니다.')
    return
  }

  tokenError(response.code)

  data.value = response.data

  patientData.value = data.value

  scheList.value = data.value.reserveScheduleList

  console.log('data,,,,,', data.value)

  console.log('data,,,,,예약일 :: ', data.value.reserveDt)

  let dateString = data.value.reserveDt?.toString()

  reserveDate.value = dateString?.slice(8, 10)
  console.log('data,,,,,프롭스용 예약일 :: ', reserveDate.value) // 출력: 29

  console.log('data,,,,,프롭스용 예약타입 :: ', data.value.reserveType)

  reserveType.value = data.value.reserveType

  console.log('reserveScheduleList,,,,,,', scheList.value)
}

/**
|--------------------------------------------------
| 🎁 4. LifeCycle 함수
|--------------------------------------------------
*/

onMounted(() => {
  fetchData()
})
</script>

<!-- /**
|--------------------------------------------------
| 🎨 화면
|--------------------------------------------------
*/ -->

<template>
  <div class="message">
    <var-pull-refresh v-model="isRefresh" @refresh="handleRefresh">
      <app-header title="일정">
        <template #left>
          <app-back />
        </template>
        <template #right>
          <app-side-menu />
        </template>
      </app-header>
      <div class="message-list">
        <div class="message-item">
          <div class="message-item-detail">
            <div class="message-item-detail-header-centered-box">
              <var-paper>{{ data?.patientName }}님</var-paper>
              <var-paper>환자 등록번호 : {{ data?.patientId }}</var-paper>
            </div>
            <div class="message-item-detail-header-centered-box">
              <ExampleFull
                :reserveDate="reserveDate"
                :reserveType="reserveType"
                :data="data"
                @toggle-drawer="goToDetail2"
              />
            </div>
            <div class="message-item-detail-description"></div>
          </div>
        </div>
      </div>
    </var-pull-refresh>
  </div>
  <!-- <TestComponent :reserveDate="reserveDate" :reserveType="reserveType" /> -->
  <var-button type="primary" block @click="goToDetail"></var-button>

  <router-stack-view />
</template>

<style lang="less" scoped>
.message {
  padding: calc(var(--app-bar-height)) 0 0;

  &-list {
    padding: 3px 0;
  }

  &-item {
    position: relative;
    display: flex;
    padding-top: 3px;

    &-avatar {
      flex-shrink: 0;
      margin: 0 18px;
    }

    &-detail {
      width: 100%;
      border-bottom: thin solid var(--divider-color);
      padding-bottom: 5px;

      &-header {
        display: flex;
        justify-content: space-between;
        align-items: center;

        &-name {
          color: var(--app-title-color);
          font-size: 16px;
          width: 190px;
        }

        &-date {
          color: var(--app-subtitle-color);
          font-size: 14px;
          margin-bottom: 2px;
        }

        &-centered-box {
          /* 위아래 여백 조절 */
          margin: 10px;
          padding: 17px;
          border: 1.5px solid rgb(222, 218, 218);
          border-radius: 10px;
          color: rgb(49, 49, 165);
        }
      }

      &-description {
        color: var(--app-subtitle-color);
        font-size: 15px;
        margin-top: 6px;
      }
    }
  }

  .var-steps {
    margin: 10px;
  }

  .var-step {
    margin: 15px;
  }
}
.var-step__vertical-content {
  margin: 15px !important;
  border: 1.5px solid rgb(222, 218, 218) !important;
  border-radius: 10px !important;
}

.var-step__vertical-tag {
  margin: 100px !important; /* 원하는 간격을 지정합니다. */
}
</style>

 

 

기존의 달력 컴포넌트 뷰

<script setup lang="ts">
// Using the publish version, you would do this instead:
// import { CalendarView, CalendarViewHeader, CalendarMath } from "vue-simple-calendar"
import CalendarView from '../vue-simple-calendar/CalendarView.vue'
import CalendarViewHeader from '../vue-simple-calendar/CalendarViewHeader.vue'
import CalendarMath from '../vue-simple-calendar/CalendarMath'
import { ICalendarItem, INormalizedCalendarItem } from './ICalendarItem'
import { onMounted, reactive, defineEmits } from 'vue'

/**
|--------------------------------------------------
| 😃  1. 변수
|--------------------------------------------------
*/

const date = ref('')
const type = ref('')

// 현재 달을 가져오는 함수 (선택적으로 시간 및 분도 포함)
const thisMonth = (d: number, h?: number, m?: number): Date => {
  const t = new Date()
  return new Date(t.getFullYear(), t.getMonth(), d, h || 0, m || 0)
}

// 부모 컴포넌트로부터 받은 props 정의
const props = defineProps<{ reserveDate: string; reserveType: string; data: any }>() // 부모Component에서 받은값
console.log('props!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  ', props)
console.log('props.reserveDate!!!!!!!!!!!!!!!', date.value)
console.log('props.reserveType!!!!!!!!!!!!!!!      ', props.reserveType)
console.log('props.data~~~~~~~~~~~~~~ ~~~~~~ ~~~~ ~~~~~~~~~ ~~~~~ ', props.data)

// 상태 객체를 반응적으로 정의
interface IExampleState {
  showDate: Date // 캘린더에 표시되는 날짜
  message: string // 상태 메시지
  startingDayOfWeek: number // 주의 시작 요일 (0: Sunday, 1: Monday, ..., 6: Saturday)
  disablePast: boolean // 과거 날짜 사용 금지 여부
  disableFuture: boolean // 미래 날짜 사용 금지 여부
  displayPeriodUom: string // 표시 기간 단위 (예: 'month', 'year')
  displayPeriodCount: number // 표시 기간 개수
  displayWeekNumbers: boolean // 주 번호 표시 여부
  showTimes: boolean // 시간 표시 여부
  selectionStart?: Date // 선택 시작 날짜
  selectionEnd?: Date // 선택 종료 날짜
  newItemTitle: string // 새로운 아이템 제목
  newItemStartDate: string // 새로운 아이템 시작 날짜
  newItemEndDate: string // 새로운 아이템 종료 날짜
  useDefaultTheme: boolean // 기본 테마 사용 여부
  useHolidayTheme: boolean // 휴일 테마 사용 여부
  useTodayIcons: boolean // 오늘 아이콘 사용 여부
  items: ICalendarItem[] // 캘린더에 표시되는 아이템 배열
}

const state = reactive({
  showDate: thisMonth(1), // 캘린더에 표시할 현재 달의 첫 날짜
  message: '', // 상태 메시지
  startingDayOfWeek: 0, // 주의 시작 요일 (0: Sunday, 1: Monday, ..., 6: Saturday)
  disablePast: false, // 과거 날짜 사용 금지 여부
  disableFuture: false, // 미래 날짜 사용 금지 여부
  displayPeriodUom: 'month', // 표시 기간의 단위 (예: 'month', 'year')
  displayPeriodCount: 1, // 표시할 기간의 개수
  displayWeekNumbers: false, // 주 번호 표시 여부
  // showTimes: true, // 시간 표시 여부
  selectionStart: undefined, // 선택 시작 날짜
  selectionEnd: undefined, // 선택 종료 날짜
  newItemTitle: '', // 새로운 아이템 제목
  newItemStartDate: '', // 새로운 아이템 시작 날짜
  newItemEndDate: '', // 새로운 아이템 종료 날짜
  useDefaultTheme: true, // 기본 테마 사용 여부
  // useHolidayTheme: true, // 휴일 테마 사용 여부
  useTodayIcons: true, // 오늘 아이콘 사용 여부
  items: [
    {
      startDate: thisMonth(Number(date.value)), // 아이템 시작 날짜 설정
      // title: props.reserveType, // 아이템 제목 설정
      title: date.value,
      url: 'https://en.wikipedia.org/wiki/Birthday' // 아이템 URL 설정
    }
    // {
    // 	id: "e11",
    // 	startDate: thisMonth(29),
    // 	title: "수술",
    // },
  ] as ICalendarItem[] // 캘린더에 표시할 아이템 배열
} as IExampleState)

// const userLocale = computed((): string => CalendarMath.getDefaultBrowserLocale())

// const dayNames = computed((): string[] => CalendarMath.getFormattedWeekdayNames(userLocale.value, "long", 0))

// 계산된 속성 정의
const themeClasses = computed(() => ({
  'theme-default': state.useDefaultTheme
  // "holiday-us-traditional": state.useHolidayTheme,
  // "holiday-us-official": state.useHolidayTheme,
}))

// 날짜 클래스를 계산하는 메서드 정의
const myDateClasses = (): Record<string, string[]> => {
  // This was added to demonstrate the dateClasses prop. Note in particular that the
  // keys of the object are `yyyy-mm-dd` ISO date strings (not dates), and the values
  // for those keys are strings or string arrays. Keep in mind that your CSS to style these
  // may need to be fairly specific to make it override your theme's styles. See the
  // CSS at the bottom of this component to see how these are styled.
  const o = {} as Record<string, string[]>
  const theFirst = thisMonth(1)
  const ides = [2, 4, 6, 9].includes(theFirst.getMonth()) ? 15 : 13
  const idesDate = thisMonth(ides)
  o[CalendarMath.isoYearMonthDay(idesDate)] = ['ides']
  o[CalendarMath.isoYearMonthDay(thisMonth(21))] = ['do-you-remember', 'the-21st']
  return o
}

// 마운트 후 수행할 작업 정의
onMounted((): void => {
  state.newItemStartDate = CalendarMath.isoYearMonthDay(CalendarMath.today())
  state.newItemEndDate = CalendarMath.isoYearMonthDay(CalendarMath.today())
})


... 생략

 

 


해결 방법

 

달력 컴포넌트안의 props 값이 뜨지 않는 이유는,

props 값을 달력 컴포넌트가 전달받기도 전에 데이터바인딩을 시도하기 때문이다. 

props 값을 달력 컴포넌트가 제대로 전달 받아서 바인딩을 시키려면 어떻게 해야할까? 

 

 

1. ref 로 변수 선언

const date = ref('') // 추가 🎯
const type = ref('') // 추가 🎯

 

 

2. 데이터 바인딩 시 필요한 변수 state 값을 원래 부분에서 지우고,

const state = reactive({
  showDate: thisMonth(1), // 캘린더에 표시할 현재 달의 첫 날짜
  message: '', // 상태 메시지
  startingDayOfWeek: 0, // 주의 시작 요일 (0: Sunday, 1: Monday, ..., 6: Saturday)
  disablePast: false, // 과거 날짜 사용 금지 여부
  disableFuture: false, // 미래 날짜 사용 금지 여부
  displayPeriodUom: 'month', // 표시 기간의 단위 (예: 'month', 'year')
  displayPeriodCount: 1, // 표시할 기간의 개수
  displayWeekNumbers: false, // 주 번호 표시 여부
  // showTimes: true, // 시간 표시 여부
  selectionStart: undefined, // 선택 시작 날짜
  selectionEnd: undefined, // 선택 종료 날짜
  newItemTitle: '', // 새로운 아이템 제목
  newItemStartDate: '', // 새로운 아이템 시작 날짜
  newItemEndDate: '', // 새로운 아이템 종료 날짜
  useDefaultTheme: true, // 기본 테마 사용 여부
  // useHolidayTheme: true, // 휴일 테마 사용 여부
  useTodayIcons: true, // 오늘 아이콘 사용 여부
  // items: [ // 여기 지우고 🎯
  //   {
  //     startDate: thisMonth(Number(date.value)), // 아이템 시작 날짜 설정
  //     // title: props.reserveType, // 아이템 제목 설정
  //     title: date.value,
  //     url: 'https://en.wikipedia.org/wiki/Birthday' // 아이템 URL 설정
  //   }
  //   // {
  //   // 	id: "e11",
  //   // 	startDate: thisMonth(29),
  //   // 	title: "수술",
  //   // },
  // ] as ICalendarItem[] // 캘린더에 표시할 아이템 배열
} as IExampleState)

 

 

3. watch 함수 추가해서 props가 변경이 될 때마다 다시 바인딩을 시도했다. 

// 이거 추가 🎯
watch(props, () => {
  date.value = props.reserveDate
  type.value = props.reserveType
  state.items = [
    {
      id: 'e1',
      startDate: thisMonth(Number(date.value)), // 아이템 시작 날짜 설정
      // title: props.reserveType, // 아이템 제목 설정
      title: type.value,
      url: 'https://en.wikipedia.org/wiki/Birthday' // 아이템 URL 설정
    }
  ]
  console.log('state============?????????????', state)
})

 

 

참고로,

state.items를 추가해주면 바인딩이 되는 이유는

아래처럼 달력컴포넌트 안에 또 <CalendarView>라는 컴포넌트가 있고,

거기서 items 를 props로 전달해주기 때문에 가능한거겠지!

<template>
  <div id="example-full">
    <div class="calendar-parent">
      <CalendarView
        :items="state.items"
        :show-date="state.showDate"
        :time-format-options="{ hour: 'numeric', minute: '2-digit' }"
        :enable-drag-drop="true"
        :disable-past="state.disablePast"
        :disable-future="state.disableFuture"
        :show-times="state.showTimes"
        :date-classes="myDateClasses()"
        :display-period-uom="state.displayPeriodUom"
        :display-period-count="state.displayPeriodCount"
        :starting-day-of-week="state.startingDayOfWeek"
        :class="themeClasses"
        :period-changed-callback="periodChanged"
        :current-period-label="state.useTodayIcons ? 'icons' : ''"
        :displayWeekNumbers="state.displayWeekNumbers"
        :enable-date-selection="true"
        :selection-start="state.selectionStart"
        :selection-end="state.selectionEnd"
        @date-selection-start="setSelection"
        @date-selection="setSelection"
        @date-selection-finish="finishSelection"
        @drop-on-date="onDrop"
        @click-date="onClickDay"
        @click-item="onClickItem"
      >
        <!-- 달력 헤더  -->
        <template #header="{ headerProps }">
          <CalendarViewHeader :header-props="headerProps" @input="setShowDate" />
        </template>

        <template> </template>
      </CalendarView>
    </div>
  </div>
</template>

 

이렇게 하면, 

콘솔창에 이렇게 아주 잘 찍히고

 

원했던 달력에 데이터 바인딩도 잘 된다!

 

 

해결된 전체 코드 

list뷰 코드는 수정되지 않았고, 달력 컴포넌트 뷰 코드만 수정되었다. 

<script setup lang="ts">
// Using the publish version, you would do this instead:
// import { CalendarView, CalendarViewHeader, CalendarMath } from "vue-simple-calendar"
import CalendarView from '../vue-simple-calendar/CalendarView.vue'
import CalendarViewHeader from '../vue-simple-calendar/CalendarViewHeader.vue'
import CalendarMath from '../vue-simple-calendar/CalendarMath'
import { ICalendarItem, INormalizedCalendarItem } from './ICalendarItem'
import { onMounted, reactive, defineEmits } from 'vue'

/**
|--------------------------------------------------
| 😃  1. 변수
|--------------------------------------------------
*/

const date = ref('') // 추가 🎯
const type = ref('') // 추가 🎯

// 현재 달을 가져오는 함수 (선택적으로 시간 및 분도 포함)
const thisMonth = (d: number, h?: number, m?: number): Date => {
  const t = new Date()
  return new Date(t.getFullYear(), t.getMonth(), d, h || 0, m || 0)
}

// 부모 컴포넌트로부터 받은 props 정의
const props = defineProps<{ reserveDate: string; reserveType: string; data: any }>() // 부모Component에서 받은값
console.log('props!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  ', props)
console.log('props.reserveDate!!!!!!!!!!!!!!!', date.value)
console.log('props.reserveType!!!!!!!!!!!!!!!      ', props.reserveType)
console.log('props.data~~~~~~~~~~~~~~ ~~~~~~ ~~~~ ~~~~~~~~~ ~~~~~ ', props.data)

// 상태 객체를 반응적으로 정의
interface IExampleState {
  showDate: Date // 캘린더에 표시되는 날짜
  message: string // 상태 메시지
  startingDayOfWeek: number // 주의 시작 요일 (0: Sunday, 1: Monday, ..., 6: Saturday)
  disablePast: boolean // 과거 날짜 사용 금지 여부
  disableFuture: boolean // 미래 날짜 사용 금지 여부
  displayPeriodUom: string // 표시 기간 단위 (예: 'month', 'year')
  displayPeriodCount: number // 표시 기간 개수
  displayWeekNumbers: boolean // 주 번호 표시 여부
  showTimes: boolean // 시간 표시 여부
  selectionStart?: Date // 선택 시작 날짜
  selectionEnd?: Date // 선택 종료 날짜
  newItemTitle: string // 새로운 아이템 제목
  newItemStartDate: string // 새로운 아이템 시작 날짜
  newItemEndDate: string // 새로운 아이템 종료 날짜
  useDefaultTheme: boolean // 기본 테마 사용 여부
  useHolidayTheme: boolean // 휴일 테마 사용 여부
  useTodayIcons: boolean // 오늘 아이콘 사용 여부
  items: ICalendarItem[] // 캘린더에 표시되는 아이템 배열
}

const state = reactive({
  showDate: thisMonth(1), // 캘린더에 표시할 현재 달의 첫 날짜
  message: '', // 상태 메시지
  startingDayOfWeek: 0, // 주의 시작 요일 (0: Sunday, 1: Monday, ..., 6: Saturday)
  disablePast: false, // 과거 날짜 사용 금지 여부
  disableFuture: false, // 미래 날짜 사용 금지 여부
  displayPeriodUom: 'month', // 표시 기간의 단위 (예: 'month', 'year')
  displayPeriodCount: 1, // 표시할 기간의 개수
  displayWeekNumbers: false, // 주 번호 표시 여부
  // showTimes: true, // 시간 표시 여부
  selectionStart: undefined, // 선택 시작 날짜
  selectionEnd: undefined, // 선택 종료 날짜
  newItemTitle: '', // 새로운 아이템 제목
  newItemStartDate: '', // 새로운 아이템 시작 날짜
  newItemEndDate: '', // 새로운 아이템 종료 날짜
  useDefaultTheme: true, // 기본 테마 사용 여부
  // useHolidayTheme: true, // 휴일 테마 사용 여부
  useTodayIcons: true, // 오늘 아이콘 사용 여부
  // items: [ // 여기 지우고 🎯
  //   {
  //     startDate: thisMonth(Number(date.value)), // 아이템 시작 날짜 설정
  //     // title: props.reserveType, // 아이템 제목 설정
  //     title: date.value,
  //     url: 'https://en.wikipedia.org/wiki/Birthday' // 아이템 URL 설정
  //   }
  //   // {
  //   // 	id: "e11",
  //   // 	startDate: thisMonth(29),
  //   // 	title: "수술",
  //   // },
  // ] as ICalendarItem[] // 캘린더에 표시할 아이템 배열
} as IExampleState)

// const userLocale = computed((): string => CalendarMath.getDefaultBrowserLocale())

// const dayNames = computed((): string[] => CalendarMath.getFormattedWeekdayNames(userLocale.value, "long", 0))

// 계산된 속성 정의
const themeClasses = computed(() => ({
  'theme-default': state.useDefaultTheme
  // "holiday-us-traditional": state.useHolidayTheme,
  // "holiday-us-official": state.useHolidayTheme,
}))

// 날짜 클래스를 계산하는 메서드 정의
const myDateClasses = (): Record<string, string[]> => {
  // This was added to demonstrate the dateClasses prop. Note in particular that the
  // keys of the object are `yyyy-mm-dd` ISO date strings (not dates), and the values
  // for those keys are strings or string arrays. Keep in mind that your CSS to style these
  // may need to be fairly specific to make it override your theme's styles. See the
  // CSS at the bottom of this component to see how these are styled.
  const o = {} as Record<string, string[]>
  const theFirst = thisMonth(1)
  const ides = [2, 4, 6, 9].includes(theFirst.getMonth()) ? 15 : 13
  const idesDate = thisMonth(ides)
  o[CalendarMath.isoYearMonthDay(idesDate)] = ['ides']
  o[CalendarMath.isoYearMonthDay(thisMonth(21))] = ['do-you-remember', 'the-21st']
  return o
}

// 마운트 후 수행할 작업 정의
onMounted((): void => {
  state.newItemStartDate = CalendarMath.isoYearMonthDay(CalendarMath.today())
  state.newItemEndDate = CalendarMath.isoYearMonthDay(CalendarMath.today())
})

// 이거 추가 🎯
watch(props, () => {
  date.value = props.reserveDate
  type.value = props.reserveType
  state.items = [
    {
      id: 'e1',
      startDate: thisMonth(Number(date.value)), // 아이템 시작 날짜 설정
      // title: props.reserveType, // 아이템 제목 설정
      title: type.value,
      url: 'https://en.wikipedia.org/wiki/Birthday' // 아이템 URL 설정
    }
  ]
  console.log('state===================///////////////////////////////////??????????????', state)
})

/**
|--------------------------------------------------
| 🔑 2. 일반 함수
|--------------------------------------------------
*/

// 기간 변경을 처리하는 함수
const periodChanged = (): void => {
  // 이 정보를 사용하여 아무 작업도 수행하지 않습니다.
  // 단순히 표시 범위의 변경을 수신하고 이에 반응하는 방법을 보여주기 위해 메서드를 포함합니다.
  //console.log(eventSource)
  //console.log(range)
}

// 부모 컴포넌트에 메시지를 전달하기 위한 이벤트 정의
const emit = defineEmits(['toggle-drawer'])

// 날짜 클릭을 처리하는 함수
const onClickDay = (d: Date): void => {
  state.selectionStart = undefined
  state.selectionEnd = undefined
  state.message = `You clicked: ${d.toLocaleDateString()}`

  let dateString = d.toString().slice(8, 10) // 26
  console.log('dateString~~~~~~ ~~~~ ~~~~~ ~~~ ~ ~~ ~~ ~ ~ ', dateString)

  if (props.reserveDate === dateString) {
    emit('toggle-drawer')
  }
}

// 항목 클릭을 처리하는 함수
const onClickItem = (item: INormalizedCalendarItem): void => {
  state.message = `You clicked: ${item.title}`

  console.log('item~~~~~~~~~~~~~~~~~~~~~~~~~ ', item)
}

// 표시할 날짜 설정을 처리하는 함수
const setShowDate = (d: Date): void => {
  state.message = `Changing calendar view to ${d.toLocaleDateString()}`
  state.showDate = d
}

// 선택 설정을 처리하는 함수
const setSelection = (dateRange: Date[]): void => {
  state.selectionEnd = dateRange[1]
  state.selectionStart = dateRange[0]
}

// 선택 완료를 처리하는 함수
const finishSelection = (dateRange: Date[]): void => {
  setSelection(dateRange)
  state.message = `You selected: ${state.selectionStart?.toLocaleDateString() ?? 'n/a'} - ${
    state.selectionEnd?.toLocaleDateString() ?? 'n/a'
  }`
}

// 드롭 이벤트를 처리하는 함수
const onDrop = (item: INormalizedCalendarItem, date: Date): void => {
  state.message = `You dropped ${item.id} on ${date.toLocaleDateString()}`
  // 이전 시작 날짜와 선택한 날짜 사이의 차이를 결정하고,
  // 해당 차이를 시작 및 종료 날짜에 모두 적용하여 항목을 이동합니다.
  const eLength = CalendarMath.dayDiff(item.startDate, date)
  item.originalItem.startDate = CalendarMath.addDays(item.startDate, eLength)
  item.originalItem.endDate = CalendarMath.addDays(item.endDate, eLength)
}

// 항목 추가를 처리하는 함수
// const clickTestAddItem = (): void => {
// 	state.items.push({
// 		startDate: CalendarMath.fromIsoStringToLocalDate(state.newItemStartDate),
// 		endDate: CalendarMath.fromIsoStringToLocalDate(state.newItemEndDate),
// 		title: state.newItemTitle,
// 		id: "e" + Math.random().toString(36).substring(2, 11),
// 	})
// 	state.message = "You added a calendar item!"
// }
</script>

<!-- /**
|--------------------------------------------------
| 🎨 화면
|--------------------------------------------------
*/ -->
<template>
  <div id="example-full">
    <div class="calendar-parent">
      <CalendarView
        :items="state.items"
        :show-date="state.showDate"
        :time-format-options="{ hour: 'numeric', minute: '2-digit' }"
        :enable-drag-drop="true"
        :disable-past="state.disablePast"
        :disable-future="state.disableFuture"
        :show-times="state.showTimes"
        :date-classes="myDateClasses()"
        :display-period-uom="state.displayPeriodUom"
        :display-period-count="state.displayPeriodCount"
        :starting-day-of-week="state.startingDayOfWeek"
        :class="themeClasses"
        :period-changed-callback="periodChanged"
        :current-period-label="state.useTodayIcons ? 'icons' : ''"
        :displayWeekNumbers="state.displayWeekNumbers"
        :enable-date-selection="true"
        :selection-start="state.selectionStart"
        :selection-end="state.selectionEnd"
        @date-selection-start="setSelection"
        @date-selection="setSelection"
        @date-selection-finish="finishSelection"
        @drop-on-date="onDrop"
        @click-date="onClickDay"
        @click-item="onClickItem"
      >
        <!-- 달력 헤더 잠깐 주석 .. -->
        <template #header="{ headerProps }">
          <CalendarViewHeader :header-props="headerProps" @input="setShowDate" />
        </template>

        <template> </template>
      </CalendarView>
    </div>
  </div>
</template>

<style>
@import 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css';
/* npm 패키지를 사용하는 앱의 경우 아래 URL은 /node_modules/vue-simple-calendar/dist/css/를 참조해야 합니다 */
@import '/css/gcal.css';
@import '/css/holidays-us.css';
@import '/css/holidays-ue.css';

/* 전체 예제 컨테이너 스타일 */
#example-full {
  display: flex;
  flex-direction: row;
  font-family: Calibri, sans-serif;
  width: 86vw; /* 전체 너비는 화면 너비의 86%로 설정 */
  min-width: 30rem; /* 최소 너비는 30rem으로 설정 */
  max-width: 90rem; /* 최대 너비는 90rem으로 설정 */
  min-height: 55rem; /* 최소 높이는 55rem으로 설정 */
  margin-left: auto; /* 왼쪽 여백을 자동으로 설정하여 가운데 정렬 */
  margin-right: auto; /* 오른쪽 여백을 자동으로 설정하여 가운데 정렬 */
}

/* #example-full .calendar-controls {
	margin-right: 1rem;
	min-width: 14rem;
	max-width: 14rem;
} */

/* 달력 부모 컨테이너 스타일 */
#example-full .calendar-parent {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  overflow-x: hidden;
  overflow-y: hidden;
  max-height: 90vh; /* 최대 높이는 화면 높이의 90%로 설정 */
  background-color: white; /* 배경색은 흰색으로 설정 */
}

/* 달력의 각 주의 높이를 조정하는 스타일 */
#example-full .cv-wrapper.period-month.periodCount-2 .cv-week,
#example-full .cv-wrapper.period-month.periodCount-3 .cv-week,
#example-full .cv-wrapper.period-year .cv-week {
  min-height: 7rem; /* 주당 최소 높이는 7rem으로 설정 */
}

/* 특정 날짜에 배경색을 변경하는 예제 - 분홍색 */
/* #example-full .theme-default .cv-day.ides {
  background-color: #ffe0e0;
} */

/* 날짜 요소에 아이콘을 추가하는 예제 */
/* #example-full .ides .cv-day-number::before {
	content: "\271D"; // 특별한 아이콘
}

#example-full .cv-day.do-you-remember.the-21st .cv-day-number::after {
	content: "\1F30D\1F32C\1F525"; // 다른 특별한 아이콘들
} */
</style>