138 lines
3.6 KiB
Vue
138 lines
3.6 KiB
Vue
<script setup>
|
|
import { computed, ref } from 'vue';
|
|
import { useI18n } from 'vue-i18n';
|
|
import isValidDate from 'filters/isValidDate';
|
|
|
|
const props = defineProps({
|
|
modelValue: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
readonly: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
isOutlined: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
emitDateFormat: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
const hover = ref(false);
|
|
|
|
const emit = defineEmits(['update:modelValue']);
|
|
|
|
const { t } = useI18n();
|
|
const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
|
|
|
|
const joinDateAndTime = (date, time) => {
|
|
if (!date) {
|
|
return null;
|
|
}
|
|
if (!time) {
|
|
return new Date(date).toISOString();
|
|
}
|
|
const [year, month, day] = date.split('/');
|
|
return new Date(`${year}-${month}-${day}T${time}`).toISOString();
|
|
};
|
|
|
|
const time = computed(() => (props.modelValue ? props.modelValue.split('T')?.[1] : null));
|
|
const value = computed({
|
|
get() {
|
|
return props.modelValue;
|
|
},
|
|
set(value) {
|
|
emit(
|
|
'update:modelValue',
|
|
props.emitDateFormat ? new Date(value) : joinDateAndTime(value, time.value)
|
|
);
|
|
},
|
|
});
|
|
|
|
const isPopupOpen = ref(false);
|
|
|
|
const onDateUpdate = (date) => {
|
|
value.value = date;
|
|
isPopupOpen.value = false;
|
|
};
|
|
|
|
const padDate = (value) => value.toString().padStart(2, '0');
|
|
const formatDate = (dateString) => {
|
|
const date = new Date(dateString || '');
|
|
return `${date.getFullYear()}/${padDate(date.getMonth() + 1)}/${padDate(
|
|
date.getDate()
|
|
)}`;
|
|
};
|
|
const displayDate = (dateString) => {
|
|
if (!dateString || !isValidDate(dateString)) {
|
|
return '';
|
|
}
|
|
return new Date(dateString).toLocaleDateString([], {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
});
|
|
};
|
|
|
|
const styleAttrs = computed(() => {
|
|
return props.isOutlined
|
|
? {
|
|
dense: true,
|
|
outlined: true,
|
|
rounded: true,
|
|
}
|
|
: {};
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div @mouseover="hover = true" @mouseleave="hover = false">
|
|
<QInput
|
|
class="vn-input-date"
|
|
readonly
|
|
:model-value="displayDate(value)"
|
|
v-bind="{ ...$attrs, ...styleAttrs }"
|
|
:class="{ required: $attrs.required }"
|
|
:rules="$attrs.required ? [requiredFieldRule] : null"
|
|
@click="isPopupOpen = true"
|
|
>
|
|
<template #append>
|
|
<QIcon
|
|
name="close"
|
|
size="xs"
|
|
v-if="hover && value && !readonly"
|
|
@click="onDateUpdate(null)"
|
|
></QIcon>
|
|
<QIcon name="event" class="cursor-pointer">
|
|
<QPopupProxy
|
|
v-model="isPopupOpen"
|
|
cover
|
|
transition-show="scale"
|
|
transition-hide="scale"
|
|
:no-parent-event="props.readonly"
|
|
>
|
|
<QDate
|
|
:today-btn="true"
|
|
:model-value="formatDate(value)"
|
|
@update:model-value="onDateUpdate"
|
|
/>
|
|
</QPopupProxy>
|
|
</QIcon>
|
|
</template>
|
|
</QInput>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.vn-input-date.q-field--standard.q-field--readonly .q-field__control:before {
|
|
border-bottom-style: solid;
|
|
}
|
|
|
|
.vn-input-date.q-field--outlined.q-field--readonly .q-field__control:before {
|
|
border-style: solid;
|
|
}
|
|
</style>
|