При построении пользовательского интерфейса часто приходится использовать компоненты которые позволяют пользователям вводить дату и время. В 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>