Files
onboard/resources/js/Components/DatePickerQuery.vue
2026-03-25 17:37:32 +09:00

184 lines
6.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import {NDatePicker, NIcon, NFlex, NButton, NSkeleton} from 'naive-ui'
import {computed, onMounted, ref, watch} from "vue";
import {router} from "@inertiajs/vue3";
import {TbCalendar} from 'vue-icons-plus/tb'
import {formatRussianDate, formatRussianDateRange} from "../Utils/dateFormatter.js";
import {useReportStore} from "../Stores/report.js";
import {formatDistanceStrict, getTime} from "date-fns";
const props = defineProps({
// Пользователь с ролью админ или зав
isHeadOrAdmin: {
type: Boolean,
default: false
},
date: {
type: [Number, Array]
},
isOneDay: {
type: Boolean
},
isShowCurrentDateSwitch: {
type: Boolean,
default: false
}
})
const reportStore = useReportStore()
const isLoading = ref(false)
const isUseDateRange = computed(() => props.isHeadOrAdmin)
const datePicker = ref()
const showCalendar = ref(false)
const calendarType = ref(props.isHeadOrAdmin ? 'daterange' : 'date')
const dateType = computed(() => {
if (isUseDateRange.value) return 'daterange'
return 'date'
})
const isDateRange = computed(() => calendarType.value === 'daterange')
const isDateIsOneDay = computed(() => {
return formatDistanceStrict(modelValue.value[0], modelValue.value[1]) === '0 seconds' || formatDistanceStrict(modelValue.value[0], modelValue.value[1]) === '1 day'
})
const queryDate = ref([null, null])
const modelValue = defineModel('date')
const onChangeDate = () => {
if (isDateIsOneDay.value) {
const startDate = Date.parse(new Date(new Date().getFullYear(), 0, 1).toDateString())
const date = Date.parse(new Date().toDateString())
const arrayDate = [startDate, date]
handleDateUpdate(arrayDate)
} else {
const date = Date.parse(new Date().toDateString())
const arrayDate = [date, date]
handleDateUpdate(arrayDate)
}
showCalendar.value = false
}
const setQueryDate = () => {
reportStore.openedCollapsible = []
router.reload({
data: {
startAt: queryDate.value[0],
endAt: queryDate.value[1]
},
onFinish: () => {
isLoading.value = false
}
})
}
// Форматированное значение для отображения
const formattedValue = computed(() => {
const value = modelValue.value
if (props.isHeadOrAdmin) {
if (props.isOneDay) {
const dateToFormat = Array.isArray(value) ? value[1] : value
return formatRussianDate(dateToFormat)
} else if (Array.isArray(value) && value.length >= 2 && value[0] && value[1]) { // Для админа - диапазон дат
return formatRussianDateRange(value)
}
// Если что-то пошло не так, форматируем как одиночную дату
if (value) {
const dateToFormat = Array.isArray(value) ? value[0] : value
return formatRussianDate(dateToFormat)
}
return ''
} else {
// Для врача - одиночная дата
let dateToFormat
if (Array.isArray(value)) {
dateToFormat = value[1] || value[0]
} else {
dateToFormat = value
}
// Если выбрана сегодняшняя дата - показываем текущее время
if (dateToFormat) {
return formatRussianDate(dateToFormat)
}
return ''
}
})
const handleDateUpdate = (value) => {
isLoading.value = true
// Устанавливаем новое значение
modelValue.value = value
// Для диапазона: отправляем только если обе даты заполнены
if (isDateRange.value) {
if (Array.isArray(value) && value[0] && value[1]) {
// Дебаунс для предотвращения двойной отправки
setTimeout(() => {
// Проверяем что значение не изменилось за время таймаута
if (JSON.stringify(modelValue.value) === JSON.stringify(value)) {
queryDate.value = value
setQueryDate()
}
}, 100)
}
} else {
// Для одиночной даты отправляем сразу
if (value) {
queryDate.value = [value, value]
setQueryDate()
}
}
}
</script>
<template>
<div class="relative inline-flex items-center">
<template v-if="isLoading">
<NSkeleton width="240" height="20" round />
</template>
<template v-else>
<div v-if="formattedValue" class="font-medium leading-3 cursor-pointer" @click="showCalendar = true">
{{ formattedValue }}
</div>
<NDatePicker v-model:value="modelValue"
v-model:show="showCalendar"
ref="datePicker"
class="opacity-0 absolute! inset-x-0 bottom-full -translate-y-1/2"
placement="top-start"
input-readonly
bind-calendar-months
@update:value="value => handleDateUpdate(value)"
:type="calendarType">
<template #confirm="{onConfirm, disabled, text}">
<NFlex justify="end" align="center" :size="0">
<NButton v-if="isShowCurrentDateSwitch" secondary type="primary" size="tiny" @click="onChangeDate()">
{{ isDateIsOneDay ? 'Текущий год' : 'Сегодня' }}
</NButton>
<NButton type="success" size="tiny" @click="onConfirm" :disabled="disabled">
{{ text }}
</NButton>
</NFlex>
</template>
</NDatePicker>
<div class="cursor-pointer p-2 flex items-center justify-center" @click="showCalendar = true">
<NIcon size="20">
<TbCalendar />
</NIcon>
</div>
</template>
</div>
</template>
<style scoped>
:deep(.n-input) {
position: absolute;
pointer-events: none;
}
</style>