DateTimePicker.vue 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <template>
  2. <v-row>
  3. <v-col cols="12" md="6">
  4. <v-text-field
  5. :model-value="
  6. dateModel
  7. ? _useDate.format(dateModel, 'fullDateWithWeekday')
  8. : undefined
  9. "
  10. :label="$t('choose_day')"
  11. prepend-icon="fas fa-calendar"
  12. :rules="rules"
  13. :variant="variant"
  14. :error="error || !!fieldViolations"
  15. :error-messages="
  16. errorMessage || (fieldViolations ? $t(fieldViolations) : '')
  17. "
  18. readonly
  19. >
  20. <v-menu
  21. v-model="showMenuDate"
  22. :close-on-content-click="false"
  23. activator="parent"
  24. min-width="0"
  25. >
  26. <v-date-picker
  27. :model-value="dateModel"
  28. @update:model-value="onUpdateDate($event)"
  29. />
  30. </v-menu>
  31. </v-text-field>
  32. </v-col>
  33. <v-col cols="12" md="6">
  34. <v-text-field
  35. :model-value="time"
  36. :variant="variant"
  37. :label="$t('choose_hour')"
  38. prepend-icon="fas fa-clock"
  39. :rules="rules"
  40. :error="error || !!fieldViolations"
  41. :error-messages="
  42. errorMessage || (fieldViolations ? $t(fieldViolations) : '')
  43. "
  44. readonly
  45. >
  46. <v-menu
  47. v-model="showMenuTime"
  48. :close-on-content-click="false"
  49. activator="parent"
  50. min-width="0"
  51. >
  52. <v-time-picker
  53. :model-value="time"
  54. format="24hr"
  55. scrollable
  56. @update:model-value="onUpdateTime($event)"
  57. />
  58. </v-menu>
  59. </v-text-field>
  60. </v-col>
  61. </v-row>
  62. </template>
  63. <script setup lang="ts">
  64. import { ref, computed, onBeforeUnmount, type PropType } from 'vue'
  65. import { useFieldViolation } from '~/composables/form/useFieldViolation'
  66. import { useDate } from 'vuetify'
  67. import DateUtils from '~/services/utils/dateUtils'
  68. const props = defineProps({
  69. /**
  70. * v-model
  71. */
  72. modelValue: {
  73. type: String as PropType<Date | string | null>,
  74. required: false,
  75. default: null,
  76. },
  77. /**
  78. * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
  79. * - Utilisé par la validation
  80. * - Laisser null si le champ ne s'applique pas à une entité
  81. */
  82. field: {
  83. type: String,
  84. required: false,
  85. default: null,
  86. },
  87. /**
  88. * Label du champ
  89. * Si non défini, c'est le nom de propriété qui est utilisé
  90. */
  91. label: {
  92. type: String,
  93. required: false,
  94. default: null,
  95. },
  96. /**
  97. * Définit si le champ est en lecture seule
  98. */
  99. readonly: {
  100. type: Boolean,
  101. required: false,
  102. },
  103. /**
  104. * Règles de validation
  105. * @see https://vuetify.cn/en/components/forms/#validation-with-submit-clear
  106. */
  107. rules: {
  108. type: Array as PropType<Array<(value: unknown) => boolean | string>>,
  109. required: false,
  110. default: () => [],
  111. },
  112. /**
  113. * Le champ est-il actuellement en état d'erreur
  114. */
  115. error: {
  116. type: Boolean,
  117. required: false,
  118. },
  119. /**
  120. * Si le champ est en état d'erreur, quel est le message d'erreur?
  121. */
  122. errorMessage: {
  123. type: String,
  124. required: false,
  125. default: null,
  126. },
  127. /**
  128. * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
  129. */
  130. variant: {
  131. type: String as PropType<
  132. | 'filled'
  133. | 'outlined'
  134. | 'plain'
  135. | 'underlined'
  136. | 'solo'
  137. | 'solo-inverted'
  138. | 'solo-filled'
  139. | undefined
  140. >,
  141. required: false,
  142. default: 'outlined',
  143. },
  144. })
  145. const showMenuTime = ref(false)
  146. const showMenuDate = ref(false)
  147. const _useDate = useDate()
  148. const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
  149. const dateModel = computed(() =>
  150. props.modelValue ? new Date(props.modelValue) : undefined,
  151. )
  152. const time = computed(() =>
  153. props.modelValue
  154. ? _useDate.format(new Date(props.modelValue), 'fullTime24h')
  155. : undefined,
  156. )
  157. const emit = defineEmits(['update:model-value'])
  158. const onUpdateDate = (event: Date) => {
  159. updateViolationState()
  160. const date = DateUtils.combineDateAndTime(event, time.value)
  161. emit('update:model-value', date.toISOString().replace('.000Z', '+00:00'))
  162. }
  163. const onUpdateTime = (event: string) => {
  164. updateViolationState()
  165. const date = DateUtils.combineDateAndTime(dateModel.value, event)
  166. emit('update:model-value', date.toISOString().replace('.000Z', '+00:00'))
  167. }
  168. onBeforeUnmount(() => {
  169. updateViolationState()
  170. })
  171. </script>
  172. <style scoped lang="scss"></style>