Создание компоненты для выбора времени и даты в Vue3/Quasar

Создание компоненты для выбора времени и даты в Vue3/Quasar

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

Вам также может понравиться

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Яндекс.Метрика