DatePicker.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <div class="admin--datepicker">
  3. <input
  4. ref="dateInput"
  5. type="text"
  6. :value="modelValue"
  7. :placeholder="placeholder"
  8. :class="['admin--form-input', { 'is-required': required }]"
  9. :required="required"
  10. readonly
  11. >
  12. </div>
  13. </template>
  14. <script setup>
  15. import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
  16. import flatpickr from 'flatpickr'
  17. import { Korean } from 'flatpickr/dist/l10n/ko'
  18. import 'flatpickr/dist/flatpickr.min.css'
  19. const props = defineProps({
  20. modelValue: {
  21. type: String,
  22. default: ''
  23. },
  24. placeholder: {
  25. type: String,
  26. default: '날짜 선택'
  27. },
  28. required: {
  29. type: Boolean,
  30. default: false
  31. },
  32. mode: {
  33. type: String,
  34. default: 'single', // single, range
  35. validator: (value) => ['single', 'range'].includes(value)
  36. },
  37. minDate: {
  38. type: String,
  39. default: null
  40. },
  41. maxDate: {
  42. type: String,
  43. default: null
  44. }
  45. })
  46. const emit = defineEmits(['update:modelValue'])
  47. const dateInput = ref(null)
  48. let flatpickrInstance = null
  49. onMounted(() => {
  50. flatpickrInstance = flatpickr(dateInput.value, {
  51. locale: Korean,
  52. dateFormat: 'Y-m-d',
  53. mode: props.mode,
  54. minDate: props.minDate,
  55. maxDate: props.maxDate,
  56. defaultDate: props.modelValue || null,
  57. onChange: (_selectedDates, dateStr) => {
  58. emit('update:modelValue', dateStr)
  59. },
  60. // 월/년 선택 드롭다운 활성화
  61. monthSelectorType: 'dropdown',
  62. // 시간 선택 비활성화
  63. enableTime: false,
  64. // 주말 강조
  65. onDayCreate: (_dObj, _dStr, _fp, dayElem) => {
  66. const day = dayElem.dateObj.getDay()
  67. if (day === 0) {
  68. dayElem.classList.add('weekend-sunday')
  69. } else if (day === 6) {
  70. dayElem.classList.add('weekend-saturday')
  71. }
  72. }
  73. })
  74. })
  75. // modelValue 변경 감지
  76. watch(() => props.modelValue, (newValue) => {
  77. if (flatpickrInstance && newValue !== flatpickrInstance.input.value) {
  78. flatpickrInstance.setDate(newValue || null, false)
  79. }
  80. })
  81. // minDate, maxDate 변경 감지
  82. watch([() => props.minDate, () => props.maxDate], ([newMinDate, newMaxDate]) => {
  83. if (flatpickrInstance) {
  84. flatpickrInstance.set('minDate', newMinDate)
  85. flatpickrInstance.set('maxDate', newMaxDate)
  86. }
  87. })
  88. onBeforeUnmount(() => {
  89. if (flatpickrInstance) {
  90. flatpickrInstance.destroy()
  91. }
  92. })
  93. </script>
  94. <style scoped>
  95. .admin--datepicker {
  96. position: relative;
  97. }
  98. .admin--datepicker .admin--form-input {
  99. cursor: pointer;
  100. background: var(--admin-bg-tertiary, #252525);
  101. color: var(--admin-text-primary, #ffffff) !important;
  102. border: 1px solid var(--admin-border-color, #333333);
  103. }
  104. .admin--datepicker .admin--form-input:read-only {
  105. background-color: #fff;
  106. color: var(--admin-text-primary, #ffffff) !important;
  107. }
  108. .admin--datepicker .admin--form-input::placeholder {
  109. color: var(--admin-text-muted, #666666);
  110. }
  111. .admin--datepicker .admin--form-input:focus {
  112. outline: none;
  113. /* box-shadow: 0 0 0 3px rgba(187, 10, 48, 0.1); */
  114. }
  115. /* 다크 테마 커스터마이징 */
  116. :deep(.flatpickr-calendar.dark) {
  117. background: #2c3e50;
  118. border-color: #34495e;
  119. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  120. }
  121. :deep(.flatpickr-calendar.dark .flatpickr-months) {
  122. background: #34495e;
  123. }
  124. :deep(.flatpickr-calendar.dark .flatpickr-current-month .flatpickr-monthDropdown-months) {
  125. background: #2c3e50;
  126. color: #ecf0f1;
  127. }
  128. :deep(.flatpickr-calendar.dark .flatpickr-day.selected) {
  129. background: #3498db;
  130. border-color: #2980b9;
  131. }
  132. :deep(.flatpickr-calendar.dark .flatpickr-day.selected:hover) {
  133. background: #2980b9;
  134. }
  135. :deep(.flatpickr-calendar.dark .flatpickr-day:hover) {
  136. background: #34495e;
  137. }
  138. :deep(.flatpickr-calendar.dark .flatpickr-day.today) {
  139. border-color: #3498db;
  140. }
  141. :deep(.flatpickr-calendar.dark .flatpickr-day.today:hover) {
  142. background: #34495e;
  143. border-color: #3498db;
  144. }
  145. /* 주말 색상 */
  146. :deep(.flatpickr-day.weekend-sunday) {
  147. color: #e74c3c !important;
  148. }
  149. :deep(.flatpickr-day.weekend-saturday) {
  150. color: #3498db !important;
  151. }
  152. :deep(.flatpickr-day.weekend-sunday.selected),
  153. :deep(.flatpickr-day.weekend-saturday.selected) {
  154. color: #fff !important;
  155. }
  156. /* Range 모드 스타일 */
  157. :deep(.flatpickr-day.inRange) {
  158. background: rgba(52, 152, 219, 0.2);
  159. border-color: transparent;
  160. box-shadow: -5px 0 0 rgba(52, 152, 219, 0.2), 5px 0 0 rgba(52, 152, 219, 0.2);
  161. }
  162. :deep(.flatpickr-day.startRange),
  163. :deep(.flatpickr-day.endRange) {
  164. background: #3498db;
  165. color: #fff;
  166. }
  167. </style>