При построении пользовательского интерфейса часто приходится использовать компоненты которые позволяют пользователям вводить дату и время. В Quasar таких инструментов несколько Первое — это компонент q-date второй — это компонент q-time. Однако удобнtt водить дату и время в одном поле, к сожалению такого компонента в Quasar нету.
Для этих целей я долгое время использовал прекрасную библотеку использовал прекрасную библиотеку VueDatePicker но решил что практичнее и удобнее будет использовать встроенные компоненты из Quasar. Первое преимушество меньше сторонних библиотек в проекте, ну и единое стилевое оформление в придачу.
Для этого Пришлось написать собственную компоненту которые позволяет вводить дату и время в одном поле. Код следующий
<template> <div> <q-input filled v-model="date" @update:model-value="handleChange" :label="$props.label" > <template v-slot:prepend> <q-icon name="event" class="cursor-pointer"> <q-popup-proxy cover transition-show="scale" transition-hide="scale"> <q-date v-model="date" @update:model-value="handleChange" mask="YYYY-MM-DD HH:mm" > <div class="row items-center justify-end"> <q-btn v-close-popup label="Закрыть" color="primary" flat /> </div> </q-date> </q-popup-proxy> </q-icon> </template> <template v-slot:append> <q-icon name="access_time" class="cursor-pointer"> <q-popup-proxy cover transition-show="scale" transition-hide="scale"> <q-time v-model="date" @update:model-value="handleChange" mask="YYYY-MM-DD HH:mm" format24h > <div class="row items-center justify-end"> <q-btn v-close-popup label="Закрыть" color="primary" flat /> </div> </q-time> </q-popup-proxy> </q-icon> </template> </q-input> </div> </template> <script lang="ts"> import { defineComponent, ref, watch } from 'vue'; export default defineComponent({ // type inference enabled props: { value: { type: String, required: true, }, label: { type: String, required: false, }, }, emits: ['update:value'], setup(props, { emit }) { const date = ref(props.value); function handleChange(newValue: string | number | null) { if (typeof newValue == 'string') { emit('update:value', newValue); } } watch(props, (newValue) => { date.value = newValue.value; }); return { date, handleChange, }; }, }); </script>
Как видим компонента простая, на вход мы подаем дату в виде строки формата «YYYY-MM-DD HH:mm«.
На выходе соответственно также получаем дату в такой же строке. Благо в Quasar есть прекрасная утилита для работы с датами, где мы можем с этой строкой сделать что хотим. Как извлечь дату так и привести к формату ISO.
Ну и собственно теперь нам остается только импртировать ее и вставить в шаблон.
<template> <div class="q-pa-md row items-start q-gutter-md"> <q-dialog ref="dialogRef" @hide="onDialogHide"> <q-card style="min-width: 400px; min-height: fit-content"> <q-form @submit.prevent="AddAppointment" class="q-gutter-md"> <q-card-section class="bg-primary text-white"> <div class="text-h6 text-center">Запись на прием</div> </q-card-section> <q-card-section> <div class="q-mb-md"> <DateTimePicker v-model:value="start_date" @update:value="handleStartDate" label="Время начала приема" ></DateTimePicker> </div> <div class="q-mb-md"> <DateTimePicker v-model:value="end_date" @update:value="handleEndDate" label="Время окончания приема" ></DateTimePicker> </div> <div class="q-mb-md" style="max-width: 380px"> <PatientDropdown @set-patient="setPatient" :clinic_id="props.clinicId" /> </div> <div class="q-mb-md" style="max-width: 380px"> <DoctorDropdown @set-doctor="setDoctor" :clinic_id="props.clinicId" :doctor_id="props.doctorId" /> </div> </q-card-section> <q-card-actions align="center" class="q-mb-lg"> <q-btn color="negative" label="Отмена" @click="onDialogCancel" /> <q-btn type="submit" color="primary" label="Добавить" /> </q-card-actions> </q-form> </q-card> </q-dialog> </div> </template> <script setup lang="ts"> import { ref, onMounted, watch } from 'vue'; import { useDialogPluginComponent } from 'quasar'; import { defineProps, defineEmits } from 'vue'; import { useQuasar } from 'quasar'; import fetchApi from 'src/api/appointment'; import { AppointmentCreateModal } from 'src/api/appointment/model'; import DateTimePicker from 'src/components/DateTimePicker.vue'; import PatientDropdown from 'src/components/dropdowns/PatientDropdown.vue'; import DoctorDropdown from 'src/components/dropdowns/DoctorDropdown.vue'; import dateUtils from 'src/utils/dateUtils'; import { date } from 'quasar'; const $q = useQuasar(); const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent(); defineEmits([...useDialogPluginComponent.emits]); const props = defineProps({ start: { type: Date, required: false }, end: { type: Date, required: false }, clinicId: { type: String, required: true }, doctorId: { type: String, required: false }, }); const setDoctor = (doctor_id: string) => { if (doctor_id) { appointment.value.doctor_id = doctor_id; } }; const setPatient = (patient_id: string) => { appointment.value.patient_id = patient_id; }; const appointment = ref<AppointmentCreateModal>({} as AppointmentCreateModal); const start_date = ref(''); const end_date = ref(''); onMounted(() => { if (props.clinicId) { appointment.value.clinic_id = props.clinicId; } if (props.doctorId) { appointment.value.doctor_id = props?.doctorId; } if (props.start) { appointment.value.start = props.start; start_date.value = date.formatDate( appointment.value.start, 'YYYY-MM-DD HH:mm' ); } else { const date = new Date(); // получаем текущую дату и время date.setMinutes(0); // устанавливаем минуты равными 0 date.setSeconds(0); // устанавливаем секунды равными 0 date.setMilliseconds(0); // устанавливаем миллисекунды равными 0 date.setHours(date.getHours() + 1); // увеличиваем часы на 1 appointment.value.start = date; } if (props.end) { appointment.value.end = props.end; end_date.value = date.formatDate(appointment.value.end, 'YYYY-MM-DD HH:mm'); } else { const date = new Date(); // получаем текущую дату и время date.setMinutes(0); // устанавливаем минуты равными 0 date.setSeconds(0); // устанавливаем секунды равными 0 date.setMilliseconds(0); // устанавливаем миллисекунды равными 0 date.setHours(date.getHours() + 2); // увеличиваем часы на 1 appointment.value.end = date; } }); async function AddAppointment() { await fetchApi .createAppointment(appointment.value) .then((res) => { if (res) { $q.notify({ message: 'Пациент записан на прием', type: 'positive', timeout: 1000, }); onDialogOK(); onDialogHide(); } }) .catch(() => { $q.notify({ message: 'Не удалось добавить пациента', type: 'warning' }); }); } const handleStartDate = (modelData: string) => { appointment.value.start = dateUtils.parseDateString(modelData); appointment.value.end = date.addToDate(appointment.value.start, { hours: 1 }); }; const handleEndDate = (modelData: string) => { appointment.value.end = dateUtils.parseDateString(modelData); }; watch( appointment, (newValue) => { start_date.value = date.formatDate(newValue.start, 'YYYY-MM-DD HH:mm'); end_date.value = date.formatDate(newValue.end, 'YYYY-MM-DD HH:mm'); }, { deep: true } ); </script>